实现类的步骤
- 第一步是使用
Class.create
新建类,初始化的固定函数是initialize
,不能使用其它名称 - 子类也是使用
Class.create
新建,父类放在第一个参数中,如var Cat = Class.create(Animal,{})
; - 子类中与父类的同名方法,如果需要在父类的同名方法上拓展,在需要在第一个参数中使用
$super
,然后在方法体内使用$super(args)
; - 如果需要在类的外面增加方法,可以使用
addMethods
方法
// 使用 Class.create 创建类
var Person = Class.create({
// 初始函数固定为 initialize, 如果不设置,会默认创建一个空函数给 initialize
initialize:function(name) {
this.name = name;
this.friends = ['jack', 'mark'];
},
getName: function(){
console.log("My name is " + this.name);
},
setFriends:function(friend){
this.friends.push(friend);
},
getFriends:function(){
console.log(this.friends)
}
});
// 使用 addMethods 给在类的初始构建之外添加方法,子类可以继承该方法
Person.addMethods({
getAge:function(age){
console.log("My age is " + age);
}
})
// 子类通过 Class.create 创建类,第一个参数为父类的名字
var Chinese = Class.create(Person,{
// 使用 $super 为第一个参数,表示当前函数在父类的同名函数上拓展
initialize:function($super, name, addr){
$super(name);
this.addr = addr;
},
getAddr:function(){
console.log("My address is " + this.addr);
}
});
var Japanese = Class.create(Person, {
initialize:function($super, name){
$super(name);
}
})
// 实例化类
var men = new Chinese('allen', 'BeiJing');
men.getName(); // My name is allen
men.getAge(23); // My age is 23
men.getAddr(); // My address is BeiJing
// 以下验证 - 子类继承父类的属性,修改了之后,其他子类再次继承父类,父类的属性的值为何不会改变
var allen = new Person();
allen.getFriends(); // ["jack", "mark"]
var women = new Japanese();
women.setFriends("lisa");
women.getFriends(); // ["jack", "mark", "lisa"]
var men = new Chinese();
men.setFriends('peter');
men.getFriends(); //["jack", "mark", "peter"]
var wallen = new Person();
wallen.getFriends(); //["jack", "mark"]
JS
是如何实现类的方法,有几个重要的问题需要搞清楚
-
JS
是如何创建类的 - 子类是如何实现继承父类属性和方法的
- 子类继承父类的属性,修改了之后,其他子类再次继承父类,父类的属性的值为何不会改变
- 子类和父类的同名函数,在同名函数中使用
$super
,是如何做到在子类中共存的 - 如何实现,不在类中,而是使用
addMethods
往类中添加方法的
下面来通过prototype.js
的class
来具体分析
var Class = (function() {
function subclass() {};
function create() {
// ...
}
function addMethods(source) {
// ...
}
// 暴露给外部的接口
return {
create: create,
Methods: {
addMethods: addMethods
}
};
})();
内部实现其实很简单,Class
是一个立即执行函数,里面只有三个函数,而且subclass
还是个空函数
/* Based on Alex Arnell's inheritance implementation. */
/**
* Refer to Prototype's web site for a [tutorial on classes and
* inheritance](http://prototypejs.org/learn/class-inheritance).
**/
var Class = (function() {
function subclass() {};
function create() {
// $A 函数就是把参数转化成数组
var parent = null, properties = $A(arguments);
// 如果第一个参数是函数,就把他当作父类
if (Object.isFunction(properties[0]))
parent = properties.shift();
function klass() {
// klass 是新建的类,把传入的参数都绑定到 klass 的 initialize 方法中
this.initialize.apply(this, arguments);
}
// 把通过 extend 方法,把 Class.Methods 的方法添加到 klass 中
Object.extend(klass, Class.Methods);
// 这里有指定 klass 的父类是哪一个
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
// 这里通过把父类的原型方法,都继承到当前类中
// 通过中间变量 subclass 来传递 prototype 来防止由于子类的修改而导致父类的属性或者方法也被修改
subclass.prototype = parent.prototype;
// 每次子类都会创建一个新的中间变量来传递,所以无论子类怎么修改属性,都不会影响到父类
klass.prototype = new subclass;
// 把当前类添加到父类的子类中
parent.subclasses.push(klass);
}
for (var i = 0, length = properties.length; i < length; i++)
// 前面把 addMethods 方法添加到 klass 中,这里就可以使用 addMethods 把传入参数中的方法,添加到 klass 中了
klass.addMethods(properties[i]);
// 如果 klass 没有初始化函数,就设置一个空函数
if (!klass.prototype.initialize)
klass.prototype.initialize = Prototype.emptyFunction;
// 把 klass 的构造函数指向自身
klass.prototype.constructor = klass;
return klass;
}
// source 是所有要添加进来方法的集合
function addMethods(source) {
var ancestor = this.superclass && this.superclass.prototype,
properties = Object.keys(source);
for (var i = 0, length = properties.length; i < length; i++) {
// value 就是单个的方法
var property = properties[i], value = source[property];
// 如果参数中的第一个参数是 $super,就需要把父类的同名方法,传递进来
if (ancestor && Object.isFunction(value) &&
value.argumentNames()[0] == "$super") {
// 把最初的 value 使用 method 存起来
var method = value;
// 继承父类的同名方法,然后把当前参数传进去
value = (function(m) {
return function() { return ancestor[m].apply(this, arguments); };
})(property).wrap(method);
// wrap 是把父类的同名方法,添加当前类的同名方法中
// We used to use `bind` to ensure that `toString` and `valueOf`
// methods were called in the proper context, but now that we're
// relying on native bind and/or an existing polyfill, we can't rely
// on the nuanced behavior of whatever `bind` implementation is on
// the page.
//
// MDC's polyfill, for instance, doesn't like binding functions that
// haven't got a `prototype` property defined.
// 将 valueOf 的方法绑定到 method 中
value.valueOf = (function(method) {
return function() { return method.valueOf.call(method); };
})(method);
// 将 toString 的方法绑定到 method 中
value.toString = (function(method) {
return function() { return method.toString.call(method); };
})(method);
}
this.prototype[property] = value;
}
return this;
}
// 暴露给外部的接口
return {
create: create,
Methods: {
addMethods: addMethods
}
};
})();
上面使用到的wrap
函数,摘抄在下面
function wrap(wrapper) {
var __method = this;
return function() {
var a = update([__method.bind(this)], arguments);
return wrapper.apply(this, a);
}
}
// 这里就是把 args 中的属性,都添加到 array 中
function update(array, args) {
var arrayLength = array.length, length = args.length;
while (length--) array[arrayLength + length] = args[length];
return array;
}
JS
面向对象系列
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。