原型和原型链

一.prototype(function)

在 js 中,每个函数都有一个 prototype 属性,

这个属性指向函数的原型对象===调用构造函数时创建的实例的原型

// 1.未调用前
function Dw(age) {
  this.age = age
}
console.dir(Dw)
/*
    {
      name: "Dw"
      prototype: {constructor: ƒ} // f Dw(age)
    }
*/
// 2.开始调用
function Dw(age) {
  this.age = age
}
console.dir(Dw)
/*
  {
    name: "Dw"
    prototype: {name: "dxw", constructor: ƒ} // f Dw(age)
  }
*/
Dw.prototype.name = 'dxw'
var d1 = new Dw()
console.log(d1)
/*
  {
    age: undefined,
    __proto__:{
      name: "dxw"
      constructor: ƒ Dw(age)
    }
  }
*/

二.__proto__(obj)

每个对象(除 null)都会有的属性,叫做__proto__,这个属性指向该对象的原型。

console.log(d1.__proto__ === Dw.prototype) // true

三.constructor

每个原型都有一个 constructor 属性,指向关联的构造函数

console.log(Dw.prototype.constructor === Dw) // true  // 函数prototype
console.log(d1.__proto__.constructor === Dw) // true // 对象__proto__

// 顺便学习一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(d1) === Dw.prototype) // true;
console.log(Object.getPrototypeOf(d1) === d1.__proto__) // true;

补充说明:

function Dw() {}
var d1 = new Dw()

console.log(d1.constructor)

console.log(d1.constructor === Dw) // true
d1.constructor === Dw.prototype.constructor // true

获取实例 d1 的 constructor 时,其实是没有 constructor 属性的,当不能读取到 constructor 属性时,会从 d1 的原型也就是 Dw.prototype 中读取,正好原型中有该属性,所以


实例的 constructor 找不到就去原型找 实例的原型===构造函数的 prototype 构造函数的 prototype 中有 constructor 属性指向构造函数 构造函数的原型的 constructor 指向构造函数

四.实例与原型

function Dw() {}
Dw.prototype.name = 'dxw'
var d1 = new Dw()
d1.name = 'big'
console.log(d1.name) // big

delete d1.name
console.log(d1.name) // dxw

delete Dw.prototype.name
console.log(d1.name) // undefined

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


上述实例:创建了一个 d1 实例,并给 d1 赋值 name,打印 name 值是 big;删除了 d1 的 name,再次打印,找不到 name 会从 d1 的原型 d1.__proto__也就是 Dw.prototype 上找 name,找到了是 dxw;如果没有找到呢?原型的原型又是什么?

五.原型的原型

原型也是一个对象,既然是对象,就用原始的创建对象方式创建它

var obj = new Object()
obj.name = 'dxw'
console.log(obj.name) // 'dxw'

综合之前,实例的__proto__指向构造函数的 prototype

六.原型链

简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript 高级程序设计》

其实简单来说,就是上述四-五的过程。

继上述五中所说,那 Object.prototype 的原型呢?

console.log(Object.prototype.__proto__ === null) // true

引用阮一峰老师的 《undefined 与 null 的区别》 就是:

null 表示“没有对象”,即该处不应该有值。

所以 Object.prototype.__proto__ 的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思。

所以查找属性的时候查到 Object.prototype 就可以停止查找了。