new
做了那些事?new
返回不同的类型时会有什么表现?- 手写 new 的实现过程
new 关键词的
主要作用就是执行一个构造函数、返回一个实例对象
,在 new 的过程中,根据构造函数的情况,来确定是否可以接受参数的传递。下面我们通过一段代码来看一个简单的 new 的例子
function Person(){
this.name = 'Jack';
}
var p = new Person();
console.log(p.name) // Jack
这段代码比较容易理解,从输出结果可以看出,p 是一个通过 Person 这个构造函数生成的一个实例对象。
new
操作符可以帮助我们构建出一个实例,并且绑定上 this,内部执行步骤可大概分为以下几步:
- 创建一个新对象
- 对象连接到构造函数原型上,并绑定
this
(this 指向新对象) - 执行构造函数代码(为这个新对象添加属性)
- 返回新对象
在第四步返回新对象这边有一个情况会例外:
那么问题来了,如果不用
new
这个关键词,结合上面的代码改造一下,去掉new
,会发生什么样的变化呢?我们再来看下面这段代码
function Person(){
this.name = 'Jack';
}
var p = Person();
console.log(p) // undefined
console.log(name) // Jack
console.log(p.name) // 'name' of undefined
- 从上面的代码中可以看到,我们没有使用
new
这个关键词,返回的结果就是undefined
。其中由于JavaScript
代码在默认情况下this
的指向是window
,那么name
的输出结果就为Jack
,这是一种不存在new
关键词的情况。 - 那么当构造函数中有
return
一个对象的操作,结果又会是什么样子呢?我们再来看一段在上面的基础上改造过的代码。
function Person(){
this.name = 'Jack';
return {age: 18}
}
var p = new Person();
console.log(p) // {age: 18}
console.log(p.name) // undefined
console.log(p.age) // 18
通过这段代码又可以看出,当构造函数最后
return
出来的是一个和this
无关的对象时,new 命令会直接返回这个新对象
,而不是通过 new 执行步骤生成的 this 对象
但是这里要求构造函数必须是返回一个对象,如果返回的不是对象,那么还是会按照 new 的实现步骤,返回新生成的对象
。接下来还是在上面这段代码的基础之上稍微改动一下
function Person(){
this.name = 'Jack';
return 'tom';
}
var p = new Person();
console.log(p) // {name: 'Jack'}
console.log(p.name) // Jack
可以看出,当构造函数中 return
的不是一个对象时,那么它还是会根据 new
关键词的执行逻辑,生成一个新的对象(绑定了最新 this
),最后返回出来
因此我们总结一下:
new 关键词执行之后总是会返回一个对象,要么是实例对象,要么是 return 语句指定的对象
手工实现New的过程
function create(fn, ...args) {
if(typeof fn !== 'function') {
throw 'fn must be a function';
}
// 1、用new Object() 的方式新建了一个对象obj
// var obj = new Object()
// 2、给该对象的__proto__赋值为fn.prototype,即设置原型链
// obj.__proto__ = fn.prototype
// 1、2步骤合并
// 创建一个空对象,且这个空对象继承构造函数的 prototype 属性
// 即实现 obj.__proto__ === constructor.prototype
var obj = Object.create(fn.prototype);
// 3、执行fn,并将obj作为内部this。使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
var res = fn.apply(obj, args);
// 4、如果fn有返回值,则将其作为new操作返回内容,否则返回obj
return res instanceof Object ? res : obj;
};
- 使用
Object.create
将obj 的
proto指向为构造函数的原型
; - 使用
apply
方法,将构造函数内的this
指向为obj
; - 在
create
返回时,使用三目运算符决定返回结果。
我们知道,构造函数如果有显式返回值,且返回值为对象类型,那么构造函数返回结果不再是目标实例
如下代码:
function Person(name) {
this.name = name
return {1: 1}
}
const person = new Person(Person, 'lucas')
console.log(person)
// {1: 1}
测试
//使用create代替new
function Person() {...}
// 使用内置函数new
var person = new Person(1,2)
// 使用手写的new,即create
var person = create(Person, 1,2)
new 被调用后大致做了哪几件事情
- 让实例可以访问到私有属性;
- 让实例可以访问构造函数原型(
constructor.prototype
)所在原型链上的属性; - 构造函数返回的最后结果是引用数据类型。