1. 类定义
- 类的定义分为类声明和类选表达式
- 类表达式在它们被求值前不能引用
- 类定义与函数定义的区别: 函数表达式可以提升, 类定义不能
- 另一个不同点, 函数受函数作用与限制; 而类受块作用域限制 ```javascript // 类声明 class Animal {}
// 类表达式 const animal = class {}
// 变量的提升 console.log(fn) // undefined var fn = function () {}
console.log(c) // 报错 var c = class {}
// 另一个不同点, 函数受函数作用与限制; 而类受块作用域限制 { function fn () {} class ClassName {} } console.log(fn) // 能够打印出fn console.log(ClassName) // 出错
**类构成**
```javascript
// 空类定义,有效
class Foo {}
// 有构造函数的类 有效
class Bar {
constructor() {}
}
// 有获取函数的类 有效
class Bar {
get myBzr () {}
}
// 有静态方法的类 有效
class Qux {
static myQux () {}
}
2. 类的构造函数
类的构造函数: constructor()
用在类内部创建类的构造函数。 constructor()
在使用 new
操作符创建类的实例时调用。
实例化:
(new 的实例化过程很重要)
讲了使用 new
操作符实例化后的操作; 意味着会跟着调用 constructor()
构造函数;
new
调用类的构造函数会执行以下操作
- 在内存种创建一个新对象
- 这个对象内部的
[[Prototype]]
指针被赋值为构造函数(constructor
)的prototype
(原型) 属性 - 构造函数
constructor
内部的this
被赋值给这个新对象 (即 this 指向新对象) - 执行构造函数
constructor
内部的代码 (给新对象添加属性) - 如果构造函数返回非空对象, 则返回该对象; 否则,返回刚创建的对象
- 在类实例化时,可以传入参数作为构造函数的参数。
- 可以使用
instanceof
检测实例 与类是否具有关联 - 类的构造函数
class Animal {}
与 构造函数function Person () {}
的区别- 调用类构造函数必须使用
new
操作符, 不使用new
操作符会报错 - 而普通构造函数不适用
new
调用, 则它们就以全局的this
(通常时window) 作为内部对象
- 调用类构造函数必须使用
class Person {
constructor(name) {
this.foo = 'foo';
if (name) {
return {
bar: 'bar'
}
}
}
}
let p1 = new Person(),
p2 = new Person('张三');
console.log(p1) // Person { foo: 'foo' }
console.log(p1 instanceof Person) // true
console.log(p2) // { bar: 'bar' } 返回的只是一个普通的对象
console.log(p2 instanceof Person) // false
//
function Person () {}
class Animal {}
let p = Person(); // 把window 作为 this 来构建实例
let a = Animal(); // 报错
把类当成一个特殊的函数
- 类的本质就是一个 函数 ;使用
typeof
检测返回function
- 类表示符有
prototype
属性,而这个原型也有一个constructor
属性指向类自身 - 可以使用
instanceof
检查构造函数原型是否存在于实例的原型链中 - 类于立即调用函数表达式相似,类也可以立即实例化 ```javascript }class Person {}; console.log(typeof Person); // function
// 第二点 let person = new Person();
console.log(Person.prototype); // 指向 constructor 类的构造函数 console.log(Person === Person.prototype.constructor); //true
// 类也可以立即实例化 let p = new class Foo { constructor(x) { console.log(x) } }(“这是传入的参数”)
<a name="PwbIn"></a>
## 3. 实例、原型、类成员
<a name="sDvZm"></a>
#### 实例成员
- 创建出来的实例_相互独立,实例不会再原型上共享属性_
```javascript
// ● 创建出来的实例相互独立,实例不会再原型上共享属性
class Person {
constructor() {
this.name = new String('Jack');
this.sayName = () => console.log(this.name)
}
}
let p1 = new Person(),
p2 = new Person();
console.log(p1.name === p2.name) // false
console.log(p1.sayName === p2.sayName) // false
// 说明了不同实例 相互独立
原型方法于访问器
- 定义属性或方法时,在类中 定义在
constructor()
构造函数中,和类本身
是不一样的- 定义在类本身, 都会出现在 类的原型上
- 定义在构造函数中, 都会出现在不同的实例上
- 不能直接在类中添加原始值, 不然会报错
- 类方法等同于对象属性, 可以使用
字符串、符号、计算的值
作为键- 例如类的方法名
[symbolKey](){}
['computed' + 'Key']() {}
- 例如类的方法名
- 类定义同样支持获取设置访问器, 设置访问器
getter | setter
```javascript class Person { constructor() { // 定义在实例上 this.locate = () => console.log(‘constructor’); }
// 定义在类的原型上 locate() { console.log(‘prototype’); } }
let p1 = new Person(), p2 = new Person();
console.log(p1) console.log(p1.locate()) // constructor
console.log(Person.prototype.locate()) // prototype
// ● 不能直接在类中添加原始值
class Person { name: ‘Yellowsea’ } // 这样会报错
// ● 类定义同样支持获取设置访问器, 设置访问器 getter | setter class Person { … get name () { return this.name } set name (Val) { this.name = Val } }
<a name="kkAUx"></a>
#### 静态类方法
静态方法, 在类中使用 `static` 关键字定义静态方法或静态属性
- 静态成员每一个类上只能有一个
- 静态成员必须要使用`static`关键字作为前缀
- 静态方法只能通过类本身来访问
- 子类通过 `super().xxx()`调用静态方法
- 在类外部添加数据成员 或者 在原型上定义数据成员
```javascript
class Person {
static sayHello() {
console.log('Hello');
}
}
// 通过 Person访问
Person.sayHello(); // Hello
// ● 在类外部添加数据成员 或者 在原型上定义数据成员
class Person {
sayName() {
console.log(`${Person.name} is ${Person.greeting}`);
}
}
Person.greeting = 'My name is';
Person.name = 'Red';
const p = new Person();
p.sayName() // Person is My name is
4. 继承
ES6新增的原型继承机制,原理同样使用的是 原型链
继承基础
- 继承使用的是
extends
关键字,可以继承任何拥有[[Construct]]
和原型链的对象 - 继承不仅可以继承类, 同样可以继承普通的构造函数
class Vehicle {}
class Bus extends Vehicle {}
let b = new Bus();
console.log(b instanceof Bus); // true
console.log(b instanceof Vehicle); // true
// 继承普通构造函数
function Person() {}
class Engineer extends Person {}
console.log(new Engineer() instanceof Person); // true
构造函数和、super()
- 派生类通过使用
super
关键字引入它们的原型。super
关键字只能在派生类中使用 - 在类构造函数中使用
super
可以调用父类构造函数 super
的作用,简单来说在继承的类构造函数类中调用父类的构造函数- 在父类定义的静态方法,同样可以使用
super().xxx
进行调用 ```javascript
class Vehicle { constructor() { this.hasEngine = true; } }
class Bus extends Vehicle { constructor() { // 不要在调用 super() 之前引入 this,否则会报错。 super(); // 执行 super() 后, 相当于调用了 Vehicle 的构造函数。给属性赋值 console.log(this instanceof Vehicle); // true console.log(this); // Bus { hasEngine: true } // 此时的this指向的是Bus } }
new Bus();
```
super的几个注意事项
super
只能在派生类的构造函数和静态方法中使用- 不能单独使用
super
,要么用它调用构造函数,要么用它引用静态方法 - 调用
super()
会调用父类的构造函数, 并将返回的实例赋值给this
super
如果调用构造函数,如果需要给父类构造函数传参,需要手动设置传入参数- 如果没有定义类的构造函数, 在实例化派生类时会调用
super()
, 而且传入的参数会传给父类的构造函数 - 在类构造函数中, 不能在调用
super()
之前引用this
- 如果派生类中显示定义了构造函数,则要么必须在其中调用
super()
, 要么必须在其中返回一个对象