张森ZS

write code everyday

导航

读书笔记:深入理解ES6 (九)

第九章 JavaScript中的类

 

第1节 ES5中的近类结构

  ES5及早期版本中没有类的概念,因此用了一个相近的思路来创建一个自定义类型:首先创建一个构造函数,然后定义另一个方法并赋值给构造函数的原型。例如:

 1 function PersonType(name)
 2 {
 3     this.name = name;
 4 }
 5 
 6 PersonType.prototype.sayName = function() {
 7     console.log(this.name);
 8 }
 9 
10 var person = new PersonType("zxx");
11 person.sayName(); // "zxx"
12 
13 console.log(person instanceof PersonType); //true
14 console.log(person instanceof Object); //true

 

第2节 类的声明

  1. 在ES6中,要声明一个类,先写 class 关键字,然后是类的关键字。例如:

 1 class PersonClass {
 2     //等价于PersonType构造函数
 3     constructor(name) {
 4         this.name = name;
 5     }
 6 
 7     //等价于PersonType.prototype.sayName
 8     sayName() {
 9         console.log(this.name);
10     }
11 }
12 
13 let person = new PersonClass("zxx");
14 person.sayName(); //"zxx"
15 
16 console.log(person instanceof PersonClass); //true
17 console.log(person instanceof Object); //true
18 console.log( typeof PersonClass); //"function"
19 console.log( typeof PersonClass.prototype.sayName); //"function"

 

  2. 自有属性

  自有属性是实例中的属性,不会出现在原型上,只能在类的构造函数或方法中创建,此例中的 name 就是一个自有属性。

 

  3. 类与自定义类型的差异:

    1)函数声明可以被提升,而类声明与 let 类似,不能被提升;

    2)类声明中的所有代码将运行在严格模式下;

    3)在类中,所有方法都是不可枚举的;

    4)每个类中,都有一个名为 [[Construct]] 的内部方法,通过关键字new调用那些不含 [[Construct]] 的方法回导致程序抛出错误;

    5)使用除 new 以外的方式调用类的构造函数会导致程序抛出错误;

    6)在类中,修改类名会导致程序报错。

 

第3节 类表达式

  类和函数都有两种存在形式:声明形式和表达形式。声明形式的函数和类都由相应关键字(分别为function,class)进行定义,随后紧跟一个标识符。表达形式的函数和类与之类似,只是不需要再关键字后加标识符。举例:

 1 let PersonClass = class {
 2     //等价于PersonType构造函数
 3     constructor(name) {
 4         this.name = name;
 5     }
 6 
 7     //等价于PersonType.prototype.sayName
 8     sayName() {
 9         console.log(this.name);
10     }
11 }
12 
13 let person = new PersonClass("zxx");
14 person.sayName(); //"zxx"
15 
16 console.log(person instanceof PersonClass); //true
17 console.log(person instanceof Object); //true
18 console.log( typeof PersonClass); //"function"
19 console.log( typeof PersonClass.prototype.sayName); //"function"

 

第4节 作为一等公民的类

  1. 什么是一等公民?

    一等公民是指一个可以传入函数,可以从函数返回,并且可以赋值给变量的值。

  2. JavaScript中函数、类都是一等公民

 

第5节 访问器属性

  尽管应该在类构造函数中创建自己的属性,但类也支持直接在原型上定义访问器属性。创建 getter 时,需要在关键字 get 后紧跟一个空格和相应的标识符;创建 setter 时,只需要把 getter 关键字 get 替换为set即可。

 

第6节 可计算成员名称

  1. 类方法和访问器属性也支持使用可计算名称。用方括号包裹一个表达式,即可使用可计算名称。例如:

 1 let methodName = "sayName";
 2 
 3 class PersonClass {
 4     constructor(name) {
 5         this.name = name;
 6     }
 7 
 8     [methodName]() {
 9         console.log(this.name);
10     }
11 };
12 
13 let me = new PersonClass("zxx");
14 me.sayName(); // "zxx"

  2. 同样地,在访问器属性中也可以使用可计算名称。例如:

 1 let propertyName = "html";
 2 
 3 class CustomHTMLElement {
 4     constructor(element) {
 5         this.element = element;
 6     }
 7 
 8     get [propertyName]() {
 9         return this.element.innerHTML;
10     }
11 
12     set [propertyName]() {
13         this.element.innerHTML = value;
14     }
15 }

 

第7节 生成器方法

  在类中,也可以通过 “*” 来定义生成器。如果类是用来表示值的集合的,那么为它定义一个默认迭代器会更有用。

 

