构造函数的工作原理
这已经是一个老生常谈的问题了,想要通过构造函数创建一个对象,需要经过下面几步👇:
创建一个 空对象newObj
将 空对象newObj的原型 绑定为 构造函数constructor的prototype
然后通过 call或者apply方法 将 构造函数constructor 的 this指针以及传入的参数 绑定到 空对象newObj上,生成新对象resultObj
最后判断 新对象resultObj 是否属于 对象类型或者函数类型 ,如果属于则 直接返回,不属于则 返回空对象newObj
被诱导的构造函数
当还没有Class
的时候,我们使用的构造函数都是由Function
改造而来的,为Function
加上new
操作符,通过改变this
的指向把属性值绑定到新对象上,然后返回新对象。
那如果不加上new
操作符,构造函数还会正常运行吗?答案是不会
,它在没有new
的帮助下依旧是一个Function
。下面是一个例子👇
如何写写出专一的构造函数
其实上面例子
的问题很简单,就是Person
函数中this
指向的问题:
- 使用
new
的Person
函数,this
指向创造出来的实例
- 直接使用的
Person
函数,严格模式下的this
指向undefined
,非严格模式下指向window
那么接下来我们解决问题的关键就是判断构造函数的指针是否指向实例
判断this的指向
最简单的方式,直接在构造函数中判断this
的指向,当this
指向undefined
或者window
是抛出错误
function Person(name, age) {
if (this == undefined || this == window) {
throw new Error("wrong way of using constructor");
}
this.name = name;
this.age = age;
}
new Person('god',123) // Person {name: 'god', age: 123}
Person('dog',321) // Uncaught Error: wrong way of using constructor
也可以使用instanceof
的方式来判断
function Person(name, age) {
if (!(this instanceof Person)) {
throw new Error("wrong way of using constructor");
}
...
}
new Person('god',123) // Person {name: 'god', age: 123}
Person('dog',321) // Uncaught Error: wrong way of using constructor
new.target
这个方法是由官方提供的,这里的new
并不是指一个对象,只不过new.target
指向到被new
调用的构造函数,于是new.
成为了一个虚拟的上下文。
- 在
构造函数
中使用new.target
,返回一个指向构造方法或函数的引用
- 在
普通函数
中使用new.target
,返回undefined
function Person(name, age) {
if (!new.target) throw new Error("wrong way of using constructor");
this.name = name;
this.age = age;
}
console.log(new Person("god", 123)) // Person {name: 'god', age: 123}
Person("dog", 123) // Uncaught Error: wrong way of using constructor
Class
Class
在推出伊始就被规定为必须结合new
使用,因此也是最简单的解决方法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
console.log(new Person("god", 123)) // Person {name: 'god', age: 123}
Person("dog", 123) // Uncaught TypeError: Class constructor Person cannot be invoked without 'new'
使用new.target实现一个抽象类
抽象类
指一个不能被单独使用,必须被继承后才能使用的类
JS本身并没有提供抽象类的实现方法,但是可以通过new.target
曲线救国
class grandFather {
constructor(address) {
if (new.target === grandFather)
throw new Error("wrong of using abstract class");
this.address = "this is a address";
}
}
class Person extends grandFather {
constructor(name, age) {
super();
this.name = name;
this.age = age;
}
}
console.log(new Person("god", 123)) // Person {address: 'this is a address', name: 'god', age: 123}
console.log(new grandFather()) // Uncaught Error: wrong of using abstract class
结语
要是有任何错误和需要修改的地方,恳请指出~