继承
在那些基于类的语言, 继承是一种代码重用的形式, 如果一个新的类与一个已存
在的类拥有大部分相同的功能, 那么就只需要说明两者之间的区别即可. 但是
JavaScript 并没有类的概念, 但是有很多代码重用的模式, 它可以模拟类的
模式, 也可以支持其他的模式.
伪类
在 C++ 和 Java中都是用 new 命令来生成示实例, 在使用 new 命令时都会调用类的
构造函数(constructor), 因此 Brendan Eich ( JavaScript 作者)将 new 引入了 JavaScript,
从原型对象上生成一个实例对象. 因为 js 没有类, 所以在 new 命令后面跟的是构造函数,
而不是像 Java 一样跟的是类. 简单的来说就是通过构造器生产对象.
1 | // 先扩展一个方法, 懒得每次打prototype |
伪类模式在通过new 命令生产对象时, 会产生内存浪费. 如上面的例子, Foo 构造函数
就回去重复构造器 Bar 已经完成的工作.
原型
在一个纯粹的原型模式中, 将摒弃类专注于对象. 基于原型的继承相比于类的
继承在概念上更为简单: 一个新对象可以继承一个旧对象的属性. 通过构造一个有用
的对象开始, 接着可以构造出更多和这个对象相似的对象. —-<JavaScript语言精粹>
1 | // 先构造一个基础对象 |
这是一种’差异化继承’, 通过制定一个新的对象, 指明它与基本对象的不同.
函数化
以上两种继承模式都没有实现私有化, 也就是说所有的变量和方法都是公开的, 所以就
可以开始运用模块模式. 这个函数主要分为四个步骤:
创建一个新的对象
定义私有属性.
给这个新对象扩充特权函数 (暴露接口)
返回这个对象
1 | // spec 对象包含构造器所需要的所有信息 |
语言精粹里的例子
构造器
1
2
3
4
5
6
7
8
9
10const mammal = spec => {
let that = {};
that.get_name = _ => spec.name;
that.says = _ => spec.saying || '';
return that
}
let myMammal = mammal({name: 'Herb'});另一个构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24let cat = spec =>{
spec.saying = spec.saying || 'meow';
// 继承
let that = mammal(spec);
that.purr = n => {
let i, s = '';
for (i = 0; i < n; i++) {
if (s) {
s += '-';
}
s += 'r';
}
return s
};
that.get_name = _ => {
return that.says() + ' ' + spec.name + ' ' + that.says();
}
return that
}
let myCat = car({name: 'Henrietta'});
超类
以上函数化的方式还不能够调用父类的方法并向父类方法传递参数. 以下是测试代码
在语言精粹
的例子的基础上稍作改动, 便于自己理解:
1 | // 构造器 |
最后随便提一下
箭头函数的几个使用注意点:
函数体内的
this
对象, 就是定义时所在的对象, 而不是使用时所在的对象.不可以当作构造函数, 也就是说, 不可以使用new命令, 否则会抛出一个错误.
因为箭头函数没有自己的this
, 而是继承外层函数的this
.不可以使用
arguments
对象, 该对象在函数体内不存在.不可以使用
yield
命令, 因此箭头函数不能用作Generator函数.
Ceated on 2017-9-15 by Cara