第八章 对象(原型链)

260 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

前言

在js里,继承机制是原型继承。继承的起点是对象的原型(Object prototype)...
一切皆为对象,只要是对象,就会有proto属性,该属性存储了指向其构造的指针。

原型链⼜是什么呢?

我们都知道当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还 查不到,就去找原型的原型,⼀直找到最顶层为⽌。

举个例子:

const person = function(){};
const father = function(){};

person.eat = '米饭';
father.prototype = person;
const son = new father();

console.log(father.eat)  // undefined
console.log(son.eat) // 米饭

为什么 father.price === undefined

  • 原型链是依赖于 __proto__,而不是prototype
  • 首先我们先看这句话const father = function(){} ;这个语句实际上就是 var father = new Function(); 由此father.__proto__ === Function.prototype是成立的;而Function.prototype并没有eat属性,所以输出的情况是 undefined;但是father.prototype.eat === '米饭'

image.png

为什么 son.eat === 米饭

  • 还是那句话原型链是依赖于 __proto__,而不是prototype
  • 我们在代码中father.prototype = person曾有过这样的赋值操作;还有这一步操作:const son = new father(); 大家可能很懵逼,这两个有什么关系呢?别急,我们看一下下面这个,简单版new操作符你就懂了
function new(obj, ...arg){ 
    let newObj = {} // 创建一个空对象 
    newObj.__proto__ = obj.prototype 
    let res = obj.apply(new, arg) 
    return res instanceof Object ? res : new; 
}
  • 我们在new的过程中,会将prototype复制给创建的__proto__,而我们又有了如下的操作father.prototype = person,那么 son__proto__ == father.prototype == person,所以当son上没有eat属性就会从__proto__上寻找方法,找到 person有eat属性,返回。

这样⼀条通过proto和 prototype 去连接的对象的链条,就是原型链

怎么样使father.eat希望不等于undefined?

就加一句:

Function.prototype.eat = '干饭'

那么:

console.log(father.eat)  // 输出 干饭

外部理解链接 最详尽的 JS 原型与原型链终极详解,没有「可能是」

由于原型链涉及构造函数、函数Function、引用类型Object及特定的两个属性prototype和_proto_,因此在谈原型链前先搞清楚他们之间的关系;

  1. 函数必然有prototype和_proto_两个属性,所有的函数(包括自定义函数)都是Function实例的对象;

clipboard.png

  1. 对象必然有_proto_属性,但不一定有prototype;实例的对象通过_proto_属性连接到构造函数的prototype属性上。而原型链就是从这两者的关系开始一层一层往下找的关系;
  2. JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象。 假设,我们目前有一个对象 person1; person1有一个 proto 属性,创建它的构造函数是 Person,且Person构造函数的原型对象是 Person.prototype ,那么我们就可以大胆的下一个这样的结论person1.__proto__ === Person.prototype
function person() {}
const person1 = new person()
那么 =>
person1.__proto__ === person.prototype // true
person.prototype.constructor=== person // true
Object.getPrototypeOf(person1) === person.prototype // true

image.png

小小的延伸一下

因为绝大部分浏览器都支持__proto__属性,所以它才被加入了 ES6 里(ES5 部分浏览器也支持,但还不是标准)

总结一下:

prototype.png 每一个对象数据类型(普通的对象、实例、prototype......)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。