从一道面试题来简单总结一下JS原型链相关知识。

270 阅读3分钟

这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

原型链是JS学习路上的一个拦路虎,是和闭包、执行栈一样的老大难问题。今天和大家分享一道原型链相关的面试题。据此再简单总结一下原型链的相关知识。希望能帮组大家粗浅的认识一下原型链和应付面试题。

面试题

const A = function () {};
A.prototype.a = 1;
const B = new A();
A.prototype = {
  b: 2,
  c: 3,
};
const C = new A();
A.prototype.d = 4;
console.log(B.a);
console.log(B.b);
console.log(C.c);
console.log(C.d);

原型链作用

JS是一门面向对象的语言,继承是面向对象的一大特征。但是JS(ES5)并没有很严格的继承机制。这时候JS中的原型链就起到了一个继承的作用。

一些基本概念的解释

  • 显式原型(prototype)
  • 隐式原型(__proto__) 后面的文章中的相应名词就用中文了,大家知悉

一个简单例子来看JS中的继承(ES5)

JS中的继承的原理就是子类的显示原型指向父类实例的隐式原型

const Person = function () {};
const Dog = function () {};
Person.prototype.sayGoodBye = () => {
    console.log("sayGoodBye");
};
const p = new Person();
// 这一步就相当于是继承了
Dog.prototype = p.__proto__; 
const d = new Dog();
// Dog类继承了Person类,这样狗狗也能sayGoodBye了
d.sayGoodBye();

重点概念讲解

先介绍一些重点概念(本文的重点)

  • 实例的隐式原型指向构造函数的显式原型Person.prototype === p1.__proto__
  • 构造函数身上有显式原型
  • 实例身上才有隐式原型
  • 显式原型是一个对象(Object实例),所有显式原型身上也有隐式原型
  • 构造函数是Function的类的实例,所以构造函数也有隐式原型
  • Object的显式原型的隐式原型对象为nullObject.prototype.__proto__ === null这就是原型链的尽头

原型与原型链

当在实例身上找属性时,会先从自己身上找,如果自己身上没有的话,会到顺着原型链条找,一直找到原型链的末尾结束。如果找到了就不会再往上找了。如果一直到末尾都没找到就显示undefined。我们还是上代码吧。

  const Person = function (name) {
    this.name = name;
    this.sayHello = () => {
      console.log(`大家好,我是${this.name},我来自${this.country}`);
    }
  };
  Person.prototype.sayHello = () => {
    console.log("hello");
  };
  Person.prototype.country = '中国';
  const p1 = new Person('德比利');
  p1.sayHello(); // 控制台输出:大家好,我是德比利,我来自中国
  console.log(p1.age); // undefined
  const p2 = new Person('dbl');
  p2.sayHello(); // 大家可以想一下这里输出什么
      

现在我们看一下控制台的结构 console.dir(p1) image.png 可以看到p1实例身上除了我们代码中声明的name和sayHello属性外还有一个[[Prototype]](隐式原型)属性,且隐式原型上有countrysayHello属性(这是我们在Person.prototype上声明的,印证了我们的重点概念的第一条),然后我们再点开p1的隐式原型的隐式原型。

image.png 可以看到,p1的隐式原型的隐式原型就是Object的显式原型,因为p1的隐式原型就是一个对象(Object构造函数的实例),所以p1.__proto__.__proto__ === Object.prototype,这再次印证了我们概念的第一条。而后,Object显式原型的隐式原型就是null了,这印证了我们概念的最后一条。

有一点需要注意,显式原型中有一个constructor属性,这个属性会指回构造函数,即 Person.prototype.constructor === Person

总结画图

还是放上这张经典图吧,结合着我们的重点概念,相信大家应该能理清整个环路了,有不清楚的,可以评论一起讨论。 image.png

回到面试题

这一行代码,改变了构造函数的显式原型指向

A.prototype = {
  b: 2,
  c: 3,
};

代码执行完后就是这么一个局势,答案也就显而易见了 image.png