彻底搞懂ES6中的Class(全面解析)
ES6中的class你会想到什么呢?可能有的小伙伴只是知道它,或许用过一两次,没关系,这次我们从头开始,带你体验class的铁汉柔情(强大和优雅);
跟往常一样,我们需要带着三个问题去看这篇文章:what?how? where?
什么是Class
使用react的小伙伴都知道,最初学习使用react框架的时候,首先了解的就是通过class来书写组件,所以,通过阅读这篇文章,也是为学习react打一个重要的基础!让我们先看一个class标准的写法吧:
class Point {
constructor (x, y) {
this.x = x
this.y = y
}
MyName () {
return (`我的名字叫${this.x}${this.y}!`)
}
}
let getName = new Point('f', 'zh');
getName.MyName() // 我的名字叫fzh
复制代码
通过上面的代码可以看出这个class的写法,与ES5中构造函数的写法很相似,没错,class
就是构造函数的语法糖(可以理解成class
就是构造函数的另一种写法),使用的时候,也是直接对class
(类)使用new
命令,跟构造函数的用法完全一致;考虑到有的同学不太明白上面写的啥,在这里解释一下:
- 首先声明了一个名字为Point的class,换做ES5的写法就是
function Point ( ) { }
;class
里面写的方法,最终都定义在了class
的原型上,换做ES5的写法就是:Point.prototype.MyName = function ( ) { }
;constructor
方法就是构造方法,里面的this
代表实例对象,constructor
属性,直接指向“类”的本身;constructor
是class的默认方法,通过new
命令生成对象实例时,自动调用constructor
,即使没有定义constructor
,也会默认有一个空的constructor
;
它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,光说无用,让我们来证明一下吧:
// 接上面代码
console.log(typeof Point); // "function"
console.log(Point === Point.prototype.constructor); // true
复制代码
这段代码表明了类的数据类型就是函数,类的本身就是指向构造函数;
本段需要注意的地方如下:
- class内部定义的方法都是不可枚举的;
- 生成类的写法需使用
new
命令,否则会报错; constructor
方法默认返回实例对象(即this
);- 类和模块的内部,默认就是严格模式,所以不需要使用
use strict
指定运行模式; - 类不存在变量提升;
- 类的方法内部如果含有this,它默认指向类的实例;
以上就是class的基本知识,如果理解了上面的内容,那么恭喜你,可以放心的在你代码中使用class了!
class进阶知识
开头咱们说到过class是强大和优雅的,那么它到底强大在哪?又优雅在哪呢?我怎么读现在还没有看出来呢?好,让我们带着这两个疑问去下面的文章寻找答案!
class静态方法
类相当于实例的原型,所有在类中定义的方法都会被实例继承,如果不想让你定义的方法被实例继承,也很简单,只需要在方法前面加上 static
关键字,就表示该方法不会被实例继承,而且直接通过类就可以调用,这也称为 静态方法 ,看下面代码:
class MyStatic {
static classMsg () {
return ("My name is feng zhihao");
}
}
// 因为"classMsg"定义方法名称之前加了 'static' 关键字
// 所以该方法是一个静态方法
Mystatic.classMsg(); // "My name is feng zhihao"
let foo = new Mystaic();
foo.classMsg(); // TypeError: foo.classMsg is not a function
复制代码
上面代码表明:静态方法可以直接在类上调用,而不是在类的实例上调用,如果在实例上调用的话,就会抛出错误,表示不存在该方法;扩展一下,父类的静态方法可以被子类继承:
class Fzh {
static classMsg () {
return (`新春快乐,鼠你最棒`);
}
}
class Ibas extends Fzh {}
Ibas.classMsg(); // "新春快乐,鼠你最棒"`
复制代码
new.taeget属性
new
是从构造函数生成实例的命令,ES6为 new
命令引入了 new.target
属性,如果构造函数不是通过 new
命令调用的,那么 new.target
会返回 undefined
,在 class
内部调用 new.taeget
,返回是当前class,需要注意的是:子类继承父类时,new.target
会返回子类:
// " 返回当前class "
class Register {
constructor (x) {
console.log(new.taeget === Register);
this.x = x;
}
}
// 返回的是true,说明new.target返回的是当前的class
let RegClass = new Register("hello") // true
复制代码
// "父类调用new.target返回子类"
class Register {
constructor () {
console.log(new.target === Register);
}
}
class Square extends Register {
constructor (x) {
super(x, x);
}
}
let sole = new Square(1); // false(new.target返回的是子类)
复制代码
super关键字
读到这,是不是觉得初学react的时候就是在学习class呢,尤其是 extends
、super
关键字,我们会经常碰到它们的身影,其实super
这个关键字既可以当做函数使用,也可以当做对象使用,在这两种情况下,它的作用完全不同:
第一种情况: 做为函数时,super
只能用在子类的构造函数中,用在其他地方会报错;当调用super时,它代表父类的构造函数 ( 这是规定 ) ,ES6还规定了子类的构造函数必须执行一次super函数;
接下来的说法可能比较绕,建议读完一遍然后看一遍代码,再回过头读一遍效果会更好:super虽然代表父类,但是它返回的是子类的实例,也就是说super内部的this是指向子类的,证明一下:
class Supe {
constructor () {
console.log(new.target.name);
}
}
class SonSupe extends Supe {
constructor () {
super();
}
}
new Supe(); // Supe
new SonSupe(); // SonSupe
复制代码
从上面代码可以看出,new.target指的是当前正在执行的函数,在 super
执行时,它指向的是子类 SonSuper
的构造函数,而不是父类 Supe
的构造函数,也就是说,super()
内部的this是指向子类的。
第二种情况: super作为对象时,在普通方法中指向父类的原型对象,在静态方法中指向父类,由于 super
指向的是父类的原型对象,所以定义在父类实例上的方法或属性是无法通过 super
调用的:
class A {
gain () {
return ("欢迎大佬指出不足,我绝对不会改~");
}
}
class B extends A {
constructor () {
super();
console.log(super.gain()); // "欢迎大佬指出不足,我绝对不会改~"
}
}
复制代码
从上面代码可以看出super指向父类的prototype
,所以 super.gain()
等同于 A.prototype.gain()
,ES6规定:通过 super
调用父类方法时,super
会绑定子类的 this
;
结尾
这篇文章主要讲了ES6中 class
的语法和小细节,我们需要做的就是记住这些知识点,真真正正的去理解它们,不断的练习与尝试,日后才能灵活的去运用到开发中,另外,推荐大家去看一下阮一峰出版的《ES6标准入门》这本书,最后祝小伙伴们新春快乐,永无Bug! 哦了,散会!
作者:冯智豪
链接:https://juejin.im/post/5e174823e51d451c52192e7f
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。