new的使用
思考
先思考几个问题:
- 构造函数中this上添加的属性和方法,为什么实例有?
- 实例为什么可以访问原型对象上的属性和方法?
- new返回不同类型的值对实例有什么影响?
猜想
- 创建一个对象obj, 并将其原型指向构造函数fn的原型对象(原型链接)
- this指向obj, 执行构造函数fn
- 如果fn无返回值或返回基本类型,则返回obj,否则返回fn的返回值res
猜想的几点和思考部分是对应的,猜想1解决思考中问题1——原型链接;猜想2和3解决思考中的问题2(this指向obj,fn中对this的操作,不就是对obj的操作么?至于是否返回obj还得看fn返回值的类型);猜想3解决思考中问题3。具体参见以下代码注释。
验证
// 全局变量, 保存Stu中的this
const global = {}
function Stu(name, age) {
console.log('enter', this);
global._this = this; // 将this保存在global下,观察global._this === jack
this.name = name;
this.age = age;
console.log('leave', this);
// 以下代码验证猜想3
// 返回基本类型——忽视
// return name;
// 返回引用类型——实例就是这个返回值, 此时实例没有链接到原型对象,无法使用原型对象上的showInfo方法
// return { name, age };
}
Stu.prototype.showInfo = function() {
console.log(`I am ${this.name}, and I am ${this.age}`);
}
// JS关键字 new
const jack = new Stu('jack', 20);
console.log(jack);
console.log(jack.__proto__ === Stu.prototype); // 验证猜想1
console.log(jack.showInfo()); // 验证猜想1
console.log(global._this === jack); // 验证猜想2
new的实现
function myNew(fn, ...args) {
// fn不是函数,抛出异常
if (typeof fn !== 'function') {
const info = `expect myNew's first param to be a function but got ${typeof fn}`;
throw new Error(info);
}
// 原理1:原型链接
const obj = Object.create(fn.prototype);
// 原理2:修改this指向,执行构造函数
const res = fn.apply(obj, args);
// 原理3:根据fn的返回值类型返回
return res instanceof Object ? res : obj;
}
验证代码是否正确,只需修改const jack = new Stu('jack', 20)为const jack = myNew(Stu, 'jack', 20)。修改Stu的返回值,发现myNew的表现和new一样。
new原理总结
- 创建一个对象obj, 并将其原型指向构造函数fn的原型对象(原型链接)
- this指向obj, 执行构造函数fn
- 如果fn无返回值或返回基本类型,则返回obj,否则返回fn的返回值res