Skip to main content

JavaScript原型与原型链

原型(prototype)

每个函数都有一个prototype属性,函数的prototype指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型。每一个javascript对象(null除外)在创建的时候都会关联另外一个对象,这个对象就是原型,每一个对象都会从原型继承属性。 Locale Dropdown

如上图中Person.prototype表示实例原型,也就是Person和Person.prototype之间的关系。

__proto__

每一个javascript对象(除了null)都具有一个属性__proto__这个属性会指向该对象的原型。

function Person{

}

const person = new Person();
person.__proto__ === Person.prototype //true

得到如下关系图:
Locale Dropdown 绝大多数浏览器都支持这个非标准的方法访问原型,然后它并不存在与Person.prototype中,实际上它来自于Object.prototype,与其说它是一个属性不如说是一个getter/setter,当使用obj.\__proto__时可以理解为Object.getPrototypeOf(obj)`

构造器(constructor)

因为构造函数可以生成多个实例,所以原型对象不不能指向实例,但是原型可以通过constructor指向构造函数。

function Person(){

}

Person === Person.prototype.constructor //true

需要注意当获取person.constructor时,其实person中并没有constructor属性,当不能读取到constructor属性时,会从person的原型上也就是Person.prototype中读取,真好原型中有该属性所以

person.constructor === Person.prototype.constructor

实例与原型

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,还是查找不到就去查找原型的原型,一直找到最顶层为止。

function Person(){

}

Person.prototype.name = 'Kevin';
const person = new Person();
person.name = 'Daisy';
console.log(person.name)//Daisy

delete person.name;
console.log(person.name)//Kevin

delete person.name;
console.log(person.name)//Kevin

在这个例子中,给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Daisy。

但是当删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.proto ,也就是 Person.prototype中查找,结果为 Kevin。

原型的原型

原型也是一个对象,那么可以使用最原始的方式创建原型对象

const obj = new Object();
obj.name = 'Kevin';
console.log(obj.name)

原型对象通过Object构造函数生成,所以实例的__proto__指向构造函数的prototype,得出如下关系图:
Locale Dropdown

原型链

Object.prototype的原型指向了null,null表示没有对象即此处不应该有值。所以Object.prototype.__proto__的值为null和Object.prototype没有原型表达了一个意思,所以查找属性查找到Object.prototype就可以停止查找了。
原型链如下图:
Locale Dropdown

关于继承

每一个对象都会从原型继承属性,实际上继承是一个十分具有迷惑性的说法引用《你不知道的javascript》中的话就是:
继承意味着复制操作,然而javascript的默认并不会复制对象的属性,相反javascript只是在两个对象之间创建了一个关联,这样一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫其继承,委托的说法范围更准确些。

prototype、constructor、__proto__总览图

Locale Dropdown