目录

ES6实现继承

# class定义类

在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类;类本质上依然是构造函数、原型链的语法糖而已;

class Person{
}
// 表达式
var Student = class{}
var stu1 = new Student()
1
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

# 类构造函数

  • 每个类都有一个自己的构造函数(方法),这个方法的名称是固定的constructor
  • 当通过new操作符操作一个类的时候会调用这个类的构造函数constructor
  • 每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常

当通过new关键字操作类的时候,会调用这个constructor函数,并且执行以下操作

  • 在内存中创建一个新的对象(空对象)
  • 这个对象内部的[[prototype]]属性会被赋值为该类的prototype属性
  • 构造函数内部的this,会指向创建出来的新对象
  • 执行构造函数内部的代码
  • 如果构造函数没有返回非空对象,则返回创建出来的新对象

# class类和function类的区别

  1. 不同点:class定义的类,不能作为一个普通的函数进行调用
  2. 构造函数和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

# 类访问器方法

// 类访问器
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

# 类的静态方法

静态方法通常用于定义直接使用类执行的方法,不需要有类的实例,使用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

# 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

# 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

# 继承内置类

class MyArr extends Array {
  // 扩展内置类方法
  get lastItem() {
    return this[this.length - 1];
  }
}
1
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

# 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

# 手写实现 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

# 手动实现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

# 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
上次更新: 2022/11/22, 08:32:14
最近更新
01
防抖和节流
02-06
02
正则表达式
01-29
03
async_await函数
12-30
更多文章>