[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 操作符理解
来源: http://www.jianshu.com/p/55fa58c1a75c