__proto__,prototype,constructor
在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。
-
__proto__属性,所有对象都拥有__proto__属性,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型
-
prototype属性,只有函数对象才有prototype属性(几乎所有的函数都有)。这个属性是指向自己的原型对象,原型对象的用途就是包含所有实例共享的属性和方法
-
constructor属性,原型对象的属性,这个属性指回原构造函数。
__proto__ 与 prototype
显式原型的作用:用来实现基于原型的继承与属性的共享。
隐式原型的作用:构成原型链,同样用于实现基于原型的继承。
function Person(name, age) {
var gender = girl // ①
this.name = name // ②
this.age = age
}
// ③
Person.prototype.sayName = function() {
alert(this.name)
}
// ④
var kitty = new Person('kitty', 14)
kitty.sayName() // kitty
Person 是一个"构造函数"(它用来"构造"对象,并且是一个函数)
①处gender是该构造函数的“私有属性”
②处的语句定义了该构造函数的“自有属性”
③处的prototype是Person的"原型对象",该对象上定义的所有属性和方法都会被"实例对象"所"继承"
④处的变量"kitty"的值是构造函数Person的“实例对象”,它可以访问到两种属性,一种是通过构造函数生成的“自有属性”,一种是原型对象可以访问的所有属性
原型链
当JavaScript引擎发现一个对象访问一个属性时,会首先查找对象的“自有属性”,如果没有找到则会在__proto__属性指向的原型中继续查找,如果没找到就继续查找,直到找到最顶部的Object.prototype,如果还是没有找到,会返回一个undefined值。这个不断查找的过程,有一个形象生动的名字“攀爬原型链”。
继承
在JavaScript中,实现继承的方式有以下两种:
- 创建一个对象并指定其继承对象(原型对象);
- 修改构造函数的原型属性(对象);
(一)关于Object.create() 和对象继承
正如之前所说,Object.create()函数是JavaScript提供给我们的一个在创建对象时设置对象内部__proto__属性的API。
var x = {
name: 'tom',
sayName: function() {
console.log(this.name)
}
}
var y = Object.create(x, {
name: {
configurable: true,
enumerable: true,
value: 'kitty',
writable: true,
}
})
y.sayName() // 'kitty'
Object.create()函数接收两个参数,第一个参数是创建对象想要继承的原型对象,第二个参数是一个属性描述对象。
- 创建了一个空对象,并赋值给相应变量;
- 将第一个参数对象设置为该对象__proto__属性的值;
- 在该对象上调用defineProperty()方法,并将第二个参数传入该方法中;
这样的方法有很多局限,比如我们只能在创建对象时设置对象的继承对象,又比如这种设置继承的方式是一次性的,我们永远无法依靠这种方式创造出多个有相同继承关系的对象,而对于这种情况,我们需要介绍 prototype原型对象。
(二)关于prototype 和构造函数继承
构造函数生产实例对象的过程本身就是一种天然的继承。
实例对象天然的继承着原型对象的所有属性,这其实是JavaScript提供给开发者第二种(也是默认的)设置对象__proto__属性的方法。
function Foo(x, y) {
this.x = x
this.y = y
}
Foo.prototype.sayX = function() {
console.log(this.x)
}
Foo.prototype.sayY = function() {
console.log(this.y)
}
function Bar(z) {
this.z = z
this.x = 10
}
Bar.prototype = Object.create(Foo.prototype) // 注意这里
Bar.prototype.sayZ = function() {
console.log(this.z)
}
Bar.prototype.constructor = Bar
var o = new Bar(1)
o.sayX() // 10
o.sayZ() // 1
(三)构造函数窃取
function Foo(x, y) {
this.x = x
this.y = y
}
Foo.prototype.sayX = function() {
console.log(this.x)
}
Foo.prototype.sayY = function() {
console.log(this.y)
}
function Bar(z) {
this.z = z
this.x = 10
Foo.call(this, z, z) // 注意这里
}
Bar.prototype = Object.create(Foo.prototype)
Bar.prototype.sayZ = function() {
console.log(this.z)
}
Bar.prototype.constructor = Bar
var o = new Bar(1)
o.sayX() // 1
o.sayY() // 1
o.sayZ() // 1