Javascript的原型和原型链你了解吗

目录
  • 一、为什么要使用原型?怎样去理解原型的出现
    • 1、对象字面量创建对象的缺点
    • 2、工厂函数
    • 3、构造函数
  • 二、使用原型
  • 三、原型概念辨析
  • 四、原型链练习
  • 总结

一、为什么要使用原型?怎样去理解原型的出现

1、对象字面量创建对象的缺点

想要介绍原型,就不得不提为什么我们要使用原型,在js早期,我们创建一个对象,比较流行的做法是使用对象字面量去创建一个对象,例如:

const person = {
            name: "wywy",
            age: 21,
            hobby: "听周杰伦"
}

用这种方式去创建对象,虽然简洁明了,但是我们如果需要大批量的创建这一类的对象,就像person这个对象,我们可能需要去创建多个不同的人,那么每一次都需要去声明创建,这样工作量是巨大的,为了解决这个问题我们引入了工厂函数的概念。

2、工厂函数

什么是工厂函数,顾名思义就可以把工厂函数看作是一个流水线的工厂,这个工厂的作用就是批量生产person对象,就像下面这样:

function createPerson(name,age,hobby){
	const obj = {};
	obj.name = name;
	obj.age = age;
	obj.hobby = hobby;
	return obj;
}
const person1 = createPerson("zs", 23, "滑板");
const person2 = createPerson("ls", 22, "听歌");
console.log(person1);
console.log(person2);

这里我们在创建person对象的时候只需要调用这个函数即可,并把每个对象对应的属性值传入就好了,这样相对于用对象字面量去创建一个个的person确实简化了代码量,但是工厂函数也有自身的缺点,就是我们不能去判断出这个对象的类型,按我们知道的在 js 中复杂引用数据类型进行细分,有 Array,Function,String 等,但是通过工厂函数模式创建的这些对象用控制台打印去全部都是 Object 类型,假如你有多个工厂函数,用多个工厂函数创建了多个实例,但是你却并不知道这些对象属于哪个工厂。为此又推出了构造函数这个概念。

3、构造函数

关于构造函数其实它和工厂函数非常相似,在js的函数中其实并没有单独的一类函数叫做构造函数,构造函数总是和new关键字一起使用,更准确地说一个函数被构造调用了。当一个构造函数不使用new关键字调用时,他和普通的函数无异。

function CreatePerson(name, age, hobby) {
            this.name = name;
            this.age = age;
            this.hobby = hobby;
}
const person1 = new CreatePerson("zs", 23, "滑板");
const person2 = new CreatePerson("ls", 22, "听歌");
console.log(person1);
console.log(person2);

仔细观察,不难发现我们对上面的工厂函数进行了以下几点改造:

1、我们将函数名的首字母大写了,在 js 中有个规定如果你以后打算将一个函数作为构造函数去使用那么最好把它的函数名首字母大写,来提醒使用者这是一个构造函数。其实不大写也不会有什么语法错误。

2、我们取消了在函数内部显示的声明一个对象 “const obj = {}”,并一并取消了最后返回这个对象的操作 “return obj”。

3、在调用这个这个 CreatePerson 函数时在前面加上了 new 关键字。

然后我们看这个结果,这个时候我们打印的对象不在是 Object 了,而是我们自己创建的函数 CreatePerson。看来构造函数确实解决了对象无法判定类型的问题。
那么神奇的new关键字在后台做了什么呢?其实它做了下面的五件事

1、在内存中创建一个新的对象。

2、让新对象的内部特性 [[Prototype]] 保存函数 CreatePerson 的 prototype 的属性值,也就是把函数的原型的指针保存到了 [[Prototype]] 中。

3、把函数内部的 this 指向这个新创建的对象。

4、执行函数内部的代码。

5、如果函数本身没有返回对象,那么就把这个新对象返回。

我们看构造函数原来在后台为我们做了这么多事。那么构造函数就完美了吗?并不是的,我们想一个 person 是不是应该也该给他加一个方法呢?那么我们就给加一个说话的方法吧:

function CreatePerson(name, age, hobby) {
     this.name = name;
     this.age = age;
     this.hobby = hobby;
     // 添加一个方法
     this.sayHi = function() {
         console.log("你好,我叫" + this.name)
     }
 }
const person1 = new CreatePerson("zs", 23, "滑板");
const person2 = new CreatePerson("ls", 22, "听歌");
 console.log(person1);
 console.log(person2);

