随笔-ES6 class语法糖 及继承性实现
ES5构造函数的缺点
在ES5时,JS还没有“类”的概念,创建对象使用的都是构造函数,而构造函数和普通函数没有什么本质区别,只是在书写上要求构造函数的函数名首字母大写,方便和普通函数区分。即ES5构造函数不仅可以new调用,还可以当成普通函数调用。
如果我们要限制ES5构造函数不能当成普通函数调用,则需要做额外处理
function Person(name){
if(this instanceof Person) {
this.name = name
} else {
throw new Error('必须使用new调用')
}
}
其次,ES5构造函数中不适合设置被所有实例共享的属性和方法
注意下面的say方法,eyenum属性对所有Person实例的逻辑都是相同,即不需要在new Person时传入say函数,所以如果将say,eyenum设置到this上,会导致每次new都会给实例创建一个新的say方法和eyenum属性,尽管逻辑相同
function Person(name){
this.name = name
this.say = function(){
console.log('my name is', this.name)
}
this.eyenum = 2
}
const p1 = new Person('张三')
const p2 = new Person('李四')
console.log(p1.say === p2.say) // false
解决方案是,将共享属性和方法定义到构造函数原型上,因为存在以下关系
Person.prototype === p1.__proto__
当p1自身找不到方法时,会沿着原型链去p1.__proto__上找
function Person(name){
this.name = name
}
Person.prototype.say = function(){
console.log('my name is', this.name)
}
Person.prototype.eyenum = 2
const p1 = new Person('张三')
const p2 = new Person('李四')
console.log(p1.say === p2.say) // true
这样改的好处是节省内存,坏处是破坏了面向对象的类的结构
另外对于静态属性和静态方法的定义,也会被定义到构造函数外部,体验破外了类的结构
function Person(name){
this.name = name
}
Person.prototype.say = function(){
console.log('my name is', this.name)
}
Person.prototype.eyenum = 2
Person.run = function(){
console.log('running')
}
Person.cate = '灵长类'
整体下来感觉很松散。
ES6 类
ES6 class其实并没有改变JS类的模型,而只是对于ES5构造函数的一种封装处理,算是一种语法糖
首先可以看出class Person中Person依旧是一个函数,所以只是关键字从function变为了class,另外也不需要带函数的参数列表了,那么它是否可以像函数一样定义成函数表达式形式呢?
答案是可以的
那么ES6的class可以当成函数调用吗?
答案是不可以,只能通过new调用 ,这是对ES5构造函数的缺陷弥补
ES6 class内部默认有一个空参constructor函数,当我们new Person时会调用默认的空参constructor函数,如果我们额外指定了constructor函数,则class不再提供默认的空参构造函数。
在constructor中我们可以指定被创建的实例对象的私有属性和方法
和ES5构造函数中定义this.name = name没有什么区别,可以说一模一样
ES6 class定义共享方法时,需要在内部定义成简写方法形式,最终会被定义到类(函数)的原型上
这里在实现结果上和ES5构造函数定义共享方法是一致的,但是有一点差别,那就是ES6 class定义到原型上的方法或属性默认是不可遍历的,即属性描述符enumerable:false
而使用ES5构造函数时,直接定义在构造函数原型上的共享方法的enumerable是true
这其实算是ES6 class的一种改进措施,因为我们不希望在for...in遍历实例对象属性时,将原型上的一些属性也遍历出来,for...in只会遍历enumerable为true的属性。
ES6 class定义共享属性时
有两个特点:
1、共享属性并没有定义到原型上,而是定义到了每个实例对象上
2、我们需要注意class中定义共享方法和共享属性不能带function,var,let,const等声明指令
这里ES6 class为什么要将共享属性定义从原型对象挪到实例对象上呢?挪窝后,这还是共享属性吗?答案,不是共享的了,而是每个实例私有的属性。
为什么要这样搞呢?那我们怎么定义共享属性呢?
其实在面向对象设计比较成熟的一些语言中,如Java,Java语言中,类的所有实例能共享的属性其实就是类上的属性,即静态属性。 Java语言底层做了优化,即可以通过实例对象直接访问定义在类上的静态属性,虽然底层实现依旧是 类.静态属性
所以JS这里其实有了两种方式实现共享属性
1、将属性定义到原型上:可以直接通过对象访问
2、将属性定义为类的静态属性:只能通过类.静态属性访问
我觉得ES6是想让我们使用2,而不是1,但是实际使用上2的体验并不好,因为无法通过实例对象直接访问到共享属性,很不方便
但是Java苦恼的却是为啥实例对象可以访问到类上的静态属性......所以说没有最完美的语言
所以这里大家就不要在class中想定义被所有实例共享的属性了,如果非要搞得话,可以去类的原型上搞
剩下的就是ES6 class中定义静态属性和方法了,即定义类(构造函数)本身的属性和方法
我们通常只在类(构造函数)上的 方法,当作工具方法,一来调用简单,直接Person.run(),而来节省内存,内存中只存在一份
除了以上语法上的不同,ES6 class 和ES5 构造函数还有以下几点不同,我们做一个总结:
ES6 class和ES5 构造函数区别总结
1、ES6 class虽然本质也是函数,但是不会发送声明提升,即声明class前,无法使用class
ES5 构造函数本质就是普通函数,会发送声明提升,即声明前,可以使用它
2、ES6 class 内部默认开启严格模式,而ES5构造函数内部默认非严格模式
如严格模式下,不允许访问函数的arguments上的callee
非严格模式下,可以访问函数的arguments上的callee
3、ES6 class定义在原型上的共享方法是不可遍历的,ES5 构造函数定义在原型上的方法是可遍历的
4、ES6 class只能new调用,ES5 构造函数既可以new调用,也可以函数调用
ES5 构造函数实现继承 最好的方式(寄生组合式继承)
function Sup(name){
this.name = name
}
Sup.prototype.say = function(){
console.log('my name is', this.name)
}
function Sub(name,age) {
Sup.call(this,name)
this.age = age
}
Sub.prototype = Object.create(Sup.prototype, {
constructor: {
value: Sub,
configurable: true,
writeable: true,
enumerable: false
}
})
Sub.prototype.intro = function(){
console.log('I am', this.age, 'years old')
}
const lisi = new Sub('lisi', 18)
lisi.say() // my name is lisi
lisi.intro() // I am 18 years old
ES6 class实现继承extends
class Sup{
constructor(name){
this.name = name
}
say() {
console.log('my name is', this.name)
}
}
class Sub extends Sup{
constructor(name,age) {
super(name)
this.age = age
}
intro(){
console.log('I am', this.age, 'years old')
}
}
const lisi = new Sub('lisi', 18)
lisi.say() // my name is lisi
lisi.intro() // I am 18 years old
可以发现ES6 class extends继承底层实现本质就是 ES5构造函数的寄生组合式继承
相关文章
- 【笔记】ES6 模板字符串
- 使用es6模板字符串嵌入变量,并实现循环「建议收藏」
- Vue之ES6对象字面量增强写法
- ES6的基础语法
- Es6新特性之【变量-块级作用域-字符串模板-解构赋值】
- 每天3分钟,重学ES6-ES12(十七)模块化历史
- 名字随便起——es6 Proxy
- 传统的回调函数与 ES6中的promise回调以及 ES7 的async/await终极的异步同步化
- webpack-ES6转ES5[通俗易懂]
- VUE学习笔记——es6对象合并 数组转对象
- ES5/ES6的继承除了写法以外还有什么区别
- 每天3分钟,重学ES6-ES12(十)Promise参数实例方法介绍
- JS面试题-es6 新增 for of 循环详解
- babel ES6 转换 ES5 实现原理
- ES6转ES5_nodejs支持es6吗
- ES6新特性class类
- ES6中Promise简单记一下笔记
- ES6 箭头函数 Arrow Function
- 深入浅出ES6的迭代器