这次,彻底弄懂 ES6 的 Class
Class 声明
可以使用类声明和类表达式来定义类。

类声明中一般会在构造函数 constructor
中添加属性:

类声明属性还有另一种方式:

然后就是声明类的方法:

也可以声明getter/setter:

还有声明类的静态方法、私有属性、私有方法,这里就不作说明。
Class 是语法糖
之所以说 Class
是语法糖,是因为 Class
的本质是一个函数。以下类的声明:

等价于:

new Class
其实就是传统的 new Function
方式声明对象的语法糖。
但 Class
创建的函数和普通函数还是有一些区别:
Class
创建的函数必须要用new调用它,直接调用会报错。

Class
创建的函数内部会添加个内部属性标记 [[IsClassConstructor]]: true
,通过检查该属性来进行控制。
声明的类方法不可枚举,类定义将 prototype
中的所有方法的 enumerable
标志设置为 false
。
构造函数中的代码是严格模式。
new Class 执行过程
- 创建一个空的简单 JavaScript 对象
- 创建的空对象的
[[Prototype]]
指向Class
创建的函数的原型对象,类的方法会放入到原型中。 - 执行构造函数,并将创建的空对象绑定为
this
的上下文,给this
对象添加属性。 - 返回这个新的对象
this 问题
对象的方法传递到其他地方执行,this
将不再指向其对象。

解决方法有 bind
和传递一个包装函数外还有一种类字段声明的方法。

通过箭头函数可以让 this
始终指向 cat
对象。
Class 继承
ES6 中可以用 extends
实现继承:

其实相当于原型继承:

extends
继承就是将 Cat
原型对象的原型设置为 Animal
的原型现成原型链,而 Cat
的 hide
方法添加到 Cat
原型中。
Class Cat
代码中没有声明 constructor
,没有 constructor
时会自动生成以下constructor
:

supper
的执行必须要在使用 this
之前,如果不使用 supper
执行就使用 this
会报错。

this
是 undefined
,也就是说执行 supper
后,this
才会被赋值一个对象。
其实继承的子类在 new
操作上和普通的函数 new
操作有一定的区别。
普通函数 new
操作:
- 创建一个空对象,这个对象的原型
[[Prototype]]
属性设置为该函数的原型对象。 - 将这个对象赋值给
this
。 - 然后再执行函数里的代码,给
this
添加属性。 - 最后返回这个对象。
而继承子类的 new
操作是:
- 不会创建一个空对象赋值给
this
。 - 通过
supper
执行父类的constructor
时创建一个对象,将父类的属性添加到这个对象中,并将设置这对象的原型链。 this
赋值这个对象。- 返回这个对象。
因此不执行 supper
对象就创建不了,this
也就是 undefined
,给 this
添加属性时就提示报错了。
super
还可以用来调用父类方法,super.method(...)
。
检查类型
typeof

可以检查基本数据类型和 function
,但是有个例外,null
会返回 object
, 因为在 JavaScript 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null
表示为全零,所以将它错误的判断为 object
。
instanceof

能检测出引用类型,缺点:不能检测出基本类型,且不能跨 iframe。
instanceof Class
的大致过程如下:
Class
是否有静态方法 Symbol.hasInstance
,有的话直接调用这个方法。
Symbol.hasInstance
用于判断某对象是否为某构造器的实例。因此你可以用它自定义instanceof
操作符在某个类上的行为。这个静态方法在内置对象中是只读不可枚举的,但可以配置,大多数内置对象是没有这个静态方法,这一般用于自定类中修改
instanceof
操作符的行为。
没有这个静态方法的话,就检查 Class.prototype
是否等于 obj
的原型链中的原型之一。

Object.prototype.toString.call

检测出值类型和引用类型。但是 Object.prototype.toString
的行为是可以通过 Symbol.toStringTag
来自定义的。
Object.prototype.toString()
返回 [object Type]
,这里的 Type
是对象的类型。如果对象有 Symbol.toStringTag
属性,其值是一个字符串,则它的值将被用作 Type
。

ES6 之前早期的内置对象是没有这个属性的,Object.prototype.toString()
会返回的是一个特殊的标签。
ES6 之后的内置对象一般都有会这个属性,并且这个属性是只读不可以枚举的,但可以配置,因此一般常常用于自定义类中定义。
那么在检查类型的时候希望考虑防止自定义类修改 Object.prototype.toString()
的行为,可以这么判断:

代码中先将 Symbol.toStringTag
设置为undefined
,然后再 Object.prototype.toString.call()
检测类型,如果 Symbol.toStringTag
是对象自身属性,将其还原,如果是其原型的属性,将添加的 Symbol.toStringTag
自身属性删除。
Class 缺点
ES6 Class
也存在一定的问题,以导致 react
放弃Class
组件声明而用函数组件和 hook
方式替代,那ES6 Class
有什么缺点呢?
this 丢失问题
使用 Class
必然会使得 this
的使用率增多,自然this
丢失的问题也就经常需要处理。
语法糖
由于 ES6 Class
只是一个语法糖,使用者需要理解 JavaScript 的 new function
和原型的原理才能更好使用 Class
,并且缺乏抽象类和接口的支持,继承和代码复用实现更加复杂。JavaScript 可以字面量声明对象的机制使得 Class
使用变得复杂化。
因此代码复用更简洁且更易维护的函数式编程慢慢被各大框架所喜爱。
转载自:https://juejin.cn/post/7352550826621583375