ECMAScript 6 笔记(24)- Class 的基本语法

原创 乘风逐月 随笔 ES6 159阅读 2019-01-11 12:13:17 举报

一、简介

JavaScript 语言中,生成实例对象的传统方法是通过构造函数。

上例的写与传统的面向对象的语言(如 C++ 和 JAVA)差异很大。
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板,通过 class 关键字,可以定义类。

二、class

1.语法

ES6 的 class 可以看做只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法。
上例代码用 ES6 的 class 改写:

上例定义了一个 “类” Point,里面有一个 constructor 方法,这就是构造方法,而 this 关键字则代表实例对象。Point 类除了构造方法,还定义了一个 toString 方法。
注意:
(1)定义 “类” 的方法的时候,前面不需要加上 function 这个关键字,直接把函数定义放进去就可以了。
(2)方法之间不需要逗号 “,” 分隔,加了会报错。

ES6 的类,完全可以看做构造函数的另一种写法。

上例表明,“类”的数据类型就是函数,类本身指向构造函数。
使用的时候,也是直接对“类”使用 new 命令,和构造函数的用法一致。

注: 类必须使用 new 调用。这是与普通构造函数的一个主要区别,后者不用 new 也可以执行。

2.关于 prototype

(1)“类”的所有方法都定义在 prototype 属性上
构造函数的 prototype 属性,在 ES6 的“类”上面继续存在。事实上,“类” 的所有方法都定义在类的 prototype 属性上面。

(2)在“类”的实例上调用方法,其实就是调用原型上的方法。

(3)Object.assgin 可以向类一次添加多个方法
由于类的方法都定义在 prototype 对象上面,所以类的新方法可以添加在 prototype 对象上面。Object.assgin 方法可以很方便地一次向类添加多个方法。

(4)prototype 对象的 constructor 属性,直接指向“类”的本身
prototype 对象的 constructor 属性,直接指向“类”的本身,这与 ES5 的行为是一致的。

(5)类的内部方法所有方法都不可枚举

上例中,Point 内部的方法都不可枚举,这与 ES5 的行为不一致。

上例中,使用 ES5 的写法,toString 方法可枚举。

3.constructor 方法

constructor 方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法。一个类必须有 constructor 方法,如果没有显示定义,一个空的 constructor 方法会被默认添加。

上例代码中,定义了一个空的类 Point ,JavaScript 引擎会自动为它添加一个空的 construct 方法。
constructor 方法默认返回实例对象(即 this),完全可以指定返回另外一个对象。

4.类的实例

生成类的实例的写法,与 ES5 完全一样。也是使用 new 命令,如果忘记加 new ,像函数那样调用 类 将会报错。
与 ES5 一样,实例的属性除非显式定义其本身(既定义在 this 对象上),否则都是定义在原型上。

上例中,x,y 都是实例 p 的自身属性(定义在 this 上),所以 hasOwnProperty 返回 true。而 toString 是原型对象的属性(定义在 Point 类上),所以 hasOwnProperty 方法返回 false。

与 ES5 一样,所有实例共享一个原型对象。可以使用 Object.getPrototypeOf 方法通过实例给 “类” 添加对象

5. 取值函数(getter)和存值函数(setter)

与 ES5 一样,在 “类” 的内部可以使用 get 和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
例如:

存值函数和取值函数是设置在属性的 Descriptor 对象上的。

上例中,set 和 get 函数是定义在 prop 属性的描述对象上的,与 ES5 一致。

6. 属性表达式

类的属性名,可以采用表达式。

上例中,Square 类的方法名 getArea,是从表达式得到的。

7. class 表达式

与函数一样,类也可以使用表达式的形式定义。

上例中,,使用表达式定义了一个类。这个类的名字是 myClass 而不是 Me,Me 只在 Class 的内部代码可用,指代当前类。
如果类的内部没有用到的话,可用省略 Me,写成以下形式:

采用 Class 表达式,可用写出立即执行的 Class。

person 是一个立即执行的类的实例。

8. 使用注意事项

(1)严格模式
类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。 考虑到未来所有代码,其实都运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。
(2)不存在提升
类不存在变量提升,这一点与 ES5 完全不同。

上例中,会报错,因为 ES6 不会把类的声明提升到代码头部。这种规定与 “继承” 有关,必须保证子类在父类之后定义。
(3)name 属性
由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被 Class 继承,包括 name 属性。

name 属性总返回紧跟在 class 关键字后面的类名。
(4)Generator 方法
如果在某个方法之前加上星号 “ * ” , 就表示该方法是一个 Generator 函数。

上例中,Foo 类的 symbol.iterator 方法前有一个星号,表示该方法是一个 Generator 函数。symbol.iterator 方法返回一个 Foo 类的默认遍历器,for...of 循环会自动调用这个遍历器。
(5) this 指向
类的方法内部如果含有 this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。

上例中,printName 方法中的 this,默认指向 Logger 类的实例。但是,如果将这个方法取出来单独使用。this 会指向该方法运行时所在的环境,因为找不到 print 方法而报错。

解决方法:
(1)在构造函数中绑定 this

(2)使用箭头函数

三、静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为 “静态方法” 。

上例中, Foo 类的 classMethod 方法前有 static 关键字,表明该方法是一个静态方法,可以直接在 Foo 类上调用。如果在实例上调用静态方法,会抛出错误。

注意:
(1)如果静态方法包含 this 关键字,这个 this 指的是类,而不是实例。
(2)静态方法可以与非静态方法重名。

(3)父类的静态方法,可以被子类继承

(4)静态方法也可以从 super 对象上调用。

四、实例属性的新写法

实例属性除了定义在 constructor() 方法里面的 this 上面,也可以定义在类的最顶层。

五、静态属性

静态属性指的 Class 本身的属性,而不是定义在实例对象上的属性。

新写法:静态属性写在最顶层,前面加上 static 关键字

六、new.target 属性

new 是构造函数生成实例对象的命令。ES6 为 new 命令引入了一个 new.target 属性,该属性一般用在构造函数之中,返回 new 命令作用于的那个构造函数。
如果构造函数不是通过 new 命令调用的,new.target 会返回 undefined,因此这个属性可以用来确定构造函数是怎么调用的。

Class 内部调用 new.target,返回当前 Class。

但是子类继承父类时,new.target 会返回子类。

利用这个特点,可以写出不能独立使用,必须继承后才能使用的类。

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复