这是我参与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的显式原型的隐式原型对象为
null,Object.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)
可以看到p1实例身上除了我们代码中声明的name和sayHello属性外还有一个
[[Prototype]](隐式原型)属性,且隐式原型上有country和sayHello属性(这是我们在Person.prototype上声明的,印证了我们的重点概念的第一条),然后我们再点开p1的隐式原型的隐式原型。
可以看到,p1的隐式原型的隐式原型就是Object的显式原型,因为p1的隐式原型就是一个对象(Object构造函数的实例),所以
p1.__proto__.__proto__ === Object.prototype,这再次印证了我们概念的第一条。而后,Object显式原型的隐式原型就是null了,这印证了我们概念的最后一条。
有一点需要注意,显式原型中有一个constructor属性,这个属性会指回构造函数,即
Person.prototype.constructor === Person
总结画图
还是放上这张经典图吧,结合着我们的重点概念,相信大家应该能理清整个环路了,有不清楚的,可以评论一起讨论。
回到面试题
这一行代码,改变了构造函数的显式原型指向
A.prototype = {
b: 2,
c: 3,
};
代码执行完后就是这么一个局势,答案也就显而易见了