Skip to main content
  1. Posts/

JS基础之原型

·1 分钟

什么是原型 #

当我们用一个构造函数创建一个实例对象的时候,实例对象里就产生了一个指针指向了它的原型。

这个原型就是构造函数的原型对象。

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,将这层关系补充到图示上:

原型-object

原型链 #

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的实例对象,fatherSon定义的一个实例属性,因为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属性也发生了变化,就是因为它们读取的是同一个引用类型。