JS原型与原型链

原型和原型链

这几个要点是理解原型的关键:
1、所有的引用类型(数组、函数、对象)可以自由扩展属性(除null以外)。
2、所有的引用类型都有一个__proto__属性(也叫隐式原型,它是一个普通的对象)。
3、所有的函数都有一个’prototype’属性(这也叫显式原型,它也是一个普通的对象)。
4、所有引用类型,它的__proto__属性指向它的构造函数的’prototype’属性。
5、当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它的__proto__属性(也就是它的构造函数的’prototype’属性)中去寻找。

原型

构造函数的属性prototype,叫做原型,这是可以直接使用的。
在JS实例对象中有一个属性 _proto_ ,它也是原型,这是供浏览器使用的,它不是标准的属性
(实际上,该属性在ES标准定义中的名字应该是[[Prototype]],具体实现是由浏览器代理自己实现,谷歌浏览器的实现就是将[[Prototype]]命名为_proto_)。

实例对象中的__proto__指向的是该实例对象中的构造函数中的prototype,构造函数中的prototype里面的属性或者方法,可以直接通过实例对象调用。

示例:

1
2
function Foo() {...}; // 申明构造函数Foo
console.log( Foo.prototype ); // 打印其prototype属性

控制台输出

1
2
3
4
5
6
7
8
9
10
11
12
{
constructor: ƒ Foo(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}

上面这个对象,就是常说的原型对象
可以看到,原型对象有一个自有属性constructor,这个属性指向该函数
1
Foo.prototype.constructor === Foo // true

原型对象也是一个普通对象,属性__proto__指向其构造函数的原型。

1
Foo.prototype.__proto__ === Object.prototype  // true

原型链

原型链是一种关系,是实例对象和原型对象之间的关系,这种关系是通过原型(proto)来联系的。JavaScript 对象有一个指向一个原型对象的链。
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

示例图:

1
2
function Foo() {...}; 
let f1 = new Foo();

总结

  • 我们需要牢记两点:
    ①__proto__和constructor属性是对象所独有的;
    ② prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。
  • __proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
  • prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,
    即f1._proto_ === Foo.prototype。
    constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。

参考:
帮你彻底搞懂JS中的prototype、_ proto _与constructor(图解)
JavaScript原型,原型链 ? 有什么特点?

2020 - ©小耳朵