但是我们发现这样做无疑为每一个实例对象都添加了一个 sayHi 方法,而且每个实例上的方法都不相等,但我们的目的是让每个实例都有这么一个功能就好了,不必创建这么多的 sayHi 方法而去浪费内存空间。说的直白一点,比如家里有五个孩子,每个孩子都想玩 switch 游戏,那么家长要给每个孩子买一台 switch 吗?当然家里有矿的当我没说,一般家庭是不是就买一台,然后哪个孩子想玩就管家长去要就行了是不是。

同样的,代码就是对现实生活的抽象,那么我们是不是也可以这样做,把方法添加到这些实例都拥有的一个爸爸是不是就好了,而仔细想想在 new 的五步中第二步是不是做了这么一件事,没错他就是 js 中的原型。这个原型就是这些实例的爸爸。

说了这么多我们总算要讲原形了!!!

二、使用原型

通过上面的讲解我们似乎对原型的作用有了大致的理解,就是把实例对象上需要用到的共有方法添加到原型上,而实例对象的自己的私有属性写在构造函数内部。
接下来我们对构造函数进行再一次改造:

function CreatePerson(name, age, hobby) {
     this.name = name;
     this.age = age;
     this.hobby = hobby;
     // 添加一个方法
     this.sayHi = function() {
         console.log("你好,我叫" + this.name)
     }
 }
const person1 = new CreatePerson("zs", 23, "滑板");
const person2 = new CreatePerson("ls", 22, "听歌");
 console.log(person1);
 console.log(person2);

首先向大家说明一点我并没有按着定义一个构造函数,然后在构造函数的原型上添加 sayHi 方法,接着使用 new 创建实例的顺序来写代码。而是先 new 创建了实例,然后再在原型上添加方法,这样的目的是想告诉大家,原型是具有动态性的,即你先创建了实例,在实例之后给原型添加了方法那么实例依然是可以访问的。而且可以看到通过比较 person1 和 person2 的 sayHi 方法我们发现这是同一个方法。这样我们完美的解决了构造函数的问题。

三、原型概念辨析

首先清楚两个概念:

  • 引用类型,都具有对象特性,即可自由扩展属性。(引用类型:Object、Array、Function、Date、RegExp)
  • 每个函数function都有一个显示原型prototype,每个实例对象都有一个隐式原型__proto__
function Fn() { // 内部语句:this.prototype = {}
}
// 1、每个函数function都有一个prototype,即显示原型(属性)
console.log(Fn.prototype);
// 2、每个实例对象都有一个__proto__,可称为隐式原型(属性)
var fn = new Fn(); // 内部语句: this.__proto__ = Fn.prototype
console.log(fn.__proto__);

两个准则:

在设计js原型原型链的时候遵循以下两个准则:

准则一: 原型对象(即Fn.prototype)的 constructor 指向构造函数本身。

准则二: 实例对象(即 fn )的__proto__ 指向其构造函数的显示原型。

function Fn() {}
var fn = new Fn();
// 原型对象的 constructor 指向构造函数本身
console.log(Fn.prototype.constructor === Fn); // true
// 对象的隐式原型的值为其对应构造函数的显示原型的值
console.log(Fn.prototype === fn.__proto__); // true

理解Function与Object特例

每个函数都是 Function 的实例,所以每个函数既有显示原型又有隐式原型,所有函数的隐式原型指向 Function.prototype; 构造器Function的构造器是它自身。

// function Foo() {} 相当于 var Foo = new Function()
// Function = new Function() => Function.__proto__ = Function.prototype
// Object 作为构造函数时,其 __proto__ 内部属性值指向 Function.prototype
// Object.__proto__ = Function.prototype
// Function.constructor=== Function;//true

Object构造函数创建一个对象包装器。JavaScript中的所有对象都来自 Object,所有对象都是Object的实例;所有对象从Object.prototype继承方法和属性,尽管它们可能被覆盖。

// Fn的原型对象(Fn.prototype)也来自Object,故Fn.prototype.__proto__ = Object.prototype
function Fn() {}

原型链:

读取某个对象的属性时,会自动找到原型链中查找。

  • 1、现在自身属性中查找,找到返回
  • 2、找不到则继续沿着__proto__这条链向上查找,找到返回
  • 3、如果最终没有找到。返回undefined设置对象的属性值时,不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值方法一般定义在原型中,属性一般通过构造函数定义在对象本身

原型链就是一个过程,原型是原型链这个过程中的一个单位,贯穿整个原型链

