JavaScript 是一种基于原型(prototype-based)的面向对象语言。在 JavaScript 中,每一个实例对象都有一个与之相关联的原型链,通过原型链与继承关系,JavaScript 定义了对象的属性。
原型链
每个实例对象都有一个私有属性(__proto__
)指向它的原型(prototype
),该原型也有自己的原型,如此不断向上就会形成一个原型链。原型链的终点为null
,null
不存在原型。在控制台中运行代码可以看到如下日志,其中o.__proto__
就指向对象o
的原型。
var o = { a: 1 }
console.log(o) // { a: 1, __proto__: Object}
复制代码
遵循 ECMAScript 标准,
someObject.[[Prototype]]
符号是用于指向someObject
的原型。从 ECMAScript 6 开始,[[Prototype]]
可以通过Object.getPrototypeOf()
和Object.setPrototypeOf()
访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性__proto__
。 接下来我们使用Object.create
以对象o
为原型创建对象p
。然后改变o.a
的值,观察p.a
的值的变化。
var p = Object.create(o)
console.log(p.a) // 1
console.log(p.__proto__===o) // true
// 完整的原型链是:p ---> o ---> Object.prototype --->null,其中Object.prototype代表Object的原型对象
o.a = 2
console.log(p.a) // 2
// 由于p的属性a继承自o,改变o.a的值,p.a的值也会随之改变
复制代码
现在我们以p
为原型创建对象q
,并重新赋值p.a
。
p.a = function () {
this.x = 1
this.y = 2
}
var q = Object.create(p)
console.log(q.__proto__===p) // true
// 完整的原型链是: q ---> p ---> o ---> Object.prototype ---> null
console.log(q.a) // f () { this.x = 1; this.y = 1 }
// q的属性a继承自原型链上的p
o.b = 3
console.log(q.b) // 3
// q的属性b继承自原型链上的o
console.log(o.c) // undefined
复制代码
当访问q
的某一属性的时候,如果q
本身没有这个属性,就会在q
的原型p
中寻找有没有名称匹配的属性,如果有则返回该属性的值,如果没有则继续向上寻找p
的原型o
中有没有名称匹配的属性,如此向上追溯直至原型链的终点。
When trying to access a property of an object, the property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached.
在 JavaScript 中,任意一个对象都有一个与之相关联的原型链,试图访问该对象的某个属性时,除了对象本身之外,还会访问与其关联的原型链,直到找到名称匹配的第一个属性或者到达原型链的终点为止。
prototype
与 __proto__
上文已经提到,在原型链q ---> p ---> o ---> Object.prototype ---> null
中, Object.prototype
代表Object
的原型对象。与对象的私有属性__proto__
指向对象的原型不同,prototype
是函数(Function
)特有的属性。在控制台中运行如下代码:
var f = function(x, y) {
this.x = x
this.y = y
}
console.log(f.prototype) // {constructor: f (x,y), __proto__: Object}
var o = new f(1,2)
console.log(o.__proto__===f.prototype) // true
console.log(f.prototype.__proto__===Object.prototype) // true
// 完整的原型链是:o ---> f.prototype ---> Object.prototype ---> null
复制代码
原型链上的每一环均为对象,实例对象(instance
)与原型对象(prototype
)。实例对象处于原型链的下游,其源自原型对象,原型对象处于原型链的上游,原型链的顶端是Object.prototype
。因此,在 JavaScript 中几乎所有的对象都是位于原型链顶端的Object
构造函数构造的实例。
根据这一点,我们可以通过改变构造函数的prototype
来改变实例的属性。
f.prototype.z = function() {
this.a = 'a'
this.b = 'b'
}
console.log(o.z) // function() { this.a='a' this.b='b' }
复制代码
可以说 JavaScript 中的所有对象均由函数以构造函数生成,都源自函数的原型对象。而函数本身其实也是一种对象(Function
对象),它们可以像对象一样具有属性和方法。因此经常说函数是 JavaScript 的头等公民(first-class object)。
参考文章