JS基础之原型
Table of Contents
什么是原型 #
当我们用一个构造函数创建一个实例对象的时候,实例对象里就产生了一个指针指向了它的原型。
这个原型就是构造函数的原型对象。
fuction Person() {
}
var person = new Person()
console.log(person.__proto__ === Person.prototype) // true
每个构造函数都有一个原型对象,并且这个原型中有个指针指向构造函数。
console.log(Person.prototype.constructor === Person) // true
以上的关系就如下图所示
原型的原型 #
上面我们提到过,原型其实就是个对象,既然是个对象,我们就能通过new Object()
的方式创建它,由此可见,Person.prototype
里也有个指针指向了Object.prototype
,将这层关系补充到图示上:
原型链 #
function Person() {
}
Person.prototype.name = 'Kavin'
var person = new Person()
person.name = 'Helen'
console.log(person.name) // 'helen'
delete person.name
console.log(person.name) // 'kevin'
当我们想要获取实例属性时,首先会在实例中查找,如果没有,就会在实例的原型中查找,如果还没有,就继续查找原型的原型,直到最后Object.prototype
的原型指向null
才停止。
这就是所谓的原型链。
需要注意的问题 #
constructor #
前面有提到,实例对象中有个指针指向构造函数,这个指针就是constructor
。
以上面的例子,我想要获取person.constructor
,而实例person
上并没有这个属性,但原型上有啊,所以我们实际获得的是Person.prototype.constructor
。
console.log(person.constructor) // function Person() {}
proto #
前面有提到原型的原型指向Object.prototype
的问题,但Person.prototype
里并没有__proto__
这个属性,却可以因此获取到Object.prototype
,这是为什么?
与其说这是个属性,不如理解成数据劫持里的getter/setter
,每次读取时都返回的是Object.getPrototypeOf(obj)
方法。
继承 #
ECMAScript将原型链作为实现继承的主要方法,通过原型让一个引用类型继承另一个引用类型的属性和方法。
function Father() {
this.son = 'Ross'
}
Father.prototype.getSonName = function() {
return this.son
}
function Son() {
this.father = 'Jake'
}
Son.prototype = new Father() // 继承自Father
son.prototype.getFatherName = function() {
return this.father
}
var family = new Son()
console.log(family.getSonName) // 'Ross'
上面的例子是通过创建Father
的实例然后赋值给Son.prototype
的方法实现继承的,也就是重写原型对象。
如果我们获取family.father
属性,会直接从family
对象上找到,因为它是Son
的实例对象,father
是Son
定义的一个实例属性,因为this
指向的就是这个构造函数创建的实例对象。而family.son
属性则会在family
的原型Son.prototype
上找到,因为它是Father
的实例对象。
也正由于原型链的这一特性,导致包含引用类型值的原型属性会被所有实例共享。
function SuperType() {
this.color = ['black', 'red', 'green']
}
function SubType() {
}
SubType.prototype = new SuperType()
var instance1 = new SubType()
instance1.color.push('yellow')
var instance2 = new SubType()
console.log(instance2) // ['black', 'red', 'green', 'yellow']
我们通过第一个实例改变了原型上的属性color
,导致第二个实例上的color
属性也发生了变化,就是因为它们读取的是同一个引用类型。