图解

四、原型链练习

//练习题1
function A(){
}
A.prototype.n = 1;
var b = new A();
A.prototype = {
	n:2,
	m:3
}
var c = new A();
console.log(b.n,b.m,c.n,c.m)
// 1 undefined 2 3
// 测试题2
var F = function() {
};
Object.prototype.a = function() {
    console.log('a()');
};
Function.prototype.b = function() {
    console.log('b()');
};
var f = new F();
f.a(); // a()
f.b(); // 报错:Uncaught TypeError: f.b is not a function
F.a(); // a()
F.b(); // b()

原型、原型链的意义与使用场景:

原型对象的作用,是用来存放实例中共有的那部分属性、方法、可以大大减少内存消耗。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • JavaScript两张图搞懂原型链

    目录 1.原型的关系 2.原型链 3.结语 前言: 我们上一篇文章介绍了JavaScript原型,为什么不将原型链一起介绍了呢?因为JavaScript中的原型链是一个难点,也是一个面试必问的考点,现在就来学习一下. 1.原型的关系 在JavaScript中的每个函数都会有一个prototype属性,这个属性又会返回一个原型,原型又有一个constructor属性,这个属性指向与之关联的构造函数.通过构造函数实例化的对象会有一个__proto__属性,这个__proto__属性指向与构造函数的p

  • javascript的构造函数, 原型,原型链和new你了解多少

    目录 1.什么是构造函数? 2.如何通过构造函数创建一个对象? 3.new一个对象的过程发生了什么? 4.什么是原型? 5.原型.构造函数.实例的关系? 6.什么是原型链? 7.类? 总结 1.什么是构造函数? 如果一个函数被设计出来,是用于通过new关键字创建对象的,它就是一个构造函数. 如下: function Father(name,age){this.name = name;this.age = age;}function Father(name,age){ this.name = na

  • javascript原型链图解的总结和实践

    目录 原型链 我们可以基于原型链来实现一个简易的JQuery库 总结 原型链 在ES6中引入了class关键字,但是JS依然是基于原型的,class实际上是语法糖. 举个例子,有一个people class: class People { constructor(props) { this.name = props.name; } run() { console.log('run') } } 通过new people 这个class 产生了许多的人,张三,李四: let lisi = new P

  • 深入了解javascript原型和原型链

    目录 一.什么是原型 二.prototype 三.__proto__ 四.constructor 五.实例与原型 六.原型的原型 七.原型链 一.什么是原型 原型:每一个javascript对象(除null外)创建的时候,都会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中"继承"属性. 例如 var obj = new Object(); 创建一个对象的时候都会同时关联一个对象,如图,关联的这个对象就是新建的对象obj的原型 二.prototype 在JavaS

  • 一篇文章让你看懂Js继承与原型链

    目录 继承与原型链 继承属性 继承方法 在 JavaScript 中使用原型 性能 附:原型链是实现继承的主要方法 总结 继承与原型链 当谈到继承时,JavaScript 只有一种结构:对象.每个实例对象(object)都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype).该原型对象也有一个自己的原型对象(proto),层层向上直到一个对象的原型对象为 null.根据定义,null 没有原型,并作为这个原型链中的最后一个环节. 几乎所有 JavaScript

  • 详解JavaScript中的原型和原型链

    目录 原型链图 原型必备知识 prototype属性(显示原型) proto属性(隐式原型) constructor属性 总结 原型链图 原型必备知识 要了解原型就必须搞清三个属性:__proto__.prototype. constructor. 1.__proto__. constructor属性是对象所独有的: 2.prototype属性是函数独有的: 3.js中函数也是对象的一种,那么函数同样也有属性__proto__. constructor: 原型五大规则: 1.所有引用类型(对象,

  • javascript学习笔记(五)原型和原型链详解

    私有变量和函数 在函数内部定义的变量和函数,如果不对外提供接口,外部是无法访问到的,也就是该函数的私有的变量和函数. 复制代码 代码如下: <script type="text/javascript">     function Test(){         var color = "blue";//私有变量         var fn = function() //私有函数         { }     } </script> 这样在

  • JavaScript使用原型和原型链实现对象继承的方法详解

    本文实例讲述了JavaScript使用原型和原型链实现对象继承的方法.分享给大家供大家参考,具体如下: 实际上JavaScript并不是一门面向对象的语言,不过JavaScript基于原型链的继承方式.函数式语法,使得编程相当灵活,所以可以利用原型链来实现面向对象的编程. 之前对JavaScript一直都是一知半解,这两天看了一下原型链这一块知识,综合练习了一下JavaScript的对象继承方式. 以下就是原型链和原型的关系,引用网上的一张图 在Javascript中,每个函数都有一个原型属性p

  • 深入理解JavaScript系列(6) 强大的原型和原型链

    前言 JavaScript 不包含传统的类继承模型,而是使用 prototypal 原型模型. 虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大.实现传统的类继承模型是很简单,但是实现 JavaScript 中的原型继承则要困难的多. 由于 JavaScript 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的,今天我们就来了解一下原型和原型链. 原型 10年前,我刚学习JavaScript的时候,一般都是用

  • 跟我学习javascript的prototype原型和原型链

    用过JavaScript的同学们肯定都对prototype如雷贯耳,但是这究竟是个什么东西却让初学者莫衷一是,只知道函数都会有一个prototype属性,可以为其添加函数供实例访问,其它的就不清楚了,最近看了一些 JavaScript高级程序设计,终于揭开了其神秘面纱. 每个函数都有一个prototype属性,这个属性是指向一个对象的引用,这个对象称为原型对象,原型对象包含函数实例共享的方法和属性,也就是说将函数用作构造函数调用(使用new操作符调用)的时候,新创建的对象会从原型对象上继承属性和

  • JavaScript原型及原型链终极详解

    JavaScript原型及原型链终极详解 一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object,Function 是JS自带的函数对象.下面举例说明 function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)'); var o3 = new f1(); var o1 = {}; var o2 =new Object()

  • JavaScript从原型到原型链深入理解

    构造函数创建对象 我们先使用构造函数创建一个对象: function Person() { } var person = new Person(); person.name = 'Kevin'; console.log(person.name) // Kevin 在这个例子中,Person 就是一个构造函数,我们使用 new 创建了一个实例对象 person. 很简单吧,接下来进入正题: prototype 每个函数都有一个 prototype 属性,就是我们经常在各种例子中看到的那个 prot

  • javascript 原型与原型链的理解及实例分析

    本文实例讲述了javascript 原型与原型链的理解.分享给大家供大家参考,具体如下: javascript中一切皆对象,但是由于没有Class类的概念,所以就无法很好的表达对象与对象之间的关系了. 比如对象A与对象B之间,它们两个是相对独立的个体,互不干扰,对象A修改自身的属性不会影响到对象B. 虽然这很好,但是有一个问题,如果对象A与对象B都有一个方法 run() ,并且代码也一样,那对象A与对象B各自都独立拥有一份 run() 方法的完整代码,这是需要资源去保存的. 一旦我们程序中应用的

  • javascript 原型与原型链的理解及应用实例分析

    本文实例讲述了javascript 原型与原型链的理解及应用.分享给大家供大家参考,具体如下: javascript中一切皆对象,但是由于没有Class类的概念,所以就无法很好的表达对象与对象之间的关系了. 比如对象A与对象B之间,它们两个是相对独立的个体,互不干扰,对象A修改自身的属性不会影响到对象B. 虽然这很好,但是有一个问题,如果对象A与对象B都有一个方法 run() ,并且代码也一样,那对象A与对象B各自都独立拥有一份 run() 方法的完整代码,这是需要资源去保存的. 一旦我们程序中

  • JavaScript进阶(四)原型与原型链用法实例分析

    本文实例讲述了JavaScript原型与原型链用法.分享给大家供大家参考,具体如下: 一句话说明什么是原型:原型就是一个JavaScript对象,原型能存储我们的方法,构造函数创建出来的实例对象能够引用原型中的方法. 一.传统构造函数的问题 有如下代码 function Foo(){ this.sayHello = function(){ } } 由于对象是调用new Foo()所创建出来的,因此每一个对象在创建的时候,函数 sayHello 都会呗创建一次 那么有没一个对象都含有一个独立的,不

  • 详解JavaScript原型与原型链

    正如一些面向对象语言中所实现的那样,在JavaScript中我们有时也需要创建一个拥有公共函数与属性的类作为父类来减少代码重复.实现类型检查与实现更加清晰地代码结构.在JavaScript中,继承是通过原型链实现的.了解JavaScript的继承与原型链之前首先需要了解JavaScript中对象创建的方式. 在JavaScript中创建对象 JavaScript中对象创建的方式有两种:工厂方法(Factory Functions).构造器方法(Constructor Functions) . 工

随机推荐