new操作符具体做了什么?
function Foo() {
}
console.log(new Foo());
结果如下:
可见我们创建了一个空的对象。
function Foo() {
}
console.log(Foo.prototype == new Foo().__proto__);
结果为
可见空对象的原型,指向于构造函数的原型。
function Foo() {
console.log(this);
this.name = '张三'
}
console.log(Foo());
结果为
可见this指向的是window,this下是没有name的,所以输出的是undefined.
现在我们new一下
function Foo() {
console.log(this);
this.name = '张三'
}
console.log(new Foo());
由此可见,将空对象作为构造函数的上下文(改变this的指向)
function Foo() {
this.name = '张三'
return 11
}
console.log(new Foo());
function Foo() {
this.name = '张三'
return true
}
console.log(new Foo());
当返回值为数值或者true的时候,结果均如下。
function Foo() {
this.name = '张三'
return {}
}
console.log(new Foo());
当返回值为一个对象的时候,这时的结果是
function Foo() {
this.name = '张三'
return [1, 2, 3]
}
console.log(new Foo());
结果:
所以说在它内部里面肯定是做了一个判断的。如果这个构造函数返回的是基本类型,则忽略这个返回值,如果返回值是引用类型,则new 就无效了。这就是对构造函数有返回值的判断。
总结
- 创建了一个空对象
- 将空对象的原型,指向于构造函数的原型
- 将空对象作为构造函数的上下文(改变this的指向)
- 对构造函数有返回值的处理判断
按照上面的四个步骤如何自己去实现呢?
function Fun(age) {
this.age = age;
}
console.log(new Fun(18));
封装一个方法,同样达到上面的效果。
//封装一个方法去实现new的功能
function Fun(age, name) {
this.age = age;
this.name = name;
}
function create(fn, ...args) {
// 1.创建一个空对象
var obj = {}; // var obj=Object.create({})
// 2. 将空对象的原型,指向于构造函数的原型
Object.setPrototypeOf(obj, fn.prototype)
//Object.setPrototypeOf(),为现有对象设置原型,返回一个新对象
//接收两个参数:第一个是现有对象,第二是原型对象
//3. 将空对象作为构造函数的上下文(改变this的指向)
var result = fn.apply(obj, args)
// 4. 对构造函数有返回值的处理判断
return result instanceof Object ? result : obj;
}
console.log(create(Fun, 18, '张三'));
最终的结果
我们修改返回值
function Fun(age, name) {
this.age = age;
this.name = name;
return 1111;
}
结果没有发生任何改变。
function Fun(age, name) {
this.age = age;
this.name = name;
return {a:1};
}
可以看出上述确实达到了new的效果。