构造函数、原型与原型链
bigdots opened this issue · 0 comments
构造函数
ECMAScript 中提供了构造函数来创建新对象。但构造函数本身就是一个函数,与普通函数没有任何区别,只不过为了区分,一般将其首字母大写,但这并不是必须的。
函数被 new 关键字调用时就是构造函数。
function f(name) {
console.log("execute");
this.name = name;
}
var k = new f("k"); // execute
console.log(k); // {name: "k"}
var h = f("h"); // execute
console.log(h); // undefined
从上面代码可以看出:
- 首字母是否大写并不影响函数 f 作为构造函数使用,
- 不使用 new 调用函数就是普通函数,直接执行内部代码,使用 new,函数的角色就成为了构造函数,创建一个对象并返回。
对象由构造函数通过 new 关键字创造,那么是如何创造的呢?
new 关键字的内部实现机制:
- 创建一个新对象;
- 将构造函数的作用域赋值给新对象;
- 执行构造函数中的代码;
- 返回新对象
var obj = {}; // 创建一个空对象
obj.__proto__ = constructor.prototype; // 添加__proto__属性,并指向构造函数的 prototype 属性。
constructor.call(this); // 绑定this
return obj;
prototype
每一个函数都有一个
prototype
属性。
function Foo() {}
Foo.prototype; // {constructor,__proto__}
无论什么时候,只要创建了一个新函数,根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。
那么这个创建的原型对象是什么呢?
{
constructor: ƒ Foo(),
__proto__: Object
}
constructor 属性
每一个原型对象都有一个
constructor
属性
创建了自定义的构造函数后,其原型对象只会默认取得 constructor
属性。这个属性解决了对象识别问题,即可以通过该属性判断出实例是由哪个构造函数创建的。
Foo.prototype.constructor === Foo; // true
前面说了,原型对象只会默认取得 constructor
属性,那么原型对象的其他属性(比如:__proto__
)是这么来的呢,这就要说到 __proto__
指针了。
__proto__
每一个实例都有一个
__proto__
指针,指向构造函数的原型对象。
var foo = new Foo();
foo.__proto__ === Foo.prototype; //true
上面提到的构造函数的原型对象它本身也是一个实例,所以在它内部会有一个 __proto__
指针。
想要知道构造函数的原型对象是由什么创建的吗?
活学活用,这里我们可以使用刚刚学到对象指向问题判断的知识。我们想要知道 constructor,那么可以通过实例的原型对象(因为每一个原型对象都有一个 constructor
属性),所以我们可以这样操作:
Foo.prototype.__proto__.constructor // ƒ Object() { [native code] }
这说明,构造函数的原型的原型是由 Object 生成的!!所以,构造函数原型对象的其他方法,则是从 Object 上继承来的。
Foo.prototype.__proto__ === Object.prototype; // true
原型链
原型链的理论主要基于上述提到的构造函数、实例和原型的关系:
- 每一个构造函数都有一个原型对象
- 原型对象都包含一个指向构造函数的 constructor 属性
- 每一个实例都包含一个指向原型对象的
__proto__
指针
其中最最重要的是第三条,依赖这条关系,层层递进,就形成了实例与原型的链条。
我接着上面的探索,构造函数的原型的原型是由 Object 生成的,那么 Object 的原型是由什么生成?而原型链的终点又是在哪?
Object.prototype.__proto__ // null
null.__proto__; // Uncaught TypeError: Cannot read property '__proto__' of null
// game over
原型的终点是 null,因为 null 没有 proto 属性。
哇,感觉这很符合创世纪的故事啊—— “初始,一切且不存在(null)”。
tips: 其实 null 是基本数据类型,typeof null 返回 object 是 ECMAScript 设计上的一个错误