引言
在JS 中,new
操作符是一个非常重要的特性,它允许我们创建对象并调用构造函数来初始化这些对象。在面试中,手写new
是一个非常常见的考点。本文将深入探讨new
操作符的工作原理,并通过手写实现一个类似new
操作符功能的函数。
new操作符的作用
在JS中,使用new
操作符可以创建一个新的对象,并将这个对象的原型链链接到构造函数的原型对象上。同时,它还会执行构造函数,并将构造函数中的this
绑定到新创建的对象上。这样,我们就可以通过构造函数来初始化新创建的对象的属性和方法。
例如,以下是使用new
操作符创建一个Person
对象的示例,来帮助我们理解new
到底都干了些什么事:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayName=function(){
console.log(this.name);
}
// new 实例化运算符
// 1. 创建一个空对象 {} 和 Person 没有血缘关系
// {} __proto__ Object.prototype
// 2. 手动的__proto__ 指向 Person.prototype
// 3. 构造函数 this 指向 {} 执行,给{}赋值
const zzz=new Person('zzz',20);
在这个例子中,我们定义了一个构造函数Person
,它接受两个参数name
和age
,并在构造函数内部将这两个参数赋值给新创建的对象的属性。同时,我们还在Person
的原型对象上定义了一个方法sayName
,这个方法可以打印出对象的name
属性。最后,我们使用new
操作符创建了一个Person
对象,并调用了它的sayName
方法。
new操作符的工作原理
了解了new
操作符的作用后,我们来深入探讨一下它的工作原理。当我们使用new
操作符时,JS引擎会执行以下几个步骤:
- 创建一个空对象 {}
- 将这个新对象的原型链链接到构造函数的原型对象上
- 执行构造函数,并将构造函数中的this 绑定到新创建的对象上
- 如果构造函数返回一个非原始值(即对象或函数),则返回这个值。否则,返回新创建的对象
手写new
下面我们来手写一个new
:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
console.log(this.name);
}
// 不设置形参 使用arguments 获取参数
function objectFactory() {
// arguments 所有参数 类数组
// console.log(arguments,arguments.length);
const obj = new Object(); // 1. 空对象创建
// arguments 类数组 没有shift 方法 借给 arguments shift方法
// [].shift.call(arguments) 将类数组 转为数组 使用 shift
const Constructor = [].shift.call(arguments)
// console.log(Constructor);
// 2. 手动的__proto__ 指向 Person.prototype
obj.__proto__ = Constructor.prototype;
// 3. this 指向 apply 第二项是数组
const result = Constructor.apply(obj,arguments);
// console.log(obj);
// 4. 如果构造函数返回的是一个对象,则返回这个对象;否则返回新创建的对象
return result instanceof Object ? result : obj;
}
// 传参为函数名和各项值
let awei = objectFactory(Person,'zzz',20)
console.log(zzz.name); // zzz
zzz.sayName(); // zzz
在这个实现中,我们首先创建了一个空对象obj
,然后将这个对象的原型链链接到构造函数的原型对象上。接着,我们使用apply
方法执行构造函数,并将构造函数中的this
绑定到新创建的对象上。最后,返回对象obj
。
步骤分析
在上面的方法中使用到的一些方法,我会在下面做一下解释:
- arguments:这是一个伪数组(类数组对象), 它包含了传递给函数的所有参数。
arguments
只能在函数体内访问 - [].shift.call(arguments):
shift()
定义在Array.prototype
上,只能由数组使用。arguments
是伪数组,没有shift()
方法。我们通过一个空数组[ ]
来调用shift()
,并使用call()
绑定到arguments
上来让伪数组使用shift()
方法。将函数名赋给Constructor
- obj.__proto__ = Constructor.prototype:将这个新对象的原型链链接到构造函数的原型对象上,保证实现
new
的功能 - Constructor.apply(obj,arguments):使用
apply
确定函数Constructor
的this
指向创建的新对象obj
,将剩下的arguments
作为参数一起传给函数Constructor
。这里绑定this
使用的是apply
,apply
可以将所有参数用数组的方式一起传送,call
只能一个一个传值。 - 最后,如果构造函数返回的是一个对象,则返回这个对象;否则返回新创建的对象
至此,一个简单的手写new
就完成了!!!
总结
通过深入解析new
操作符的机制并手写实现一个简易版本。手写new
作为面试中一道常考题,希望这篇文章能够帮助到你。