认真做技术笔记

JS 的原型与继承

JS 是一门动态语言,没有类。但需要实现自己的继承,即原型继承。JS 对象的一些属性和方法都继承自别的对象。

除了 null 和 undefined 之外所有数据类型都有 proto 属性,该属性所指的对象即是对象的原型。

原型链

构造函数,是一个普通函数,函数名首字母大写,内部使用 this,生成实例需要使用 new 关键字,且 this 将绑定在实例对象上。

1
2
3
4
5
6
7
8
9
10
11
function Person(name) {
this.name = name // this 代表创建的实例对象
this.action = function() {
console.log('Think different')
}
}
let person = new Person('iron')
console.log(person.constructor === Person) // true
console.log(person instanceof Person) // true
console.log(person.name) // iron
person.action() // Think different

缺点,每个实例有自己的属性和方法,无法共享。因此给构造函数设置 prototype 属性,存储需要共享的属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(name) {
this.name = name // this 代表创建的实例对象
}
Person.prototype = {
action() {
console.log('Think different')
}
}
let person1 = new Person('iron')
let person2 = new Person('stark')
person1.action() // Think different
person2.action() // Think different
console.log(person1.hasOwnProperty('name')) // true
console.log(person1.hasOwnProperty('action')) // false

每个对象都有一个指向其原型的属性,原型的终点是 null。

class

ES6 引入 class 来定义类,是语法糖。

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
class Person {
constructor(name) {
this.name = name
}
action() {
console.log(`${this.name}, think different`)
}
get getName() {
return this.name
}
set setName(v) {
this.name = v
}
static say() { // 相当于 Person.say = function() {}
console.log(`I'm person`)
}
}
let person1 = new Person('iron')
console.log(person1.hasOwnProperty('name')) // true,person1 就是构造函数中的 this
console.log(person1.hasOwnProperty('action')) // false,action 定义在类上而非构造函数上
person1.action() // iron, think different,类的所有方法都定义在 prototype 上,在实例上调用等同于在原型上调用
console.log(typeof Person) // function
console.log(Person === Person.prototype.constructor) // true,类指向构造函数
console.log(person1.constructor === Person.prototype.constructor) // true
person1.setName = 'stark'
console.log(person1.getName) // stark
console.log(person1.name) // stark
Person.say() // 直接在类上调用,若方法体内含 this,则 this 指向类而非实例。目前 ES6 只支持静态方法不支持静态属性,可减少内存占用

继承

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
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
static say() {
return `I'm static Person`
}
say() {
return `I'm Person`
}
}
class Man extends Person {
constructor(name, age) {
super(name, age) // 先构造父类的 this
this.gender = 'male' // 再构造子类
}
static say() {
return super.say() // 调用父类的静态方法
}
sayGender() {
return `${super.say()}, and I'm a ${this.gender}` // super 调用父类的原型对象
}
}
let c = new Man('tr', 30)
console.log(Man.say()) // I'm static Person
console.log(c.say()) // I'm Person,可以使用父类的方法
console.log(c.sayGender()) // I'm Person, and I'm male
console.log(Person.__proto__ === Function.prototype) // true
console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Man.__proto__ === Person) // true
console.log(Man.prototype.__proto__ === Person.prototype) // true,子类的原型对象是父类的原型对象的实例