共计 1992 个字符,预计需要花费 5 分钟才能阅读完成。
[toc]
分析:new 做了什么
new
操作符通过执行自定义构造函数或者 js 内置构造函数,从而生成一个实例对象。
mdn 上把内部操作大概分为 4 步:
- 创建一个空的简单 JavaScript 对象(即{});
- 链接该对象(即设置该对象的构造函数)到另一个对象;
- 将步骤 1 新创建的对象作为 this 的上下文;
- 如果该函数没有返回对象,则返回 this。
通过一个简单的 demo 感受下上面的步骤
function Person (name){this.name = name}
let p = new Person('jack');
console.log(`p:`, p); // {name: 'jack'}
console.log(`p.__proto__===Person.prototype:`,p.__proto__===Person.prototype); //true
可以看到 new 操作符执行 Person 构造函数后,返回了一个内部创建的新对象,并且以这个对象为上线文环境执行了一遍 Person 函数 ,最后将其返回,同时对象 p 的原型属性指向构造函数的原型, 这样也就保证了实例能够访问在构造函数原型中定义的属性和方法。
上面的 demo 中构造函数是没有返回值的,如果说构造函数有返回值呢,如下
function Person (name){
this.name = name;
return {age: 18}
}
let p = new Person('jack');
console.log(`p:`, p); // {age: 18}
如果构造函数最后返回了一个对象,就会直接将其返回,而不是内部创建的新对象。
经过测试发现,除了返回对象,如果返回其他类型,只要最后返回的类型为引用类型 object
或者 function
(Function
,Object
,Array
,Date
,Error
,Regexp
, 要排除null
,因为typeof null === 'object'
) 就会直接将其返回,而其他基本类型都会返回内部新创建的对象。
自定义实现
这里我们尝试通过封装一个 myNew
方法模拟 new 操作符的主要功能:接受若干参数,第一个参数为构造函数ctr
,其余为构造器所需参数,myNew(ctr, arg1, arg2,...)
第一步
这里的第一步把 mdn 中的 1、2 步放在了一起:创建一个新对象,并将其 __proto__
属性指向构造函数的 prototype
属性
function myNew(ctr) {let obj = Object.create(ctr.prototype);
}
也可以使用如下方法
function myNew (ctr){let obj = {};
obj.__proto__ = ctr.prototype;
}
第二步
获取到参数之后,以内部新创建的对象 obj
为上线文执行构造函数,作用是为 obj
赋值
function myNew(ctr) {let obj = Object.create(ctr.prototype);
const args = [].slice.call(arguments, 1);
let result = ctr.apply(obj, args);
console.log(`obj:`,obj);
}
上面的 const args = [].slice.call(arguments, 1);
用于将 arguments
类数组转为数组并获取参数,也可以通过 Array.form(arguments).slice(1)
或者 [...arguments].slice(1)
实现。
第三步
对执行构造函数后的返回值 result
做兼容处理。
如果构造函数最终返回对象、函数、数组、日期等其他引用类型及 Symbol,会将其直接返回,其他基本类型及 null
、undefined
会返回内部新创建的对象实例。
function myNew(ctr) {let obj = Object.create(ctr.prototype);
const args = [].slice.call(arguments, 1);
let result = ctr.apply(obj, args);
var isObj = (typeof result === 'object' && result !== null);
var isFn = typeof result === 'function';
return (isObj || isFn) ? result : obj;
}
测试
最后,简单测试一下
没有返回值
function Person(name) {this.name = name;}
let p = myNew(Person,'jack');
console.log(`p:`,p);
有返回值
function Person(name) {
this.name = name;
return {age: 33}
}
let p = myNew(Person,'jack');
console.log(`p:`,p);
参考
面试官问:能否模拟实现 JS 的 new 操作符
手动实现一个 new 操作符理解