在红皮书《JavaScript高级程序设计》中有这么一段话描述了new操作符:
要创建一个对象的新实例,必须使用new操作符,以这种方式调用构造函数实际上会经历以下4个步骤:
1、创建一个新对象;
2、将构造函数的作用域赋给新对象(因此this就指向了这个对象);
3、执行构造函数中的代码(为这个新对象添加属性);
4、如果构造函数返回对象,则返回该对象;否则,返回刚创建的新对象(空对象)。
由于对象的构造函数体的不同,也分为多种情况:
- 无返回值
function Children(name, age) {
this.name = name;
this.age = age;
}
console.log(new Children('xiaoming', 3)); //{name: 'xiaoming', age: 3}
- 返回值是一个对象
function Children(age) {
this.age = age;
return {name: 'xiaohong', age: 4};
}
console.log(new Children(3)) // {name: 'xiaohong', age: 4}
- 返回值是非对象
function Children(name, age){
this.name = name;
this.age = age;
return "奥特曼";
}
console.log(new Children('小白', 5)) // {name: '小白', age: 5}
- 返回值是非对象且没有属性绑定
function Children(name, age) {
return "怪兽";
}
console.log(new Children('xiaoqiang', 6)); // {}
从上面几种情况可以看出,只有构造函数的return返回的是一个对象类型时,才会改变结果。
众所周知,只有function类型的数据才会被实例化,class也不例外,但是 Generator 函数和箭头函数却不能,
function* gen() {
yield 'hello';
yield 'world';
return 'ending';
}
const arr = () => {
console.log(arr);
}
function common(age, name){
this.age = age;
this.name = name;
}
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
console.log(typeof gen); // fuinction
console.log(typeof arr); // fuinction
console.log(typeof common); // fuinction
console.log(typeof Point); // fuinction
箭头函数是因为没有this指向而无法实例化new,而Generator 函数返回的总是遍历器对象,而不是this对象,所以在手写自己的new函数时要把这两种函数排至在外。
以下是完整代码
function _new(context, ...args) {
// 判断构造函数的类型合法
if(typeof context !== 'function') {
throw new Error('parameter type error!')
}
// 针对箭头函数
if(!context.hasOwnProperty('prototype')) {
throw new Error(`${context.name} is not a constructor`)
}
// 针对generator函数
if(context.constructor.name === 'GeneratorFunction'){
throw new Error(`${context.name} is not a constructor`)
}
// 创建一个空对象
let obj = {};
// 将构造函数的作用域赋给新对象
obj.__proto_ = context.prototype;
// 执行构造函数中的代码
const res = context.apply(obj, args);
// 如果有返回值且返回值是对象,则将它作为返回值,否则返回之前创建的新对象
const isObject = typeof res === 'object' && res !== null;
const isFunction = typeof res === 'function';
return isObject || isFunction ? res : obj;
}