JS 常见面试题 2

1.JS 如何实现类型判断

在 JavaScript 中,你可以使用不同的方式来进行类型判断或检查变量的类型。以下是一些常见的方法:

  1. typeof 操作符: 使用 typeof 操作符可以检查一个变量的类型。它返回一个字符串,表示变量的数据类型。

    typeof 5; // "number"
    typeof "Hello"; // "string"
    typeof true; // "boolean"
    typeof undefined; // "undefined"
    typeof null; // "object"(特殊情况,实际上应为 "object")
    typeof {}; // "object"
    typeof []; // "object"
    typeof function () {}; // "function"
    

    注意:typeof null 返回 "object" 是历史遗留问题,不是一个准确的类型检查方式。

  2. instanceof 操作符:instanceof 操作符用于检查一个对象是否是指定构造函数的实例。它用于检查对象的原型链。

    const arr = [];
    arr instanceof Array; // true
    

    注意:instanceof 只能用于检查对象是否是某个构造函数的实例,不能用于原始数据类型的检查。

  3. Object.prototype.toString.call() 方法: 使用 Object.prototype.toString.call() 方法可以更准确地检查对象的类型。

    Object.prototype.toString.call(5); // "[object Number]"
    Object.prototype.toString.call("Hello"); // "[object String]"
    Object.prototype.toString.call(true); // "[object Boolean]"
    Object.prototype.toString.call(undefined); // "[object Undefined]"
    Object.prototype.toString.call(null); // "[object Null]"
    Object.prototype.toString.call({}); // "[object Object]"
    Object.prototype.toString.call([]); // "[object Array]"
    Object.prototype.toString.call(function () {}); // "[object Function]"
    

    这种方法返回一个以 [object 类型] 形式表示的字符串,可以用来准确检查变量的类型。

  4. typeof 与 instanceof 的限制:typeofinstanceof 在某些情况下可能不够准确,尤其是对于复杂的数据类型(如数组和自定义对象)。因此,通常最好使用 Object.prototype.toString.call() 来进行更精确的类型检查。

总的来说,根据具体的使用场景,你可以选择不同的方法来进行类型检查。通常,typeof 用于检查原始数据类型,instanceof 用于检查对象的构造函数,而 Object.prototype.toString.call() 用于更精确地检查对象的类型。

2. js 实现继承

在 JavaScript 中,可以使用不同的方式来实现继承。以下是一些常见的继承方式:

  1. 原型链继承: 这是 JavaScript 中最基本的继承方式。通过将子类的原型对象设置为父类的实例,子类可以继承父类的属性和方法。

    function Parent() {
      this.name = "Parent";
    }
    
    Parent.prototype.sayHello = function () {
      console.log("Hello from " + this.name);
    };
    
    function Child() {
      this.name = "Child";
    }
    
    Child.prototype = new Parent();
    
    const childInstance = new Child();
    childInstance.sayHello(); // "Hello from Child"
    

    注意:原型链继承有一些缺点,例如共享父类的属性,不能传递参数给父类构造函数等。 这个是通过原型链继承的第一个问题:原型中存在引用类型的时候,原型中包含的引用值会在所有实例共享。第二个问题是创建子类实例对象的时候,无法向父类构造函数传参

  2. 构造函数继承(借用构造函数): 在子类构造函数内调用父类构造函数,从而继承父类的属性。

    function Parent(name) {
      this.name = name;
    }
    
    function Child(name) {
      Parent.call(this, name);
    }
    
    const childInstance = new Child("Child");
    console.log(childInstance.name); // "Child"
    

    这种方式解决了原型链继承的一些问题,但父类的方法仍然无法被子类继承。

  3. 组合继承(原型链继承 + 构造函数继承): 这是一种常用的继承方式,结合了原型链继承和构造函数继承的优点。

    function Parent(name) {
      this.name = name;
    }
    
    Parent.prototype.sayHello = function () {
      console.log("Hello from " + this.name);
    };
    
    function Child(name) {
      Parent.call(this, name);
    }
    
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    
    const childInstance = new Child("Child");
    childInstance.sayHello(); // "Hello from Child"
    

    这种方式既可以继承父类的属性,也可以继承父类的方法。

  4. ES6 类继承: ES6 引入了 classextends 关键字,更方便地实现继承。

    class Parent {
      constructor(name) {
        this.name = name;
      }
    
      sayHello() {
        console.log("Hello from " + this.name);
      }
    }
    
    class Child extends Parent {
      constructor(name) {
        super(name);
      }
    }
    
    const childInstance = new Child("Child");
    childInstance.sayHello(); // "Hello from Child"
    

    ES6 类继承更符合传统面向对象编程的思维方式。

选择合适的继承方式取决于你的需求和项目的架构。不同的继承方式适用于不同的情况,需要根据具体情况来决定使用哪种方式。

Contributors: masecho