ES6之Class
简介
作为前端开发人员,并不陌生es5,es6。今天在这里主要是讲讲es6的Class,它具体是干什么的。这篇幅主要是针对,对于es6中Class使用较少,不了解Class的人员进行一个大概的描述,并对Class的一些注意点进行标注。可以方便快捷的了解Class。
Class的定义
Class是一个语法糖,当我们使用Class声明一个变量的时候,就类似于es5中我们使用function 声明一个具名的构造函数.简化来说,就是Class声明的‘类’近似等于构造函数。在class声明的变量中的函数方法,都是直接作用到es5中funtion.prototype上
老规矩,上代码先:
// es5 进行自定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.toString = function () {
return '名称:' + this.name + '年龄:' + this.age
}
var p = new Person('章三', 20);
// 输出 {name: '章三', age: 20}
// es6的改版
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
toString() {
return '名称:' + this.name + '年龄:' + this.age
}
}
var p = new Person('李四', 20);
// 输出 {name: '李四', age: 20}
上面的代码只是一个简单的例子,很明显咱们可以看出结构上面有很大的差别,es5之前自定义一个‘类’,就是使用funtion进行声明。如果需要自定义一些其他函数,还需要再使用其原型进行定义。es6的结构,则不需要额外进行再进行声明,具体的情况再往下看。
Class的一些要点
es5 与 es6 在Class上面的异同点
es5于es6的不同点
- Class 声明‘类’,在es5 之前都是没有,于es6 开始支持该声明变量的方法,简化了代码,让js代码使java等后端人员更好的认识。
- es5 在声明‘类’的时候,直接使用function进行声明一个构造函数,es6及以上版本,则是使用class声明包含构造函数的‘类’
- es5 声明的‘类’,在其原型上面添加的函数变量都是可以被枚举的,es6声明的则不可以。
- es6 生成的类,必须使用 new 调用,这也是区别去普通构造函数的一点
- es6 class 不进行变量提升
- 严格模式,es6在class 中严格执行严格模式
es5于es6的相同点
- 生成对应的实例,都是使用new进行操作,并且共享同一个原型对象
- getter(取值函数)和 setter(存值函数)仍然存在于Descriptor对象上
Class上容易忽视的点
- 方法之间不需要逗号‘,’,可使用分号‘;’。
- class声明的变量 等于 该变量的构造函数 。例: Person=== Person.prototype.constructor
- 内部所定义的方法,不可被枚举,因此使用Object.keys()是获取不到自定义(除去constructor构造方法)方法的,使用Object.getOwnPropertyNames()方法可以获取。
- 如果一个类没有显式定义constructor方法,会被默认添加一个constructor 方法
- 类的属性名,可以使用表达式
let message = 'getName'
class Person {
_name = '章三';
[message]() {
return this._name
}
}
const people = new Person()
console.log(people[message]()) // 章三
- 类也是可以使用表达式进行表示
// 这个例子中的Person 类,只能在Woman里面使用,如果执行new Person() 就会报错
const Woman = class Person {
getNames() {
return Person.name
}
const woman = new Woman()
woman.getNames() // Person
}
const Man = class {}
// 以上两种是表达式进行表示类,不同的是,第一种,能找到对应的类的名称,并且很明显,知道原始类是谁。
- 静态方法是不能被 实例 继承的,只能被类所使用(子类进行继承 extends) 。并且静态方法中的this指的是类,而不是实例
class Person{
name = '章三';
static getName() {
return this.name
}
getName() {
return this.name
}
}
class Man extends Person{}
const man = new Person()
console.log(man.getName()) // 章三
console.log(Person.getName()) // Person
console.log(Man.getName()) // Man
- 静态属性
calss Foo {}
Foo.prop = 1;
// 2022 新写法
class Foo {
static prop = 1;
}
- 私有属性和私有方法是无法被实例所使用的,只能是类本身使用
Class 的新要点
- 实例属性的改变。上代码:这种写法是定义 实例对象 自身的属性,并非实力对象原型的属性
// ES2022之前版本
class IncreasingCounter {
constructor() {
this._count = 0
}
increment() {
this._count++;
}
}
// ES2022的新版本
class IncreasingCounter {
_count = 0;
increment() {
this._count++;
}
}
const a = new IncreasingCounter()
a.increment()
console.log(a._count) // 1
- 私有属性于私有方法(2022开始施行),2022之前没有真正的私有属性和私有方法(之前的定义方法只是在使用上的区别,但也还是能得到并且使用的,出现symbol这种类型的时候,虽然能解决,但未完全解决,仍然可以使用Reflect.ownKeys()获得)
// 私有属性发展史
// 1. 刚开始在定义类的时候,使用下划线开头的定为私有方法和私有属性
// 2. symbol的唯一性,将私有方法的名称唯一化
const symbol = Symbol('symbol')
class Symbolfun = {
[symbol](text) {
this[text] = text
}
}
// 3. 最新的声明方式, #声明的变量是私有属性,static声明的方法是私有方法
class Fun = {
#text = 1; // 私有属性,初始值,可不赋值
get #text() { // 私有方法,私有属性的getter方法
return this.#text
}
set #text(text) {
this.#text = text
}
}
- 无法进行变量提升
// class 只有先定义才能使用,这是因为与继承有关
// 例子1
new Foo() // ReferenceError
class Foo {}
// 例子2 ,如果class可以提升,则继承的时候,let声明的Foo并没有提升,这就会导致Foo没有值,则会导致报错。
let Foo = class {}
const Bar extends Foo {
}
- 类自带name,并且name值为类名,且无法修改
class Preson {}
Person.name // Person
- Generator 方法(不明白的可以看一下es6的generator方法), 就是在类方法前增加*,表示一个generator方法,具体看下面的例子
class Counter {
constructor(limit) {
this.limit = limit;
this.current = 0;
}
*incrementToLimit() {
while (this.current < this.limit) {
this.current += 1;
console.log(`Current: ${this.current}`);
yield;
}
}
}
const counter = new Counter(5);
const iterator = counter.incrementToLimit();
console.log('Starting...');
iterator.next(); // 输出: Starting...
iterator.next(); // 输出: Current: 1
iterator.next(); // 输出: Current: 2
// ...继续执行,直到达到限制值。
- this的指向 (类的方法内部如果含有 this ,它默认指向类的实例所运行的环境。如果想要让this指向实例,有两种方案可以使用。所以class中this慎用)
// 第一种方案, 构造函数中,使用bind进行绑定this
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
printName(name = 'there') {
// 未增加 构造函数的时候,此时的this指的是实例的所处环境的this
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName();
// 第二种方案,还是在构造函数中使用箭头函数接受一下对应的this
class Obj {
constructor() {
this.getThis = () => this;
}
}
const myObj = new Obj();
myObj.getThis() === myObj // true
- 静态方法与静态属性(类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法“。 )(静态属性指的是Class本身的属性,即 Class.propName,而不是定义在实例对象( this )上的属性。提案,静态属性在前面使用static)
// 以下例子,标明了静态方法和静态方法中this的指向
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log('hello');
}
baz() {
console.log('world');
}
}
Foo.bar() // hello
const foo = new Foo()
foo.bar() // TypeError: foo.bar is not a function
// 静态属性的例子, myStaticProp 就是类MyClass的静态属性,只能被类自身使用
class MyClass {
static myStaticProp = 42;
constructor() {
console.log(MyClass.myStaticProp); // 42
}
}
- 私有属性和私有方法 (私有方法,两种方案:类里面命名加_ ,第二种是声明在类外面)(私有属性,提案是在属性名前加 # )
- new.target === 类,该属性一般用在构造函数之中。可以用来判断是否通过new进行生成实例,但可以被子类继承,子类继承的时候,new.target就是子类。因此可以形成一个公共类,只能继承的使用方法。例子如下:
class Shape {
constructor() {
if (new.target === Shape) {
throw new Error('本类不能实例化');
}
}
}
class Rectangle extends Shape {
constructor(length, width) {
super();
// ...
}
}
var x = new Shape(); // 报错
var y = new Rectangle(3, 4); // 正确
总结
es6及以上对于类还是一直在封装发展,所以,现在这些东西或许后面还会进行改变。不过也需要我们了解class的演变,懂的底层越多,后面成长性会越高。