一. new运算符调用构造函数经历了4个步骤
- 创建一个空对象
- 将构造函数的作用域赋给新对象(修改this指向)
- 执行构造函数
- 如果构造函数没有显式返回对象,那么返回创建的新对象。
二. 假如构造函数显式的添加返回值会怎样?
- 显式返回值不是对象的情况
function Person(name){
this.name = name;
return 'hello world';
}
let p = new Person('tom');
console.log(p);// Person { name: 'tom' }
正常的返回了预料之中的结果,也就是忽略了显式的返回值。
- 显式返回值值是一个对象的情况
function Person(name){
this.name = name;
return {};
}
let p = new Person('tom');
console.log(p);// {}
这里说的对象并不是单单指{}
这种对象,而是指对象类型,数组[]
也是一样的。
function Person(name){
this.name = name;
return [1,2];
}
let p = new Person('tom');
console.log(p);// [ 1, 2 ]
得到的并不是新的Person实例,而是显式返回的空对象。
- 经过进一步的测试发现:
构造函数返回值为所有基本数据类型时(Boolean、Number、Null、String、Symbol、Undefined),返回的都是创建的对象
构造函数返回值为函数时,返回函数
function Person(name){
this.name = name;
return function a(){
};
}
let p = new Person('tom');
console.log(p);// [Function: a]
构造函数返回值为其他对象类型时(Array等),返回该对象
三. 如何模拟一个new操作符(new Foo())
- new创建的对象是继承自
Foo.prototype
的 - 需要修改this指向
- 判断Foo有没有返回值,或者说返回值是什么类型的。
- 如果返回值类型为基本数据类型,返回创建的对象。如果返回值为
Function
,返回这个函数。如果返回值为其他对象类型,返回该对象。
使用Object.create()修改原型关系
let new2 = function(fn,...args){
// 使用Object.create()实现继承
let obj = Object.create(fn.prototype);
// 执行构造函数,并修改this
let res = fn.apply(obj,args);
// 判断返回值
if(typeof fn() !== 'object' && typeof fn() !== 'function'){
return obj;
}else{
return res;
}
}
直接通过修改__proto__链接到原型,不太优雅
let new2 = function(fn,...args){
// 链接到原型
let obj = {};
obj.__proto__ = fn.prototype;
// 执行构造函数,并修改this
let res = fn.apply(obj,args);
// 判断返回值
if(typeof fn() !== 'object' && typeof fn() !== 'function'){
return obj;
}else{
return res;
}
}
测试
function Person(name,age){
this.name = name;
this.age = age;
}
let p = new2(Person,'tom',21);
console.log(p);// Person { name: 'tom', age: 21 }
console.log(p.constructor);// [Function: Person]
console.log(p instanceof Person);// true