new做了什么
先看一个例子:
function Test(name) {
this.name = name
}
Test.prototype.sayName = function () {
console.log(this.name)
}
const t = new Test('hello')
console.log(t.name) // 'hello'
t.sayName() // 'hello'
new通过构造函数test创建出来的实例t,可以访问到构造函数中的属性name- 创建出来的实例,可以访问到构造函数原型链中的属性
sayName
关于原型链,我们再看个例子
function Test(name) {
this.name = name
}
Test.prototype.sayName = function () {
console.log(this.name)
}
const t = new Test('hello')
console.log(t)
var obj = new Object();
console.log(obj)
看下结果:
新生成的对象下面有一层prototype,它的constructor是Test函数,test函数再套了一层prototype,constructor是Object,这是js自带的。
所以这也印证了上面第二点,创建的实例对象拥有构造函数的prototype
可以看下这张图理解函数的原型关系
给构造函数添加一个返回值
function Test(name) {
this.name = name;
return 1;
}
Test.prototype.sayName = function () {
console.log(this.name)
}
const t = new Test('hello')
console.log(t.name) // 'hello'
t.sayName() // 'hello'
在控制台执行上述代码,发现return并没起到任何作用
改成return { a: 1};
function Test(name) {
this.name = name;
return { a: 1};
}
Test.prototype.sayName = function () {
console.log(this.name)
}
const t = new Test('hello')
console.log(t) // { a: 1}
t.sayName() // 报错
可以看到改成返回对象后,实例t变成了返回的对象
总结:构造函数尽量不要设置返回值,会导致new操作符失效
手写实现new操作符
从上面的例子可以看到new操作符具体做了这些事情
- 返回一个对象(内部需要创建一个对象)
- 函数的
this绑定到生成的新对象上(需要修改this指向) - 创建的每个新对象最终都会被
[[Prototype]]链接到这个函数的prototype对象上(需要将对象与构造函数的原型链接起来) - 函数没有返回对象类型,那调用new创建的实例,会返回这个新的对象
来看看怎么实现
function newFunc(Con, ...args) {
let obj = {}; //1、创建一个空对象
obj.__proto__ = Con.prototype; // 2、将构造函数的原型对象,作为新对象的原型对象
let result = Con.apply(obj, args); // 3、让构造函数的this指向新对象obj
return result instanceof Object ? result : obj; // 4、判断构造函数返回的是否是一个对象,是的话,就返回构造函数返回的值,否则,返回obj
}
检验下:
function Test(name) {
this.name = name;
}
Test.prototype.sayName = function () {
console.log(this.name)
}
const t = newFunc(Test, 'hello');
console.log(t.name) // hello
t.sayName() // hello
通过手写,可以看到构造函数也是改变this指向的一种方式。