继承(Inheritance)

伪类(Pseudoclassical)

当一个函数对象被创建时,这个函数的构造器会产生一个函数对象运行类似下面的代码:

1
this.prototype = {constructor: this};

例子,定义一个构造器,并在prototype上添加方法

1
2
3
4
5
6
7
8
9
var Mammal = function (name) { 
	this.name = name;
};
Mammal.prototype.get_name = function () { 
	return this.name;
};
Mammal.prototype.says = function () { 
	return this.saying || '';
};

创建实例

1
2
var myMammal = new Mammal('Herb the Mammal');
var name = myMammal.get_name(); // 'Herb the Mammal'

创建另一个伪类继承Mammal,通过定义该伪类的constructor函数并且替换它的prototype属性为Mammal的实例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
var Cat = function (name) { 
	this.name = name; 
	this.saying = 'meow';
};
// Replace Cat.prototype with a new instance of Mammal
Cat.prototype = new Mammal();
// Augment the new prototype with purr and get_name methods.
Cat.prototype.purr = function (n) { 
	var i, s = '';
	for (i = 0; i < n; i += 1) { 
		if (s) {
			s += '-'; 
		}
		s += 'r'; 
	}
	return s; 
};
Cat.prototype.get_name = function () {
	return this.says() + ' ' + this.name + ' ' + this.says();
};
1
2
3
4
var myCat = new Cat('Henrietta');
var says = myCat.says(); // 'meow'
var purr = myCat.purr(5); // 'r-r-r-r-r' 
var name = myCat.get_name(); // 'meow Henrietta meow'

使用前面定义的method方法定义一个inherits方法:

1
2
3
4
Function.method('inherits', function (Parent) { 
	this.prototype = new Parent();
	return this;
});

因为inheritsmethod方法都返回this,我们可以通过及联调用的方式实现Cat

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
var Cat = function (name) { 
this.name = name; 
this.saying = 'meow';
}.inherits(Mammal)
.method('purr', function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
	if (s) {
		s += '-';
	}
	s += 'r'; 
}
return s; 
})
.method('get_name', function () {
return this.says() + ' ' + this.name + ' ' + this.says();
});

伪类继承存在的问题

  • 没有私有属性/方法(JavaScript所有属性都是公有的)。
  • 不能访问父类的方法。
  • 如果调用的时候忘记了new(没有以构造函数的方式调用),则this不能被绑定到新创建的对象实例(this将会被绑定到全局对象)。

对象说明符(Object Specifiers)

当一个构造器有非常多的参数时,参数的顺序会变的非常难记,这时候可以通过对象描述符代替。

例如:

1
var myObject = maker(f, l, m, c, s);

可以写为:

1
2
3
4
5
6
7
var myObject = maker({
	first: f,
	last: l,
  middle: m,
  state: s,
  city: c
});

原型(Prototypal)

基于原型的继承方式比经典的继承在概念上更为简单,并且更容易理解。

示例

1
2
3
4
5
6
7
8
9
var myMammal = {
	name : 'Herb the Mammal', 
	get_name : function () {
		return this.name; 
	},
	says : function () {
		return this.saying || '';
	} 
};

创建对象实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
var myCat = Object.create(myMammal); 
myCat.name = 'Henrietta'; 
myCat.saying = 'meow';
myCat.purr = function (n) {
	var i, s = '';
	for (i = 0; i < n; i += 1) {
		if (s) {
			s += '-';
		}
		s += 'r'; 
	}
	return s; 
};
myCat.get_name = function () {
	return this.says() + ' ' + this.name + ' ' + this.says();
};

函数化(Functional)

通过模块化的方式实现封装,可通过如下4步实现

  1. 创建一个对象,创建对象的方式:

    • 字面符
    • 通过new调用构造器
    • 从一个已经存在的对象使用Object.create方法新建一个实例
    • 调用一个函数返回一个对象
  2. 定义私有的实例变量和方法(可选

  3. 为创建的对象增加方法(这鞋方法有权限去访问底2步中定义的变量/方法)

  4. 返回新建的对象。

伪代码如下:

1
2
3
4
5
6
7
8
var constructor = function (spec, my) {
	var that, other private instance variables; 
	my = my || {};
	Add shared variables and functions to my 
	that = a new object;
	Add privileged methods to that
	return that;
};

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var mammal = function (spec) {
	var that = {};
	that.get_name = function () { 
		return spec.name;
	};
	that.says = function () { 
		return spec.saying || '';
	};
  return that;
};
var myMammal = mammal({name: 'Herb'});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
var cat = function (spec) {
	spec.saying = spec.saying || 'meow'; 
	var that = mammal(spec);
	that.purr = function (n)
		var i, s = '';
		for (i = 0; i < n; i
			if (s) {
				s += '-';
			}
			s += 'r'; 
		}
		return s; 
	};
	that.get_name = function(){
		return that.says() + ' ' + spec.name + ' ' + that.says();
	};
	return that;
};
var myCat = cat({name: 'Henrietta'});

访问父类方法

1
2
3
4
5
6
7
Object.method('superior', function (name) { 
	var that = this,
	method = that[name]; 
	return function () {
		return method.apply(that, arguments); 
	};
});

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var coolcat = function (spec) { 
	var that = cat(spec),
			super_get_name = that.superior('get_name'); 
	that.get_name = function (n) {
		return 'like ' + super_get_name() + ' baby'; };
    return that;
  };  
var myCoolCat = coolcat({name: 'Bix'}); 
var name = myCoolCat.get_name();
// 'like meow Bix meow baby'