XieYang-blog

javascript难点剖析一:prototype(原型)

要理解 javascript 中的 prototype,首先必须弄清楚以下几个概念

  1. javascript 中所有的东西都是对象
  2. javascript 中所有的东西都由 Object 衍生而来, 即所有东西原型链的终点指向 Object.prototype
    [“constructor”, “toString”, “toLocaleString”, “valueOf”, “hasOwnProperty”, “isPrototypeOf”, “propertyIsEnumerable”, “defineGetter“, “lookupGetter“, “defineSetter“, “lookupSetter“]
    console.log(Object.getOwnPropertyNames(Object.prototype));
  3. javascript 中构造函数和实例(对象)之间的微妙关系
    构造函数通过定义 prototype 来约定其实例的规格, 再通过 new 来构造出实例, 他们的作用就是生产对象.
    而构造函数本身又是 Function 的实例, 因此也可以查到它的 proto (原型链)

构造函数与实例

构造函数

Object //javascript 原生API提供的构造函数
function Foo() {} //自定义的构造函数

实例

new Object()
new Foo()
实例就“只能”查看 proto 来得知自己是基于什么 prototype 被制造出来的,而“不能”再重新定义实例的 prototype (即不能定义实例的实例)

构造函数到底是什么

1
2
3
4
5
6
7
8
9
function Empty() {}
console.log(Function.prototype, Function.__proto__) // Object {}

function Empty() {}
console.log(Object.prototype, Object.__proto__)

function Foo() {} // Foo {}
function Empty() {}
console.log(Foo.prototype, Foo.__proto__)

prototype 输出的格式为: 构造函数名 原型

首先看下Object.prototype输出了什么?
Object {} -> 前面的Object为构造函数的名称, 后面的那个表示原型, 这里是一个{}, 即一个Object对象的实例(空对象)
那么 Foo {} 我们就明白是什么意思了, Foo 就是构造函数的名称, 原型也是一个空对象

再来看看由构造函数构造出来的实例

1
2
3
4
5
6
7
var 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
5
function 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
7
function 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
4
function Foo() {}
Foo.prototype = {
a: function() {}
}

重新指定正确的构造函数

1
2
3
4
Foo.prototype.constructor = Foo;
var i = new Foo(); // undefined
Foo {a: function, constructor: function}
console.log(i.prototype, i.__proto__);

现在又能得到正确的原型信息了

原型链

简单的来讲和OOP中的继承关系(链)是一样的, 一层一层往上找, 直至最终的 Object.prototype
prototype chain

🐶 您的支持将鼓励我继续创作 🐶