前言
原型与原型链,在JS中是个重难点知识,不少人被它们之间绕来绕去的关系弄得云里雾里,现在我就试着写一篇文章,讲讲我对原型与原型链的理解,希望对你有帮助。
知识点
在进入主题前,我们先要记住以下知识点。可能有些绕,一遍看不懂没关系,多看几遍,一定要记住这些概念。
- 只有对象有
__proto__
和constructor
属性。 - 只有函数具有prototype属性,因为函数也是一种对象,所以函数也拥有
__proto__
和constructor
属性。 - 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个
prototype
属性,这个属性指向函数的原型对象,可以理解为函数.prototype就是函数的原型对象。 - 在默认情况下,所有原型对象都会自动获得一个
constructor
(构造函数)属性,这个属性包含一个指向prototype
属性所在函数的指针。
即:函数.prototype.constructor === 函数 ==> 构造函数.prototype.constructor === 构造函数
- 当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性
__proto__
),指向构造函数的原型对象。即:实例(对象)的__proto__
=== 其构造函数.prototype 。__proto__
属性在ES5标准里管这个指针叫[[Prototype]]
,在ES5中没有标准的方式访问[[Prototype]]
,但现代浏览器在每个对象上都支持一个属性__proto__
,__proto__
===[[Prototype]]
。
构造函数
记住了上面的概念,我们从两行代码说起:
function Foo(){};
var f1 = new Foo;
其中,Foo是构造函数,f1是Foo构造出的实例对象。
按照上面的结论,应该得出以下结果
// 只有函数具有prototype属性
f1.prototype === undefined // true
// 实例(对象)的__proto__ === 其构造函数.prototype
f1.__proto__ === Foo.prototype // true
Foo.__proto__ === Function.prototype // true
// 构造函数.prototype.constructor === 构造函数
Foo.prototype.constructor === Foo // true
// f1.__proto__ === Foo.prototype,Foo.prototype.constructor === Foo
f1.__proto__.constructor === Foo // true
f1.constructor === Foo // true
经实际验证,结论都正确
函数对象与普通对象
JavaScript 中,有两种对象类型。分为普通对象和函数对象,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。通过typeof
也能方便区分,结果为'function
'的,都是函数对象。
// 所有的函数对象都来自于Function.prototype,甚至包括Object及Function自身
Number.__proto__ === Function.prototype // true
Number.constructor == Function //true
Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true
String.__proto__ === Function.prototype // true
String.constructor == Function //true
Object.__proto__ === Function.prototype // true
Object.constructor == Function // true
Array.__proto__ === Function.prototype // true
Array.constructor == Function //true
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true
RegExp.__proto__ === Function.prototype // true
RegExp.constructor == Function //true
Error.__proto__ === Function.prototype // true
Error.constructor == Function //true
Date.__proto__ === Function.prototype // true
Date.constructor == Function //true
Foo.prototype是原型对象,原型对象也是一个普通对象,由于一个普通对象的构造函数 === Object,因此
Foo.prototype.__proto__ === Object.prototype // true
Function.prototype.__proto__ === Object.prototype // true
延伸一下,Object.__proto__ === Function.prototype
,Object.__proto__.__proto__ === Function.prototype.__proto__
,由于Function.prototype.__proto__ === Object.prototype
,所以Object.__proto__.__proto__ === Object.prototype
经过以上的分析,思考一下下面的问题
Object.prototype.__proto__ === Object.prototype // false
Object.prototype是原型对象,按理说上面值应该相等才对,然而结果是不相等,这里引入新的知识点:
__proto__
属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__
属性所指向的那个对象(父对象)里找,一直找,直到__proto__
属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__
属性将对象连接起来的这条链路就是所谓的原型链。
因为null处于原型链的顶端。所以Object.prototype.__proto__
的值为null。
Object.prototype.__proto__ === null // true
总结
最后总结一下文章中出现的所有重要知识点:
- 只有对象有
__proto__
和constructor
属性。 - 只有函数具有prototype属性,因为函数也是一种对象,所以函数也拥有
__proto__
和constructor
属性。prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法。constructor属性的作用就是指向该对象的构造函数。 - 函数.prototype就是函数的原型对象。
- 构造函数.prototype.constructor === 构造函数,实例.constructor === 构造函数。
- 实例(对象).
__proto__
=== 其构造函数.prototype 。 - JavaScript 中 凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。 即:
typeof
结果为'function
'的,都是函数对象。 - 所有的函数对象都可以看成是构造函数Function()的new操作的实例化对象。函数对象(Function也是函数)是new Function的结果,所以函数可以作为实例对象,其构造函数是Function(),原型对象是Function.prototype,即:所有函数对象的
__proto__
=== Function.prototype,包括Object及Function自身 - 所有的普通对象都可以看成是Object()构造函数的new操作的实例化结果。普通对象(函数也是对象)是new Object的结果,所以对象可以作为实例对象,其构造函数是Object(),原型对象是Object.prototype,即:所有普通对象的
__proto__
=== Object.prototype - 以上两点结论是基于原型链查找规则的,当实例对象有自定义的构造函数时,首先指向该构造函数,否则会向上查找,直到
__proto__
指向Object.prototype。再往上就是null了,null位于原型链顶端。 Object.__proto__.__proto__ === Object.prototype
Object.prototype.__proto__ === null
这些知识点里,弄明白原型链指向的核心就是实例(对象).__proto__
=== 其构造函数.prototype ,只要分清楚实例对象究竟是由哪个构造函数实例化的结果,就可以轻易弄清原型链的指向了。
function Foo(){};
var f1 = new Foo;
f1.__proto__ === Foo.prototype
//f1是普通对象,Foo是f1的构造函数
Foo.__proto__ === Function.prototype // Function是Foo的构造函数
Function.__proto__ === Function.prototype // Function是Function的构造函数
附
f1.constructor为: ƒ Foo() {}
Foo.constructor: ƒ Function() { [native code] }
f1.prototype: undefined
Foo.prototype: {constructor: ƒ}
f1.__proto__: {constructor: ƒ}
Foo.prototype.__proto__: {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
Foo.__proto__: ƒ () { [native code] }
console.log('f1.constructor: ',f1.constructor);
console.log('Foo.constructor: ',Foo.constructor);
console.log('f1.prototype: ',f1.prototype);
console.log('Foo.prototype: ',Foo.prototype);
console.log('f1.__proto__: ',f1.__proto__);
console.log('Foo.prototype.__proto__: ',Foo.prototype.__proto__);
console.log('Foo.__proto__: ',Foo.__proto__);
console.log('Foo.prototype.constructor: ',Foo.prototype.constructor);
版权属于:Sanakey(特殊声明除外)
本文链接:https://keymoe.com/archives/93/
所有原创文章采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。 您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。