前端面试题详解整理14|手写一个new(),原型链 Object.create(null) {} 区别说说this指向手写两个有序数组的并集

149 阅读5分钟

2023-12-28 17:21已编辑门头沟学院 计算机类

关注

快手前端一面

50min
大部分问的基础
很多手写题
1 手写一个new()

实现一个简单的 new() 函数的过程是创建一个对象并将其与指定的构造函数连接起来,具体步骤如下:

  1. 创建一个新的空对象。
  2. 将这个新对象的 __proto__ 属性链接到构造函数的原型对象(即构造函数的 prototype 属性)。
  3. 将构造函数的 this 绑定到新创建的对象。
  4. 执行构造函数,并将参数传递给构造函数。
  5. 如果构造函数返回一个对象,则返回该对象;否则,返回新创建的对象。

以下是一个简单的实现:

function myNew(constructor, ...args) {
    // 创建一个新的空对象
    let obj = {};

    // 将新对象的 __proto__ 属性链接到构造函数的原型对象
    obj.__proto__ = constructor.prototype;

    // 执行构造函数,并将 this 绑定到新对象
    let result = constructor.apply(obj, args);

    // 如果构造函数返回一个对象,则返回该对象;否则,返回新创建的对象
    return result instanceof Object ? result : obj;
}

使用这个 myNew() 函数可以像使用 new 关键字一样来创建对象,例如:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

let person = myNew(Person, 'John', 30);
console.log(person); // 输出 { name: 'John', age: 30 }

这样就实现了一个简单的 new() 函数。值得注意的是,这个实现并未考虑到 ES6 中的类、SymbolProxy 等特性,仅适用于普通的构造函数。

2 原型链 Object.create(null)   {}  区别  结合题目说原型指向
原型链是 JavaScript 中一种对象之间的关系,通过原型链,一个对象可以从另一个对象继承属性和方法。在 JavaScript 中,对象可以通过 __proto__ 属性来访问其原型,通过原型链向上查找属性和方法。

现在来解释 Object.create(null){} 的区别以及与原型链的关系:

  1. Object.create(null):

    • Object.create(null) 创建了一个全新的对象,并将其原型设置为 null。这意味着这个对象没有原型链,不会继承任何属性或方法,也不具备任何原生方法(如 toString()hasOwnProperty() 等)。
    • 这种方式创建的对象通常被称为“纯净对象”或“字典对象”,它们非常适合用作存储键值对而不需要继承任何属性或方法的情况。
  2. {}(空对象字面量):

    • {} 是 JavaScript 中创建对象的常用方式之一,它会创建一个空对象,并将其原型链接到 Object.prototype 上。因此,这个对象会继承 Object.prototype 上的属性和方法,如 toString()hasOwnProperty() 等。
    • 这种方式创建的对象属于一般的 JavaScript 对象,会继承一些原生方法和属性。
  3. 原型指向:

    • 对象的原型通过 __proto__ 属性指向其原型对象,可以通过原型链向上查找属性和方法。
    • 对于 {} 创建的对象,默认情况下其原型链会指向 Object.prototype,因此可以继承 Object.prototype 上的方法。
    • 而对于 Object.create(null) 创建的对象,其原型链上没有任何对象,因此不会继承任何属性或方法。

综上所述,Object.create(null) 创建的对象是一个纯净的、没有原型链的对象,而 {} 创建的对象会继承 Object.prototype 上的属性和方法。这两者在原型链和继承上存在明显的区别。

3 说说this指向
在 JavaScript 中,this 是一个关键字,用于指向当前执行上下文中的对象。this 的指向在不同的情况下会有所不同,主要取决于函数的调用方式。下面是 this 的几种常见指向情况:

  1. 全局作用域中的 this

    • 在全局作用域中,this 指向全局对象(浏览器环境中为 window,Node.js 环境中为 global)。
    • 例如:
      console.log(this); // 输出 window(浏览器环境)
      
  2. 函数中的 this

    • 在普通函数中,this 的指向取决于函数的调用方式。如果函数是作为对象的方法被调用,this 就指向该对象;否则,this 指向全局对象(浏览器环境中)或 undefined(严格模式下)。
    • 例如:
      function greet() {
        console.log(this.name);
      }
      
      const obj = { name: 'John', greet: greet };
      obj.greet(); // 输出 John
      
      const func = obj.greet;
      func(); // 输出 undefined 或者报错(严格模式)
      
  3. 箭头函数中的 this

    • 箭头函数的 this 是词法作用域,即箭头函数的 this 继承自外部作用域的 this,并且在定义时确定,不会被函数调用方式改变。
    • 例如:
      const obj = {
        name: 'John',
        greet: function() {
          const innerFunc = () => {
            console.log(this.name);
          };
          innerFunc();
        }
      };
      
      obj.greet(); // 输出 John
      
  4. 构造函数中的 this

    • 在构造函数中,this 指向通过 new 关键字创建的新对象。
    • 例如:
      function Person(name) {
        this.name = name;
      }
      
      const person = new Person('Alice');
      console.log(person.name); // 输出 Alice
      
  5. DOM 事件处理函数中的 this

    • 在 DOM 事件处理函数中,this 通常指向触发事件的 DOM 元素。
    • 例如:
      <button id="myButton">Click me</button>
      <script>
        document.getElementById('myButton').addEventListener('click', function() {
          console.log(this); // 输出点击的按钮元素
        });
      </script>
      

总的来说,this 的指向取决于函数的调用方式以及函数的类型,在不同的情况下可能会指向不同的对象。理解 this 的指向是 JavaScript 开发中十分重要的一部分。

4 事件循环机制题目
5 nexttick题目 (面试官提示了差点答错了)
6 项目优化
7 手写防抖
8 手写两个有序数组的并集
下面是手写的两个有序数组的并集的 JavaScript 函数:

function mergeSortedArrays(arr1, arr2) {
    let mergedArray = [];
    let i = 0; // 用于遍历 arr1 的指针
    let j = 0; // 用于遍历 arr2 的指针
    
    // 遍历两个数组,将较小的元素依次添加到并集中
    while (i < arr1.length && j < arr2.length) {
        if (arr1[i] < arr2[j]) {
            mergedArray.push(arr1[i]);
            i++;
        } else if (arr1[i] > arr2[j]) {
            mergedArray.push(arr2[j]);
            j++;
        } else { // 如果两个元素相等,只添加一个到并集中
            mergedArray.push(arr1[i]);
            i++;
            j++;
        }
    }
    
    // 将剩余未遍历完的数组添加到并集中
    while (i < arr1.length) {
        mergedArray.push(arr1[i]);
        i++;
    }
    while (j < arr2.length) {
        mergedArray.push(arr2[j]);
        j++;
    }
    
    return mergedArray;
}

// 示例
const arr1 = [1, 3, 5, 7, 9];
const arr2 = [2, 4, 6, 8, 10];
console.log(mergeSortedArrays(arr1, arr2)); // 输出 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

这个函数会遍历两个有序数组 arr1arr2,并将它们合并成一个新的有序数组 mergedArray,最后返回这个新的数组作为两个数组的并集。

反问
技术栈和业务用的react和原生js,说不会卡vue,但是感觉有点寄
更新
早上11点面下午5点光速挂