只需一点基础直面面试题之`new`过程发生了什么

131 阅读3分钟

前言

相信不少小伙伴在面试的时候都遇到了面试官在一开始问使用new关键字创建一个对象时的过程,接下来,我将用通俗易懂的语言解释new过程:

逐步解释

1.创建一个新对象:

let obj = {};

创建一个新的空对象。

2.设置原型:

obj.__proto__ = Constructor.prototype;

将新对象的原型设置为构造函数的prototype属性。这使得新对象可以继承构造函数原型上的方法和属性。

什么是prototype属性

每个JavaScript函数都有一个prototype属性,这个属性是一个对象,用来定义将由该构造函数创建的实例对象共享的属性和方法。当使用new关键字创建一个新对象时,这个新对象会自动连接到构造函数的prototype对象,从而继承其属性和方法。

举个例子

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

// 向Person的prototype属性添加一个方法
Person.prototype.greet = function() {
    console.log('Hello, my name is ' + this.name);
};

// 创建一个新的Person实例
let person1 = new Person('Alice', 30);

// 通过原型链访问greet方法
person1.greet(); // 输出: Hello, my name is Alice

在这个例子中,Person是一个构造函数,用来创建新的对象实例,我们在Person.prototype上定义了一个方法greet,创建一个新对象person1,将person1__proto__属性指向Person.prototype,使得person1继承了Person.prototype上的方法。因为person1的原型是Person.prototype,所以person1可以访问并调用greet方法。

3.调用构造函数,并传递参数:

Constructor.call(obj, ...args);

使用 call 方法调用构造函数,并将新创建的对象 obj 作为 this 绑定给构造函数。...args 是展开运算符,将传入的参数数组展开为独立的参数传递给构造函数。这样,构造函数内部的代码就会对新对象进行初始化。

Constructor构造函数

Constructor 通常是指一个构造函数。构造函数是一种特殊的函数,用于创建并初始化对象。 Constructor 在上下文中的意义 在下面代码中,Constructor 是一个通用的变量名,表示任何构造函数。

function myNew(Constructor, ...args) {
    // 1. 创建一个新对象
    let obj = {};
    // 2. 将新对象的原型指向构造函数的 prototype 属性
    obj.__proto__ = Constructor.prototype;
    // 3. 调用构造函数,并将新对象绑定到构造函数的 this 上,同时传递参数
    Constructor.call(obj, ...args);
    // 4. 返回新对象
    return obj;
}

let person1 = myNew(Person, 'Alice', 30);
console.log(person1.name); // 输出: Alice
console.log(person1.age);  // 输出: 30

在这个函数 myNew 中:

  • Constructor 是一个占位符,可以是任何构造函数(如 Person)。
  • ...args 是构造函数的参数列表,可以包含任意数量的参数。

通过调用 Constructor.call(obj, ...args);,我们在新创建的对象 obj 上执行构造函数,并将 this 绑定到 obj 上,同时传递参数 args。这个过程模拟了 new 关键字的行为。

...args

...args 是 JavaScript 中的展开运算符(spread operator),用于将一个可迭代对象(如数组或字符串)展开成多个独立的参数。在函数调用时,展开运算符可以将一个数组展开成函数的多个独立参数。 示例:

function logAllArguments(...args) {
    console.log(args);
}

logAllArguments(1, 2, 3); // 输出: [1, 2, 3]
logAllArguments('a', 'b', 'c'); // 输出: ['a', 'b', 'c']

在这个例子中,函数 logAllArguments 可以接收任意数量的参数,并将它们作为数组处理。这就是 ...args 的强大之处,使得函数更为灵活和易于扩展。

4.返回新对象:

return obj;

返回新创建并初始化的对象。

完整实例代码

function myNew(Constructor, ...args) {
    // 1. 创建一个新对象
    let obj = {};
    // 2. 将新对象的原型指向构造函数的 prototype 属性
    obj.__proto__ = Constructor.prototype;
    // 3. 调用构造函数,并将新对象绑定到构造函数的 this 上,同时传递参数
    Constructor.call(obj, ...args);
    // 4. 返回新对象
    return obj;
}

let person1 = myNew(Person, 'Tang', 22);
console.log(person1.name); // 输出: Tang
console.log(person1.age);  // 输出: 22
console.log(person1 instanceof Person); // 输出: true