前端面试必考题目(二)js原型和原型链
这是关于javascript原型的第七次讨论
昨天
昨天我们开始讨论了关于js原型的面试题目,原型是javascript的基础,javascript通过原型链可以实现继承,因为有了继承我们可以通过构造函数,构建出可以复用的实例对象。
五条原型原则
- 所有的引用类型(数组,对象,函数)都具有对象特性,即可自由扩展属性(除了null以外)
- 所有的引用类型(数组,对象,函数),都有一个proto属性,属性值是一个普通的对象 (隐式原型)
- 所有的函数,都有一个prototype属性,属性值也是一个普通的对象
- 所有的引用类型(数组,对象,函数),proto属性值值向他构造函数的prototype属性值
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的proto(即它的构造函数的prototype)中寻找
今天
关于javascript原型的面试题,一共就那么几种,很多公司,没有太多的精力自己去出题,都是拿来主义,充其量就是做一些修改,然而换汤不换药,万变不离其宗,我们今天继续拆解
第一题
function A() {}
function B(a) {
this.a = a;
}
function C(a) {
if (a) {
this.a = a;
}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);
console.log(new B().a);
console.log(new C(2).a);
代码仔细品味,分别输出什么?
别往下滑了,考验自己的时候到了
我们一起拆解一下
//新建一个构造函数A
function A() {}
//新建一个构造函数B,并添加一个自有属性a,属性a的值取决于传入的参数
function B(a) {
this.a = a;
}
//新建一个构造函数C,如果有参数,则添加自有属性a,属性a的值为传入的参数值,
//如果没有传入参数,则构造函数C没有自有属性
function C(a) {
if (a) {
this.a = a;
}
}
//在A的原型对象上添加一个属性a,属性a的值是1,(两个同理)
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
求A的实例下属性a的值即new A().a
构造函数function A(){},是没有自有属性的,没有怎么办?顺着原型链查找,我们找到构造函数A的原型对象A.prototype,
因为A.prototype.a = 1;
所以console.log(new A().a);输出1
还可以这么拆解
var foo = new A()
console.log(foo.a)
同样的构造函数A下面找不到属性a,那就得顺着原型链查找,即foo._proto_我们知道
五条原型原则的第四条
- 所有的引用类型(数组,对象,函数),proto属性值值向他构造函数的prototype属性值
所以我们得出
foo._proto_===A.prototype
因为已知条件A.prototype.a = 1;
最后在原型链上找到了属性a
看图
console.log(new A().a)//1
如果这个理解了,那么下面的两个自然也就引刃而解
求B的实例下属性a的值即new B().a
我们知道B实例下是有属性a的,的值等于传入的参数,因为创建B实例的时候没有传入参数所以最后输出==>undefined
注意
这里有个地方要说明一下
由于构造函数B存在属性a,但是由于没有传入参数,导致属性a的值是undefined,
后来又一次执行了 B.prototype.a = 1;在B的原型上添加了属性a,并赋值为1,当查找属性或方法是,先查找自身,自身没有,再去原型链上的,如果在自身找到了,就会停止,
这个例子中,我们找到了a但是a的值是undefined,undefined也是值,找到了就停止,尽管原型链上还有值,js也不会继续查找下去
所以这个例子很经典,贴个图,感受一下
console.log(new B().a);//undefined
求C的实例下属性a的值即new C(2).a
如果👆两题都懂了,这个就是小case
实例化一个构造函数C并传入参数2,因为有参数,所以,构造函数C存在自有属性a,并且属性a的值为2
C.prototype.a = 1;
在C的原型对象上添加一个属性a,并且赋值1
然后就出现这样的情况,自有属性和原型属性都存在,这时候取值当然就是从自有属性中取啦
来个图感受一下
所以
console.log(new C(2).a);//2
第二题
在第二题开始之前我们很有必要先复习一下昨天的一个知识点?为什么说很有必要呢?因为这是一把🔑,它是解开这道题目的关键
instanceof
instanceof用于判断一个变量是否某个对象的实例
var F = function() {};
Object.prototype.a = function() {
console.log("a()");
};
Function.prototype.b = function() {
console.log("b()");
};
var f = new F();
F.a();//a()
F.b();//b()
f.a();//a()
f.b();//Uncaught TypeError: f.b is not a function
image
F是个构造函数,而f是构造函数F的一个实例。
到instanceof发力的时候了
console.log(F instanceof Object);
console.log(F instanceof Function);
所以我们得出结论
F是Object 和 Function两个的实例,执行代码
console.log(F.prototype);
image
即F既能访问到a,也能访问到b。
所以F.a() 输出 a() F.b() 输出 b()
继续
console.log(f instanceof Object);//true
console.log(f instanceof F);//true
console.log(f instanceof Function);//false
f并不是Function的实例,因为它本来就不是构造函数,所以就不能调用Function原型链上的相关属性和方法了,只能访问Object原型链。
所以f.a() 输出 a()
而f.b()就报错了。
接下来,我们具体分析下,它们是如何按路径查找的:
- f.a()的查找路径: f自身: 没有 ---> f._proto_(Object.prototype): 输出a()
- f.b()的查找路径: f自身: 没有 ---> f._proto_(Object.prototype): 没有 ---> f._proto_._proto_ (Object.prototype._proto_): 因为找不到,所以报错
- F.a()的查找路径: F自身: 没有 ---> F._proto_(Function.prototype): 没有 ---> F._proto_._proto_(Object.prototype): 输出 a()
- F.b()的查找路径: F自身: 没有 ---> F._proto_(Function.prototype): b()
end
参考链接
京东JS面试题
整理了一下前端跳槽面试必备技巧
公众号后台回复 前端面试 即可领取
陌上寒个人博客.png