这是我参与更文挑战的第13天,活动详情查看: 更文挑战
前言
这是一个高频面试题,应该很多同学都被问到过,比如我旁边的同学被问了不下两次←_←。在考虑如何用ES5实现以前,先来简单回顾下ES6的类语法与ES5构造函数的写法到底有啥区别,具体ES6 class部分请移步阮大。
类语法与构造函数语法区别
- 函数声明可以提升,类声明不能提升
- 类声明的代码默认运行在严格模式下
- 在类中,所有方法都是不可枚举的
- 在类中,只能通过new关键词去调用类的构造函数
- 类中通过static关键词定义的属性和方法只能通过类本身去访问,不能通过类实例去访问。
- 在类中不可以修改类名
下边是一个ES6 class继承的例子:
ES6实现
class FatherFun{
constructor(val) {
this.fatherVal = val;
}
getFatherVal() {
console.log('父类属性值:',this.fatherVal);
}
static staticMethod() {
console.log('这是一个父类静态方法');
}
}
class SonFun extends FatherFun{
constructor(val, sonVal) {
super(val);
this.sonVal = sonVal;
}
getSonVal() {
console.log('子类属性值:', this.sonVal);
}
static staticSonMethod() {
console.log('这是一个子类静态方法');
}
}
在ES6 class语法没有出现以前,我们实现继承最常用的方式是通过构造函数+原型链来实现的,但是ES5是没有static实现的。static要求不能在实例访问,只能类本身访问。我们知道JS中一切皆对象,如果我们把static属性当作是对象中的一个key,那是不是就可以实现只能构造函数本身去访问,实例无法访问呢~
ES5实现(第一版)
function FatherFun(val) {
this.fatherVal = val;
}
FatherFun.prototype.getFatherVal = function() {
console.log(this.fatherVal);
}
FatherFun.staticMethod = function() {
console.log('这是一个父类静态方法');
}
//构造函数+原型链实现继承
function SonFun(fatherVal, sonVal) {
FatherFun.call(this, fatherVal);
this.sonVal = sonVal;
}
SonFun.prototype = new FatherFun();
SonFun.prototype.constructor = SonFun;
SonFun.prototype.getSonVal = function() {
console.log(this.sonVal);
}
SonFun.staticSonMethod = function() {
console.log('这是一个子类静态方法');
}
但是对于类语法的一些特殊用法我们要怎么去实现呢??
- 类声明不能提升这块,我们可以考虑用let或者const来声明构造函数。
- 加入use strict来表明严格模式下运行
- 只通过new关键词调用这块,我们可以考虑使用new.target来判断
- 所有方法不能枚举,可以考虑用Object.defineProperty去修改enumerable
- 类的名称只在类中是常量,不可修改,外部是可以修改的。可以考虑在类内部重复声明类名为const
ES5实现(第二版)
先考虑实现父类FatherFun:
let FatherFun = (function () {
'use strict';
const FatherFun = function(val) {
if(typeof new.target === 'undefined') {
throw new Error('必须通过new关键词调用构造函数');
}
this.fatherVal = val;
}
Object.defineProperty(FatherFun.prototype, 'getFatherVal', {
value: function() {
if(typeof new.target !== 'undefined') {
throw new Error('必须通过new关键词调用构造函数');
}
console.log(this.fatherVal);
},
enumerable: false,
writable: true,
configurable: true
})
FatherFun.staticProp = 'static property';
FatherFun.staticMethod = function() {
console.log('这是一个父类静态方法');
}
return FatherFun;
})()
直接通过父类本身去访问父类的静态方法和属性
FatherFun.staticMethod(); //这是一个父类静态方法
通过父类实例去访问父类属性和方法
let f = new FatherFun('我是父类实例');
console.log(f1.fatherVal); //father
f1.getFatherVal(); //父类属性值: 我是父类实例
console.log(f.staticProp); //undefined
f.staticMethod(); //Uncaught TypeError: f.staticMethod is not a function
问题来了,ES5中我们一般是用构造函数+原型链的方式去实现继承,但是由于类是只能通过new关键字来调用,所以构造函数的方式是无法使用,就只能考虑其他方式来实现继承了,如只用原型链。