【面试篇】几道面试题带你深入理解JavaScript面向对象

2,325 阅读6分钟

前言

过去的两篇文章我们介绍了JavaScript的面向对象编程,今天的这篇文章我们来通过几道面试题深入理解一下JavaScript的面向对象编程。

题目一:实现new操作符

function Dog(name{
    this.name = name;
}
Dog.prototype.bark = function ({
    console.log('wangwang');
}
Dog.prototype.sayName = function ({
    console.log('my name is ' + this.name);
}
/*
let sanmao = new Dog('三毛');
sanmao.sayName();
sanmao.bark();
*/

//=>基于内置的new关键词,我们可以创建Dog的一个实例sanmao,实例可以调取原型上的属性和方法,现在的需求是:自己实现一个_new方法,也能模拟出内置new后的结果
function _new({
    //=>完成你的代码

}
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true

解答

function _new(Func, ...args{
    //=>完成你的代码
    let obj = Object.create(Func.prototype);
    let result = Func.call(obj, ...args);
    return result !== null && (typeof result === "object" || typeof result === "function") ? result : obj;
}

题目二:基于内置类原型扩展实现数组去重

解答

首先说一些数组去重的方法:

  • for循环

    function myDeduplication() {
      let obj = {}; 
      for(let i = 0;i <= this.length;i++) {
          let item = this[i];
          if(obj[item] !== undefined) {
              this[i] = this[this.length - 1];
              this.length--;
              i--;
              continue;
          }
          obj[item] = item;
      }
      return this;
    }
  • set方法

    function myDeduplication(){
      return Array.from(new Set(this));
    }
  • 拓展运算符

    function myDeduplication(this){
      return [...new Set(this)];
    }

    接下来,我们将myDeduplication方法拓展到Array上,并使用

Array.prototype.myDeduplication = myDeduplication;

[1, 2, 88, 3, 4, 6, 4, 8, 10, 88].myDeduplication();

题目三:实现(4).minus(3).add(4) => 5

解答

看一眼题目,这个题目是让我们实现基于Number扩展实现链式加减操作,下面开始实现:

Number.prototype.add = function(num{
    if(typeof num !== 'number') {
        throw new Error(`num's type is not a number`);
    }
    return this + num;
}

Number.prototype.minus = function(num{
    if(typeof num !== 'number') {
        throw new Error(`num's type is not a number`);
    }
    return this - num;
}

console.log((4).minus(3).add(4));       // 5
console.log((4).minus('3').add(4))      // "num's type is not a number"

题目四:下面的代码片段输出什么?

解答

function Foo({
    getName = function ({
        console.log(1);
    };
    return this;
}
Foo.getName = function ({
    console.log(2);
};
Foo.prototype.getName = function ({
    console.log(3);
};
var getName = function ({
    console.log(4);
};
function getName({
    console.log(5);
}
Foo.getName();                 // 2
getName();                     // 4
Foo().getName();               // 1
getName();                     // 1
new Foo.getName();             // 2
new Foo().getName();           // 3
new new Foo().getName();       // 3

本题的首先定义了Foo函数,然后在Foo上创建了名为getName的静态属性创建了一个函数,之后在Foo的原型上创建了getName匿名函数,然后又通过函数变量表达式创建了一个getName的函数,最后声明一个名为getName的函数。

第一问:直接访问Foo函数上的getName静态属性,所以直接输出2

第二问:我们可以把上述代码拆分一下

var getName;

function getName({
    // ...
}

getName = function({
    // ...
}

所以输出了1

第三问:先执行了Foo函数,后调用Foo函数返回值对象的getName属性函数,所以输出1

第四问:在第三问中,我们注意到,Foo函数返回值中的getName属性是一句函数赋值语句,所以原有的getName已被改变,这个时候输出的结果是1

第五问:.运算符优先级高于new,所以此问相当于new (Foo.getName)(),此时输出2

第六问:()的优先级高于new,此问相当于(new Foo()).getName(),此时输出结果为3

第七问:根据运算优先级,此问改写为new ((new Foo()).getName)(),最后输出结果为3

题目五:下面的代码片段输出什么?

解答

let obj = {
    23,
    34,
    length2,
    pushArray.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj); 

这是一个类数组,其中push方法的内部实现为:

Array.prototype.push = function(target{
    this[this.length] = target;
    this.length++;
}

根据push方法的原理,最终输出了这样的结果。

题目六:a如何继承b?

解答

  • 利用原型链继承

    function A({
      // ...
    }

    function B({
      // ...
    }

    A.prototype = new B();
  • 构造函数实现

    function B({
      // ...
    }

    function A({
      B.call(this)
    }
  • 原型链+构造函数

    function A({
      // ...
    }

    function B({
      // ...
    }

    A.prototype =  new B();
    A.prototype.constructor = A;
  • Object.create()[推荐此方法]

    function A({
      // ...
    }

    function B({
      // ...
    }

    A.prototype =  Object.create(B.prototype);
    A.prototype.constructor = A;
  • class方法

    class B {
      constructor() {
          // ...
      }
    }

    class A extends B {
      constructor() {
          super()
      }
    }

推荐阅读

总结

上面的几道有关于面向对象的题,是我近期遇到的,然后将其整理分享给大家~如果大家有其他的好的题,也希望能够在评论区分享给我,分享给大家~如果上面整理的有问题,也希望大家在评论区指点出来,我们共同进步~

最后,分享一下我的个人公众号「web前端日记」,欢迎大家积极关注~