ES6实现继承
# class定义类
在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类;类本质上依然是构造函数、原型链的语法糖而已;
class Person{
}
// 表达式
var Student = class{}
var stu1 = new Student()
1
2
3
4
5
2
3
4
5
# 类构造函数以及类中的内容
class Person {
// 类中的构造方法
// new关键字调用Person实例,默认调用class中的constructor方法
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
running() {
console.log(this.name + "running");
}
}
var p1 = new Person("zs", 18);
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 类构造函数
- 每个类都有一个自己的构造函数(方法),这个方法的名称是固定的
constructor
- 当通过new操作符操作一个类的时候会调用这个类的构造函数
constructor
- 每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常
当通过new关键字操作类的时候,会调用这个constructor函数,并且执行以下操作
- 在内存中创建一个新的对象(空对象)
- 这个对象内部的
[[prototype]]
属性会被赋值为该类的prototype
属性 - 构造函数内部的this,会指向创建出来的新对象
- 执行构造函数内部的代码
- 如果构造函数没有返回非空对象,则返回创建出来的新对象
# class类和function类的区别
- 不同点:class定义的类,不能作为一个普通的函数进行调用
- 构造函数和class定义的类特性是一致的
# 类的访问器方法
# 对象访问器方法
// 方式一:通过描述符
var obj = {};
Object.defineProperty(obj, "name", {
configurable: true,
enumerable: true,
set: function () {},
get: function () {},
});
// 方式二:直接在对象定义访问器
var obj = {
_name: "zs",
set name(value) {
this._name = value;
},
get name() {
return this._name;
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 类访问器方法
// 类访问器
class Person {
// 默认约定:以_开头的属性和方法,是不允许在外界访问的
constructor(name, age) {
this._name = name;
}
set name(value) {
console.log("设置name属性");
this._name = value;
}
get name() {
console.log("获取name属性");
return this._name;
}
}
var p1 = new Person("zs", 18);
p1.name = "ls";
console.log(p1.name);
// console.log(p1._name);
// 访问器的应用场景
class Rectangle {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
get position() {
return { x: this.x, y: this.y };
}
get size() {
return { width: this.width, height: this.height };
}
}
var rect1 = new Rectangle(10, 20, 100, 200);
console.log(rect1.position);
console.log(rect1.size);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 类的静态方法
静态方法通常用于定义直接使用类执行的方法,不需要有类的实例,使用static
关键字来定义
// function Person() {}
// // 实例方法
// Person.prototype.running = function () {};
// // 类方法
// Person.randomPerson = function () {};
// var p1 = new Person();
// p1.running();
// Person.randomPerson();
// class定义类
class Person {
// 实例方法
running() {}
eating() {}
// 类方法(静态方法)
static randomPerson(){
// 类方法中的this直接指向 class Person{}
// return new Person() 等价于 new this()
}
}
var p1 = new Person()
p1.running()
p1.eating()
Person.randomPerson()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ES6 extends 实现继承
// 定义父类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
running() {
console.log("running~");
}
eating() {
console.log("eating~");
}
}
class Student extends Person {
constructor(name, age, sno, score) {
super(name, age);
this.sno = sno;
this.score = score;
}
studying() {
console.log("studying~");
}
}
var stu1 = new Student("zs", 18, 110, 80);
console.log(stu1.name);
stu1.running();
stu1.eating();
stu1.studying();
class Teacher {
constructor(name, age, title) {
super(name, age);
this.title = title;
}
teaching() {
console.log("teaching!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# super关键字
class提供了super关键字
- 执行super.method(...)来调用一个父类方法
- 执行super(...)来调用一个父类constructor
注意:在子(派生)类的构造函数中使用this或者默认返回对象之前,必须先通过super调用父类的构造函数
super的使用位置
- 子类的构造函数
- 实例方法
- 静态方法
class Animal {
running() {
console.log("running");
}
eating() {
console.log("eatting");
}
static sleep() {
console.log("static sleep");
}
}
class Dog extends Animal {
running() {
super.running();
}
static sleep() {
super.sleep();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 继承内置类
class MyArr extends Array {
// 扩展内置类方法
get lastItem() {
return this[this.length - 1];
}
}
1
2
3
4
5
6
2
3
4
5
6
# 类的混入
// JavaScript仅支持单继承(不支持多继承)
function mixinAnimal(BaseClass) {
return class extends BaseClass {
running() {
console.log("running~");
}
};
}
function mixinRunner(BaseClass) {
return class extends BaseClass {
flying() {
console.log("flying~");
}
};
}
class Bird {
eating() {
console.log("eating");
}
}
// var newBird = mixinRunner(mixinAnimal(Bird));
class newBirdexport extends mixinRunner(mixinAnimal(Bird)) {}
var bird = new newBird();
bird.running();
bird.flying();
bird.eating();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Babel将ES6代码转换为ES5代码
# JavaScript中的多态
继承是多态的前提;必须有父类引用指向子类对象
# 字面量增强
字面量增强包括以下部分
- 属性简写
- 方法简写
- 计算属性名
var name = "zs"
var age = 18
var key = 'address'
var obj = {
// 属性增强
name,
age,
// 方法增强
running: function(){}
// 这种方式是上面函数方式的增强,可以绑定this
swing(){}
// 箭头函数不绑定this
eatting: () => {}
// 计算属性名
[key]: '北京' // address: '北京'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 手写实现 apply, call, bind函数
# 手动实现apply函数
function foo(name, age) {
console.log(name, age);
}
Function.prototype.slApply = function (thisArg, otherArg) {
// 1. 获取thisArg,做边界判断,保证其是一个对象类型
thisArg =
thisArg === null || thisArg === undefined ? window : Object(thisArg);
// thisArg.fn = this
Object.defineProperty(thisArg, "fn", {
enumerable: false,
configurable: true,
value: this,
});
thisArg.fn(...otherArg);
delete thisArg.fn;
};
foo.slApply({ name: "zs", age: 18 }, ["ls", 20]);
foo.slApply(123, ["sd", 20]);
foo.slApply(null, ["ld", 20]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 手动实现call函数
function foo(name, age) {
console.log(name, age);
}
Function.prototype.slCall = function (thisArg, otherArg) {
// 1. 获取thisArg,做边界判断,保证其是一个对象类型
thisArg =
thisArg === null || thisArg === undefined ? window : Object(thisArg);
// thisArg.fn = this
Object.defineProperty(thisArg, "fn", {
enumerable: false,
configurable: true,
value: this,
});
thisArg.fn(...otherArg);
delete thisArg.fn;
};
foo.slCall({ name: "zs", age: 18 }, ["ls", 20]);
foo.slCall(123, ["sd", 20]);
foo.slCall(null, ["ld", 20]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# apply-call函数抽取封装
function foo(name, age) {
console.log(name, age);
}
// 封装
// 1. 封装到独立的函数中
function execFn(thisArg, otherArg, fn) {
// 1. 获取thisArg,做边界判断,保证其是一个对象类型
thisArg =
thisArg === null || thisArg === undefined ? window : Object(thisArg);
// thisArg.fn = this
Object.defineProperty(thisArg, "fn", {
enumerable: false,
configurable: true,
value: fn,
});
thisArg.fn(...otherArg);
delete thisArg.fn;
}
// 封装到原型上,封装到独立函数中如果单独存放到一个文件中需要导入使用,因此进一步优化封装到原型上
Function.prototype.slExecFn = function (thisArg, otherArg) {
// 1. 获取thisArg,做边界判断,保证其是一个对象类型
thisArg =
thisArg === null || thisArg === undefined ? window : Object(thisArg);
// thisArg.fn = this
Object.defineProperty(thisArg, "fn", {
enumerable: false,
configurable: true,
value: this,
});
thisArg.fn(...otherArg);
delete thisArg.fn;
};
Function.prototype.slCall = function (thisArg, otherArg) {
// execFn(thisArg, otherArg, this);
this.slExecFn(thisArg, otherArg);
};
Function.prototype.slApply = function (thisArg, otherArg) {
execFn(thisArg, otherArg, this);
};
foo.slCall({ name: "zs", age: 18 }, ["ls", 20]);
foo.slCall(123, ["sd", 20]);
foo.slCall(null, ["ld", 20]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
上次更新: 2022/11/22, 08:32:14