# JavaScript中的new操作符详解
# 1. new操作符的作用
new操作符是JavaScript中用于创建对象实例的关键字,特别是在面向对象编程中创建类的实例时使用。
# 2. new操作符的执行过程
当我们使用new调用一个函数(构造函数)时,会发生以下步骤:
- 创建一个全新的空对象
instance(instance = new Object()) - 设置该对象的原型链,将对象的
__proto__属性指向构造函数的prototype属性(设置原型链instance.__proto__ = F.prototype) - 将构造函数中的
this绑定到新创建的对象instance上 - 执行构造函数中的代码(为新对象添加属性和方法)
- 如果构造函数没有显式返回对象,则自动返回新创建的对象;如果显式返回对象,则返回该对象
const newOperator = (ctor, ...rest) => {
// 基于obj的原型创建一个新的对象
const newObj = Object.create(ctor.prototype);
// 添加属性到新创建的newObj上, 并获取obj函数执行的结果.
const result = ctor.apply(newObj, rest);
// 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
return typeof result === 'object' ? result : newObj;
};
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 3. 构造函数与原型链
在理解new操作符之前,我们需要先了解构造函数和原型链的概念。
构造函数是一种特殊的函数,用于创建特定类型的对象。在JavaScript中,任何函数都可以作为构造函数使用,只要通过new关键字调用它。
每个函数都有一个prototype属性,它指向一个对象,这个对象包含了可以被该函数的所有实例共享的属性和方法。
当使用new操作符调用构造函数时,新创建的对象会获得一个内部属性[[Prototype]](在大多数浏览器中暴露为__proto__),该属性指向构造函数的prototype对象。
// 构造函数示例
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在原型上添加方法
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
};
// 使用new创建实例
const person1 = new Person('Alice', 25);
const person2 = new Person('Bob', 30);
person1.sayHello(); // "Hello, my name is Alice"
person2.sayHello(); // "Hello, my name is Bob"
// 验证原型链
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4. 完整的模拟实现
/**
* 模拟实现 new 操作符
* @return { Object|Function|Regex|Date|Error } [返回结果]
*/
const newOperator = (ctor, ...rest) => {
if (typeof ctor !== 'function') {
throw 'newOperator function the first param must be a function';
}
// ES6 new.target 是指向构造函数
newOperator.target = ctor;
// 1.创建一个全新的对象,
// 2.并且执行[[Prototype]]链接
// 通过`new`创建的每个对象将最终被`[[Prototype]]`链接到这个函数的`prototype`对象上。
const newObj = Object.create(ctor.prototype);
// let newObj = new Object();
// newObj.__proto__ = ctor.prototype;
// 3.生成的新对象会绑定到函数调用的`this`。
// 获取到ctor函数返回结果
const ctorReturnResult = ctor.apply(newObj, rest);
// 4.中这些类型中合并起来只有Object和Function两种类型 typeof null 也是'object'所以要不等于null,排除null
const isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;
const isFunction = typeof ctorReturnResult === 'function';
if (isObject || isFunction) {
return ctorReturnResult;
}
// 5.如果函数没有返回对象类型`Object`(包含`Function`, `Array`, `Date`, `RegExg`, `Error`),那么`new`表达式中的函数调用会自动返回这个新的对象。
return newObj;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 5. new操作符的实际应用
new操作符在JavaScript开发中有着广泛的应用,特别是在面向对象编程中。
# 5.1 创建自定义对象实例
// 定义构造函数
function Car(brand, model, year) {
this.brand = brand;
this.model = model;
this.year = year;
this.getInfo = function() {
return `${this.year} ${this.brand} ${this.model}`;
};
}
// 使用new创建实例
const myCar = new Car('Toyota', 'Camry', 2022);
console.log(myCar.getInfo()); // "2022 Toyota Camry"
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 5.2 与ES6类的结合使用
虽然ES6引入了class语法,但其底层仍然使用构造函数和原型链:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
}
// 使用new创建实例
const person = new Person('Alice', 25);
person.greet(); // "Hello, I'm Alice"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 6. 注意事项和最佳实践
始终使用new调用构造函数:忘记使用new关键字会导致this指向全局对象,可能造成意外的全局变量污染。
构造函数命名规范:通常使用大写字母开头命名构造函数,以区别于普通函数。
避免在构造函数中返回对象:除非有特殊需求,否则不要在构造函数中显式返回对象,这会覆盖默认返回的新实例。
利用new.target检测调用方式:可以使用new.target来检测函数是否通过new调用:
// 在函数中区分是否使用new进行调用
function User (name) {
if (!new.target) {
// 如果没有使用new调用,抛出错误或进行处理
throw new Error('User must be called with new');
// 或者自动加上new
// return new User(name);
}
this.name = name;
}
// 不带 "new" 会抛出错误
// User('Alice'); // Error: User must be called with new
// 带 "new":
const user = new User('Alice'); // 正常执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 7. 参考资料
← Js中的call方法 Js原型链 →