要理解 javascript 中的 prototype,首先必须弄清楚以下几个概念
- javascript 中所有的东西都是对象
- javascript 中所有的东西都由 Object 衍生而来, 即所有东西原型链的终点指向 Object.prototype
[“constructor”, “toString”, “toLocaleString”, “valueOf”, “hasOwnProperty”, “isPrototypeOf”, “propertyIsEnumerable”, “defineGetter“, “lookupGetter“, “defineSetter“, “lookupSetter“]
console.log(Object.getOwnPropertyNames(Object.prototype));- javascript 中构造函数和实例(对象)之间的微妙关系
构造函数通过定义 prototype 来约定其实例的规格, 再通过 new 来构造出实例, 他们的作用就是生产对象.
而构造函数本身又是 Function 的实例, 因此也可以查到它的 proto (原型链)
构造函数与实例
构造函数
Object //javascript 原生API提供的构造函数
function Foo() {} //自定义的构造函数
实例
new Object()
new Foo()
实例就“只能”查看 proto 来得知自己是基于什么 prototype 被制造出来的,而“不能”再重新定义实例的 prototype (即不能定义实例的实例)
构造函数到底是什么
1 | function Empty() {} |
prototype 输出的格式为: 构造函数名 原型
首先看下Object.prototype输出了什么?
Object {} -> 前面的Object为构造函数的名称, 后面的那个表示原型, 这里是一个{}, 即一个Object对象的实例(空对象)
那么 Foo {} 我们就明白是什么意思了, Foo 就是构造函数的名称, 原型也是一个空对象
再来看看由构造函数构造出来的实例1
2
3
4
5
6
7var o = new Object(); // var o = {}; // undefined
Object {}
console.log(o.prototype, o.__proto__);
function Foo() {}
var i = new Foo(); // undefined
Foo {}
console.log(i.prototype, i.__proto__);
我们再深入一点, 定义下 F 的原型看看到底会发生些什么?1
2
3
4
5function Foo() {}
Foo.prototype.a = function() {};
var i = new Foo(); // undefined
Foo {a: function}
console.log(i.prototype, i.__proto__);
这样我们就清楚的看到 i 是由 Foo 构造出来的, 原型是 {a: function}, 就是原本的空对象原型新增了一个 a 方法
我们再换一种情况, 完全覆盖 Foo 的原型会怎么样?1
2
3
4
5
6
7function Foo() {}
Foo.prototype = {
a: function() {}
};
var i = new Foo(); // undefined
Object {a: function}
console.log(i.prototype, i.__proto__);
咦~ 为什么这里表明 i 是由 Object 构造出来的? 不对吧!
因为我们完全将 Foo 的 prototype 覆盖, 其实也就是将原型指定为对象{a: function}, 但这会造成原本的 constructor 信息丢失, 变成了对象{a: function}指定的 constructor。
那么对象{a: function}的constructor是什么呢?
因为对象{a: function}其实就相对于
var o = {a: function() {}} // new了一个Object
那么 o 的 constructor 当然是 Object 啦
我们来纠正下这个错误1
2
3
4function Foo() {}
Foo.prototype = {
a: function() {}
}
重新指定正确的构造函数1
2
3
4Foo.prototype.constructor = Foo;
var i = new Foo(); // undefined
Foo {a: function, constructor: function}
console.log(i.prototype, i.__proto__);
现在又能得到正确的原型信息了
原型链
简单的来讲和OOP中的继承关系(链)是一样的, 一层一层往上找, 直至最终的 Object.prototype
prototype chain