1. 专业名词理解
- js是以对象为驱动编写的,即一切皆对象,所有数据都是对象。
- 要理解原型链,就是理解函数、原型对象、实例对象之间的关系。
constructor
一般称为构造函数属性,指向函数本身。prototype
一般称原型对象属性,指向函数的原型对象。__proto__
我下面称为原型属性,指向原型对象。[[Prototype]]
我下面称为原型链属性,指向原型对象。- 原型链:每个JavaScript对象都有一个内部属性,称为
[[Prototype]]
,它指向另一个对象。这个被指向的对象就是当前对象的原型。当试图访问一个对象的属性或方法时,如果该对象本身没有这个属性,JavaScript引擎会沿着原型链向上查找,直到找到这个属性或直到原型链的尽头Object.prototype
或null
(因为null的原型是undefined)。
2. 函数
原型对象属性prototype
与构造函数属性constructor
- 函数上的原型对象属性
prototype
指向函数的原型对象,原型对象上的constructor
指向函数,他们是循环引用的。
- 上图中,
Foo,Object,Function
都是函数,Object,Function
是内置函数,Foo
是自定义的函数。 - 原型对象属性
prototype
只存在于函数上。 Foo.prototype.constructor === Foo
原型属性__proto__
与原型链属性[[Prototype]]
在JavaScript中,
__proto__
和[[Prototype]]
密切相关,但它们之间存在关键的区别:
[[Prototype]]
:这是一个对象的内部属性,由JavaScript引擎管理,它指向对象的原型。这个属性对开发者是不可见的,不能直接访问或修改。[[Prototype]]
的值决定了对象的原型链,即在查找属性和方法时的搜索路径。__proto__
:这是ECMAScript规范中定义的一个访问器属性,它提供了对对象内部[[Prototype]]
属性的访问。__proto__
允许开发者在代码中读取和设置对象的原型。在非严格模式下,__proto__
可以用来读取和设置对象的原型。然而,在严格模式下,直接修改__proto__
可能会引发错误。
关系和区别:
- 访问和修改:
__proto__
提供了对[[Prototype]]
的访问,但它是[[Prototype]]
的一个外部表现,而不是[[Prototype]]
本身。通过__proto__
,开发者可以读取和(在某些情况下)设置对象的原型。- 内部与外部:
[[Prototype]]
是内部的,不可直接访问;__proto__
是外部的,通过它来间接访问[[Prototype]]
。- 规范与实现:
[[Prototype]]
是由ECMAScript规范定义的,而__proto__
是一个实现细节,虽然被广泛支持,但在严格模式下使用时需要谨慎。
总之,
__proto__
是[[Prototype]]
的一个访问器,它使得开发者能够以一种可控的方式与对象的原型链交互。然而,由于其可能带来的副作用,使用时应遵循最佳实践,尤其是在严格模式下编程时。
使用方法获取原型对象,Object.getPrototypeOf(Foo) === Foo.__proto__
原型对象、实例对象上存在__proto__
,函数上也存在__proto__
,因为函数也是实例对象,Function
的实例对象
Foo.__proto__ === Function.prototype
Object.__proto__ === Function.prototype
Function.__proto__ === Function.prototype
3. 原型链:原型对象之间的关系
Foo.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
Function.prototype.__proto__ === Object.prototype
4. 实例对象的原型链
f.__proto__ === Foo.prototype
Foo.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
5. new运算符的原理
在JavaScript中,new
关键字用于创建一个由构造函数定义的新对象。使用 new
关键字时,会经历以下步骤:
- 创建新对象:首先,
new
会创建一个空的简单JavaScript对象,并且会设置该对象的[[Prototype]]
属性,让它指向构造函数的prototype
属性所指向的对象。 - 绑定
this
:然后,构造函数被调用,而this
关键字被绑定到上一步创建的新对象。这意味着构造函数内部的this
现在指向新创建的对象。 - 执行构造函数:构造函数的代码开始执行。构造函数通常会设置新对象的属性和值。
- 确定返回值:构造函数执行完毕后,如果构造函数显式返回一个对象,则返回该对象。如果构造函数没有返回对象,或者返回的是基本类型值,
new
表达式则返回新创建的对象。
//new的实现
var new2 = function (func) {
var o = Object.create(func.prototype); //1.创建新对象,并设置原型
var k = func.call(o); //2.绑定this 3.执行构造函数
if (typeof k === 'object') { //4.确定返回值
return k;
} else {
return o;
}
}
6. 总结
- 函数与原型对象的关系
Foo.prototype.constructor === Foo
Object.prototype.constructor === Object
- 函数与函数的关系
Foo.__proto__ === Function.prototype
Object.__proto__ === Function.prototype
Function.__proto__ === Function.prototype
- 原型链:原型对象与原型对象的关系
f.__proto__ === Foo.prototype
Foo.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
Function.prototype.__proto__ === Object.prototype