学习本文内容内容前,你需要了解js中对象,原型链,call(),bind(),apply(),arguments的用法。
new操作符的原理
在调用 new 的过程中做了四件事:
- 新生成对象:new关键字会首先创建一个空对象
- 链接到原型:将这个空对象的原型对象指向构造函数的原型属性,从而继承原型上的方法
- 绑定this:将this指向这个空对象,执行构造函数中的代码,以获取私有属性
- 返回新对象:如果构造函数返回了一个对象res,就将该返回值res返回,如果返回值不是对象,就将创建的对象返回
理解了new的原理,我们就可以手动实现一个new的创建了。
//创建一个 _new
function _new() {
//定义空对象
/*
* arguments获取调用_new时所传入的实参
* 它并不是一个真正的数组
* 我们约定,调用_new时,所传入的第一个元素是构造器
*/
const obj = {};
// 取出参数列表的第一个参数(构造函数)
/*
* shift作用:
* 1.取出数组的第一个元素(构造器)
* 2.同时它会修改数组
*/
const fn = [].shift.call(arguments);
//手动指正obj的构造函数为 fn(链接原型)
obj.__proto__ = fn.prototype;
//调用fn,改变this为obj,传入剩余参数arguments(此时arguments是取出第一个元素后的部分)
const res = fn.apply(obj, arguments);
//考虑到fn函数中有return的原因,需要对res进行判断
// return typeof res === 'object' || typeof res === 'function' ? res : obj;
return res instanceof Object ? res : obj;
}
案例
//构造函数
function Person(name, sex) {
this.name = name;
this.sex = sex
}
Person.prototype.getName = function() {
return this.name
}
const person1 = new Person('hugh', '男');
const person2 = _new(Person, 'hugh', '男');
console.log(person1) //Person {name: 'hugh', sex: '男'}
console.log(person2)//Person {name: 'hugh', sex: '男'}
//共享原型上的方法
console.log(person1.getName === person2.getName); //true
注解:
1.关于[].shift.call(arguments)
因为 arguments
是类数组对象,虽然有length属性,但是没有 shift
方法,故通过 call
方法改变执行上下文调用 shift
方法
2.关于obj.proto = fn.prototype
原型链使用, __proto__
是每个对象拥有的隐藏属性,指向它构造函数的原型对象(prototype
), prototype
是每个构造函数(Fuction.prototype
除外)拥有的属性,它是函数所独有的,它是从一个函数指向一个对象(构造函数的原型对象)。
若是对上面的shift
,call
和原型链不是特别熟悉,我们再看看下面一个。
//不携带参数
function new1() {
// 获取构造函数和传递的参数
const [fn, ...args] = [...arguments];
//1.通过Object.create()方法创建一个构造函数实例原型引用的一个对象
const obj = Object.create(fn.prototype);
//2.将构造函数中的this指向obj,执行构造函数代码,获取返回值
const res = fn.apply(obj, args);
//3.判断返回值类型
return res instanceof Object ? res : obj;
}
//携带参数
function new2(fn, ...args) {
//1.创建一个空对象,并将对象的__proto__指向构造函数的prototype 这里我两步一起做了
const obj = Object.create(fn.prototype);
//2.将构造函数中的this指向obj,执行构造函数代码,获取返回值
const res = fn.apply(obj, args);
//3.判断返回值类型
return res instanceof Object ? res : obj;
}