new 操作符在执行过程中会改变 this 的指向,所以我们在了解 new 操作符之前,我们先解释一下 this 的用法。
构造函数中 this 的用法
1 | function Person (name, age) { |
输出结果中包含了 name 和 age 的信息。
事实上,我们并没有通过 return 返回任何值,为什么输出信息中会包含 name 和 age 属性呢? 其中起作用的就是 this 这个关键字。
我们通过以下代码输出 this ,看看 this 具体的内容。
1 | function Person (name, age) { |
我们发现 this 的实际值为 Person 空对象,后两句话就相当于给 Person 对象添加 name 和 age 属性,结果真的是这样吗?我们改写一下 Person 函数。
1 | function Person (name, age) { |
输出结果中并未包含 name 和 age 属性。
因为在Javascript中,如果构造函数没有 return 值,则默认 return this。而上面代码中,this 实际是一个 Person 空对象,name 和 age 属性只是被添加到了临时变量 Person 中,为了能让输出结果包含 name 和 age 属性,我们将临时变量 Person 进行 return 就可以了。
1 | function Person (name, age) { |
换句话说,new Person 做的就是类似的事情。
1 | function Person(name, age) { |
通过以上分析,我们了解了构造函数中 this 的用法。
this 与 new 操作符的关系
通过 new 操作符生成一个Person 对象的实例。
1 | let Person = new Person() |
new 操作符做了 3 件事情。
1 | 1.let person = {} |
我们自定义一个类似 new 功能的函数,来具体讲解上上面的 3 行代码。
1 | function Person (name, age) { |
返回的结果中也包含 name 和 age 属性,这就证明了 new 运算符对 this 指向的改变。Person.apply(obj, arguments) 调用后Person对象中的 this 就指向了 obj 对象,这样 obj 对象就具有了 name 和 age 属性。
但是,不仅关注 new 操作符的函数本身,也要关注它的原型属性。
prototype 属性与 new 的关系
我们在 Person 的原型上添加一个 sayHi 函数,然后通过New()返回的对象去调用 sayHi 函数。
1 | function Person (name, age) { |
我们发现执行报错了,New 函数返回的对象并没有调用sayHi方法。
这是因为 sayHi 方法是属于 Person 原型的函数,只有 Person 原型链上的对象才能继承 sayHi 方法。那么我们该怎么做呢?
这里用到的就是__proto__属性,实例的__proto__属性指向创建实例的函数的原型。设置 obj 对象的__proto__值为 Person 对象的 prototype 属性,那么 obj 对象就继承了 Person 原型上的 sayHi 方法。
1 | function Person (name, age) { |
结果输出 ‘Hi’, 方法调用成功。