首先要知道为什么使用new
我们都知道new是将构造函数变为实例对象 ,如果不使用new操作符会怎么样?
首先看一段有没new操作符代码
// 创建一个构造函数
function Foo(name, age){
this.name = name;
this.age = age;
}
// 添加方法
Foo.prototype.sayhi = function (){ // 切记不要用箭头函数 否则this指向全局作用域
console.log(this.name);
}
// 直接调用构造函数
const foo = Foo('哈哈',123);
console.log(foo); // undefined
console.log(window.name); // '哈哈'
在看一段使用new操作符代码
// 创建一个构造函数
function Foo(name, age){
this.name = name;
this.age = age;
}
// 添加方法
Foo.prototype.sayhi = function (){ // 切记不要用箭头函数 否则this指向全局作用域
console.log(this.name);
}
// 直接调用构造函数
const foo = new Foo('哈哈',123); // 使用new操作符
console.log(foo); //Foo {name: '哈哈', age: 123, __proto__:{sayhi: ƒ ()}}
foo.sayhi(); // '哈哈'
console.log(foo.name); // '哈哈'
new操作符 和不用new操作符区别是什么?
有new操作符 | 无new操作符 |
---|---|
foo 实例对象 | foo undefined |
this 指向 foo | this 指向 window |
new操作符到底做了什么?
1.把函数变为一个对象
2.foo原型链和构造函数Foo原型对象绑定
3.this指向foo
根据上面我们来实现一个new操作符
// const xxx = _new(Foo,'哈哈',123) ==> new Foo('哈哈',123)
function _new(fn){
// 1 获取除fn以外的所有arguments
// 使用slice删除arguments第一个元素就得到其他arguments
const args = Array.prototype.slice.call(arguments,1); // ['哈哈',123]
// 新建一个对象 用于函数变对象
const newObj = {};
// 原型链被赋值为原型对象
newObj.__proto__ = fn.prototype;
// this 指向新对象
fn.apply(newObj, args);
// 返回这个新对象
return newObj;
}
这段代码比较难理解的是 fn.apply(newObj, args);
正常的理解是fn这个方法被执行了 将args赋值给this中的属性
其实还可以换个角度思考一下这段代码
function Foo(){
this.name = '咿呀';
this.age = 123;
}
const obj = {};
Foo.apply(obj);
// Foo 执行后
// Foo中的this属性被绑定在obj对象上
console.log(obj) // {name: '咿呀', age: 123}
new到底做了什么?专业描述如下:
1.在内存中创建一个新对象
2.这个新对象内部的[[prototype]]特性被赋值为构造函数的prototype属性
3.构造函数内部的this被赋值为这个新对象(即this指向新对象)
4.执行构造函数内部的代码(给新对象添加属性)
- 如果构造函数返回非空 对象,则返回该对象;否则,返回刚创建的新对象。
最后还差一点没有说,上代码
function Foo(name){
this.name = name;
return {
height:180
}
}
const foo = new Foo('哈哈');
// 如果构造函数返回非空 对象,则返回该对象;
console.log(foo); // {height: 180}
再对代码做一下完善并升级
function _new(fn,...args){
const newObj = Object.create(fn.prototype);
const value = fn.apply(newObj,args);
// 如果函数返回 非空并是对象 返回 value 否则 返回 newObj
return value instanceof Object ? value : newObj;
}
重点: 使用new就不要在构造函数中 return
重点: 使用new就不要在构造函数中 return
重点: 使用new就不要在构造函数中 return