js中原型的一些总结

152 阅读3分钟

1. 什么是原型、原型链?

想要理解原型最重要的就是理解,实例,构造函数原型和构造函数之间的关系

  • 只要创建函数,该函数就拥有了prototype属性
  • 默认情况下,所有的原型对象都会自动获得一个constructor属性,指回与之关联的构造函数
  • 每次调用一个构造函数,会创建一个新实例,实例内部的[[prototype]]指针就会被赋值为构造函数的原型对象。

function Person() {
}

let zhangsan = new Person()
// 对象实例通过内部的浏览器实现的__proto__属性指向构造函数的原型
console.log(zhangsan.__proto__ === Person.prototype);

// 构造函数的原型对象的constructor重新指回了Person 构造函数
console.log(Person.prototype.constructor === Person);

只要是对象就有对应的构造函数的原型 ,是构造函数就会有原型属性,特殊情况如下

  • prototype属性也是一个对象,所以其也有__proto__属性指向Object.prototype
  • 因为函数也是一个对象,所以它含有__proto__和prototype两种属性,Person.__proto__指向Function.prototype, 同时对于一些内置的函数,Date等,都是指向了Function.prototype
  • 对于Function,同时也是一个对象,所以有__proto__属性,Function.__proto__指向Function.prototype
// 构造函数的__proto__指向Function的原型
console.log(Person.__proto__ === Function.prototype); // true

// 普通构造函数原型对象的__proto__指向Object的原型
console.log(Person.prototype.__proto__ === Object.prototype); // true

// 对象的原型对象的__proto__指向null,是原型链的最顶端
console.log(Object.prototype.__proto__ === null); // true

// Function的__proto__指向Function.prototype,这里,Function被当做普通函数,也是指向Function.prototpye
console.log(Function.__proto__ === Function.prototype); // true

// 内置构造函数的__proto__指向Function.prototype
console.log(Date.__proto__ === Function.prototype); // true

构造函数创建的两个实例共享一个原型对象

function Person() {}
let p1 = new Person()
let p2 = new Person()

console.log(p1.__proto__ === p2.__proto__); // true

instanceof检查对象的有原型链中是否有指定的构造函数的原型

function Person() {}
let p1 = new Person()

console.log(p1 instanceof Person); // ture
console.log(p1 instanceof Object); // ture
console.log(Person.prototype instanceof Object);// ture

确定对象关系的prototype的isPrototypeOf方法

console.log(Person.prototype.isPrototypeOf(p1)); // true
console.log(Object.prototype.isPrototypeOf(Person.prototype)); // true

对象的获取原型的方法,

// Object.getPrototypeOf 等同于__proto__效果
console.log(Object.getPrototypeOf(p1) === Person.prototype);  // true

原型链

构造函数、原型和实例之间的关系是:每个构造函数都有一个原型对象,原型对象有一个属性constructor指回构造函数,实例有一个内部[[prototype]]属性指向原型对象。如果原型对象是另外一个构造函数的实例,也就是原型对象本身又有一个内部指针指向两一个原型对象,相应的,每个原型都有一个指针指向另一个构造函数,这样实例和原型之间就形成了一条原型链。

function Father() {
  this.property = 'father'
}

Father.prototype.getFatherValue = function() {
  return this.property
}

function Son() {
  this.sonProperty = 'son'
}

// 重写子构造函数的原型对象
// 把子构造函数的原型对象作为父构造函数的实例对象,这样就在父子之间形成了原型链,
// Son能够访问到父对象的方法
Son.prototype = new Father()

Son.prototype.getSonValue = function() {
  return this.sonProperty
}

let son = new Son()
console.log(son.getFatherValue());  // father

原型链扩展了原型搜索机制,当读取实例上的属性时,首先会在实例上搜索该属性,如果没有,则会搜索实例的原型,通过原型链实现继承之后,就可以接着搜索原型的原型。所以调用getFatherValue,经过了三个步骤,son本身、通过son.__proto__连接到的Son.prototype、 通过Son.prototype.__proto__连接到的Father.prototype. 对于属性和方法的搜索会一直持续到原型链的末端。

参考文献:《javascript高级程序设计》(第4版)