第8节 静态成员

  ES5、ES6中都有静态成员的语法。下面分别讲一下它们:

  1. ES5中,通过直接将方法添加到构造函数中类模拟静态成员。例如:

 1 function PersonType(name) {
 2     this.name = name;
 3 }
 4 
 5 //静态方法
 6 PersonType.create = function(name) {
 7     return new PersonType(name);
 8 };
 9 
10 //实例方法
11 PersonType.prototype.sayName = function() {
12     console.log(this.name);
13 };
14 
15 var person = PersonType.create("zxx");

  2. 在ES6中,简化了创建静态成员的语法。在方法或者访问器属性名前使用正式的静态注释(static)即可。例如:

 1 class PersonClass {
 2     //等价于PersonType的构造函数
 3     constructor(name) {
 4         this.name = name;
 5     }
 6 
 7     //等价于PerosonType.prototype.sayName
 8     sayName() {
 9         console.log(this.name);
10     }
11     
12     //等价于PersonType.create()
13     static create(name) {
14         return new PersonClass(name);
15     }
16 }
17 
18 let person = PersonClass.create("zxx");

  3. 注意:

    1)类中的所有方法和访问器属性都可以用static关键字类定义,唯一的限制是不能将static用于定义构造函数方法。

    2)不可在实例中访问静态成员,必须要直接在类中访问静态成员。

 

第9节 继承与派生类

  1. 基本概念介绍

    1)使用 extends 关键字可以指定类继承的函数。

    2)通过 super() 方法即可访问基类的构造函数。

    3)继承自其它类的类被称作派生类。

    4)如果不使用构造函数,则当创建新的类实例时会自动调用 super() 并传入所有参数。

    5)使用super()需要注意:

      a. 只可以在派生类的构造函数中使用super()

      b. 在构造函数中,访问 this 之前一定要调用 super(),它负责初始化this

      c. 如果不想调用 super(),唯一的方法是让类的构造函数返回一个对象。

  2. 类方法遮蔽

    派生类中的方法总会覆盖基类中的同名方法。如果仍然想使用基类中的方法,则可以借助 super 关键字来实现。

  3. 静态成员继承

    派生类继承基类后,基类中的静态成员也可以直接在派生类中使用。

  4. 派生自表达式的类

    1)只要表达式可以被解析为一个函数,并且具有[[Construct]]属性和原型,那么就可以用extends进行派生。

    2)extends强大的功能使得类可以继承自任意类型的表达式,而且可以是不止一个的表达式,这样可以动态地确定类的继承目标,创建不同的继承方法。举例:

 1 function Rectangle(length, width)
 2 {
 3     this.length = length;
 4     this.width = width;
 5 }
 6 
 7 Rectangle.prototype.getArea = function() {
 8     return this.length * this.width;
 9 }
10 
11 function getBase()
12 {
13     return Rectangle;
14 }
15 
16 class Square extends getBase() {
17     constructor(length) {
18         super(length * length);
19     }
20 }
21 
22 var x = new Square(3);
23 
24 console.log(x.getArea()); //9
25 console.log(x instanceof Rectangle); //true

  5. 内建对象的继承。

    在ES5中,无法通过继承的方式创建属于自己的特殊数组。而ES6类语法的一个目标是支持内建对象的继承。具体是:先由基类(Array)创建this的值,然后派生类的构造函数(MyArray)再修改这个值。举例:

 1 class MyArray extends Array {
 2     //
 3 }
 4 
 5 var colors = new MyArray();
 6 colors[0] = "red";
 7 
 8 console.log(colors.length); //1
 9 
10 colors.length = 0;
11 console.log(colors[0]) // undefined

  6. Symbol.species属性

    内建对象继承的一个实用之处是,原本在内建对象中返回实例自身的方法将自动返回派生类的实例。

    通过Symbol.species可以定义当派生类的方法返回实例时,应该返回的值的类型。

 

第10节 在类的构造函数中使用new.target

  类的构造函数必须通过new关键字调用,所以总是在类的构造函数中定义new.target属性。但是其值有时会不同。每个构造函数都可以根据自身被调用的方式改变自己的行为。例如,可以用new.target创建一个抽象基类(不能被直接实例化的类),就像这样:

 1 //抽象基类
 2 class Shape {
 3     constructor() {
 4         if (new.target === Shape) {
 5             throw new Error("这个类不能被直接实例化");
 6         }
 7     }
 8 }
 9 
10 class Rectangle extends Shape {
11     constructor(length, width) {
12         super();
13         this.length = length;
14         this.width = width;
15     }
16 }
17 
18 var x = new Shape(); // 抛出错误
19 
20 var y = new Rectangle(3,4); //没有错误
21 console.log(y instanceof Shape); // true

 

(本节完)

 

posted on 2019-09-12 11:23  张森ZS  阅读(196)  评论(0编辑  收藏  举报