陌上寒

陌上寒个人博客

前端面试必考题目(二)js原型和原型链

这是关于javascript原型的第七次讨论

昨天

昨天我们开始讨论了关于js原型的面试题目,原型是javascript的基础,javascript通过原型链可以实现继承,因为有了继承我们可以通过构造函数,构建出可以复用的实例对象。

五条原型原则

  1. 所有的引用类型(数组,对象,函数)都具有对象特性,即可自由扩展属性(除了null以外)
  2. 所有的引用类型(数组,对象,函数),都有一个proto属性,属性值是一个普通的对象    (隐式原型)
  3. 所有的函数,都有一个prototype属性,属性值也是一个普通的对象
  4. 所有的引用类型(数组,对象,函数),proto属性值值向他构造函数的prototype属性值
  5. 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的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__我们知道
五条原型原则的第四条

  1. 所有的引用类型(数组,对象,函数),proto属性值值向他构造函数的prototype属性值

所以我们得出
foo.__proto__===A.prototype
因为已知条件A.prototype.a = 1;
最后在原型链上找到了属性a
看图
《前端面试必考题目(二)js原型和原型链》
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也不会继续查找下去
所以这个例子很经典,贴个图,感受一下
《前端面试必考题目(二)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
然后就出现这样的情况,自有属性和原型属性都存在,这时候取值当然就是从自有属性中取啦
来个图感受一下
《前端面试必考题目(二)js原型和原型链》
所以
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

看图似乎更加直观
《前端面试必考题目(二)js原型和原型链》
F是个构造函数,而f是构造函数F的一个实例。
到instanceof发力的时候了

console.log(F instanceof Object);
console.log(F instanceof Function);

所以我们得出结论
F是Object 和 Function两个的实例,执行代码

console.log(F.prototype);

看输出
《前端面试必考题目(二)js原型和原型链》
即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面试题
整理了一下前端跳槽面试必备技巧
《前端面试必考题目(二)js原型和原型链》
公众号后台回复 前端面试 即可领取