4

忘记了没关系!再学一遍JavaScript原型链

 3 years ago
source link: https://segmentfault.com/a/1190000039003514
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1.原型的概念

先提出一个问题:将函数定义在全局作用域,会污染全局作用域的命名空间(接下来起了个同名函数就会覆盖),而且非常不安全(别人一起写时,也可能会写一样名字的函数)。

怎么办?

——将这些函数放到原型对象中去。那么什么是原型对象?

我们所建立的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是原型对象。

  • 如果函数作为普通函数调用prototype没有任何作用意义。
  • 当函数以构造函数的形式调用时,它所创建的对象中都会包含一个隐含属性,指向构造函数的原型对象,实例中我们可以通过 __proto__ 来访问该属性。
  • 同一个类对应的原型对象是同一个,原型对象相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象, 所以我们可以将对象共有的东西统一设置到原型对象里。

例子:

function person(){
}
var per1=new person();
var per2=new person();
console.log(per1.__proto__==per2.__proto__);//输出true
所以同一个构造函数的实例共有一个原型对象。

在原型对象中设置属性:

例子:

function person(){

}
var per2=new person();
person.prototype.a=123;
console.log(per2.a);//输出123
per2是person的实例,所以可以访问person对象下的原型函数。

上面的 person.prototype.a=123; 就是将a属性设置到了person构造函数的原型函数里。

2.原型链

1.原型链图解

图:实例原型链

UjQnUbQ.png!mobile

先写一段代码(然后再使用代码来验证其中的关系)

/注意:下面对象的原型函数 和 其创建的原型函数是不同的/

/在Object创建的原型链中创建一个函数/
Object.prototype.print=function(){
    console.log("我在Object下的原型对象中");
};
/在Object的原型对象中创建一个函数/
Object.__proto__.print3=function(){
    console.log('我在Object的原型对象中');
}
/创建一个Fun构造函数,这个Fun实际上也是全局下一个普通函数/
function Fun(x,y){
    this.x=x;
    this.y=y;
}
/在Fun创建的原型函数下创建一个函数/
Fun.prototype.print2=function(){
    console.log('Fun类下的原型对象');
}
/使用Fun构造函数来创建一个a实例,a实例属于Fun类下/
var a=new Fun(1,2);

2.理清例子中的原型链

  • 原型链相关的语法( 图中用foo,我们这里定义的是fun

使用构造函数创建一个实例,与该实例一起被创建的还有原型函数,该原型函数在该类下只有一个,该1类的实例共用一个原型函数。

实例使用 实例对象.__proto__ 来访问对原型函数进行读写操作。

构造函数(Object和Fun使用 对象.prototype 来访问其创建的原型函数)

  • Fun和Object的关系
/注意:这里的代码是承接上面的那一段代码的,下面同理/
console.log(Fun===Object);
//false,Fun和Object是两个不同的构造函数
console.log(Fun.__proto__===Object.__proto__);
//true,其共用一个原型对象
console.log(Fun.__proto__.print3());
//我在Object的原型对象中

Fun和Object是同级别的,共用一个原型链。

  • Fun和Object它们对应的原型对象
console.log(Fun.__proto__===Object.__proto__);
//输出true
console.log(Function.__proto__==Fun.__proto__);
//输出true
console.log(Object.__proto__);
//输出{ print3: [Function (anonymous)] }
console.log(Object.__proto__.__proto__);
//输出[Object: null prototype] { print: [Function (anonymous)] }
console.log(Object.__proto__.__proto__.__proto__);
//输出null

说明Object和Fun上有一个共用的原型对象,该原型对象上还有一个空原型对象,再往上就为空了

  • Fun和Object其各自创建的原型对象的关系
console.log(Fun.prototype==Object.prototype);
//false
console.log(Fun.prototype.constructor==Fun);
//true
console.log(Object.prototype.constructor==Object);
//true
console.log(Fun.prototype.constructor.__proto__==Object.__proto__);
//true

说明两个构造函数各种创建的原型对象不是同一个。而且构造函数下的原型对象的构造函数就是其本身,证明了

FveAva2.png!mobile

  • a与Fun的关系
console.log(Object.print3());
//我在Object的原型对象中
console.log(Fun.print3());
//我在Object的原型对象中
console.log(a.print3());
//a.print3 is not a function
console.log(a.__proto__.constructor==Fun);
//true
console.log(a.__proto__.constructor==a.constructor);
//true
console.log(a.constructor==Fun);
//true
console.log(a.constructor==Fun.prototype);
//false
console.log(a.__proto__==Fun.prototype);
//true
console.log(a.print());
//我在Object创建的原型对象中
console.log(a.print2());
//Fun类创建的原型对象中
console.log(Function.__proto__==Fun.__proto__);
//true

可以看出a和Fun是实例与构造函数的关系,a无法访问到 object.__proto__

  • a与Object的关系
console.log(a.constructor.__proto__==Object.__proto__);
//true

这里用代码捋了捋关系,但没讲完精髓,请再看后面!

3.再走一遍原型链

这里为原型链标上序号(注意 函数下的函数的 表达区别)

AfQZfeI.jpg!mobile

先自己分析一遍,明白了就跳过下面内容,直接离开。

  • 序号1:Foo构造函数的实例f1其的原型对象是构造函数Foo下的原型对象,也就是 f1.__proto__==Foo.prototype ,而Foo下的原型对象也是通过Foo构造函数创建的实例,所以其与f1同级,构造函数Function同理。

nMrYFv7.png!mobile

uyqyYvy.png!mobile

  • 序号2:是对象就有原型对象(不是死循环,到object的prototype为止),构造函数Foo下创建的原型对象的原型对象是指向object.prototype的,Foo的实例共有的原型对象是Foo.prototype,而各大构造函数的构造函数下的原型对象的原型对象是object下的原型对象。
  • NFNrInQ.png!mobile
  • 序号3:这里就印证了序号2介绍的第一句话,object.prototype是终端,object是大长老。

AzeuQrM.png!mobile

  • 序号4:到这里就是开始了最重要的部分,因为这里开始出现了function这各构造函数,这里先不讲,留到5来说,Object构造函数的实例o1的原型对象是object.prototype,object的构造函数时function。

QFvUfyV.png!mobile

  • 序号5: 大胆下结论 ,一切构造函数的爸爸(构造函数)都是function。object是function的实例, function是最特殊的,object的原型对象是function下的原型对象,而function.prototype.__proto__又是object.prototype。还有就是 Function.__proto__ == Function.prototype
    理解的钥匙 :function 是关键字,Function 是引用类型。使用function创建的变量是一个指针,指向的对象是一个Function对象。下面有三种函数声明方式,
//第一种:
function a (){};
//第二种:
var a = function(){};
//第三种:
var a = new Function();

V7F3UrY.png!mobile

  • 序号6: Function.prototype.__proto__==Object.prototype ,就像序号2介绍中说的各大构造函数的构造函数下的原型对象的原型对象是object下的原型对象。

    ryaUfer.png!mobile

感谢阅读,如果有更好的理解欢迎留言!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK