公开记录学习JS MVC,不知道能坚持多久= =。以《基于MVC的JavaScript web富应用开发》为主要学习资料。
JavaScript类的内容好多,我脑容量太小,分两次学习
JavaScript的类
JavaScript是基于原型的语言,没有包含内置的类,但是通过JavaScript可以轻易地模拟出经典的类。JavaScript中有构造函数和 new 运算符。构造函数用来给实例对象初始化属性和值。任何JavaScript函数都可以用做构造函数,构造函数必须使用new 运算符作为前缀来创建新的实例。
new 运算符改变了函数的执行上下文,同时改变了return 语句的行为。实际上,使用new和构造函数和传统的实现了类的语言中的使用方法是很类似的:
var Person = function(name) {
this.name = name;
};
// 实例化一个Person
var alice = new Person('alice');
// 检查这个实例
assert( alice instanceof Person );
构造函数的命名通常使用驼峰命名法,首字母大写,以此和普通的函数区分开来,这是一种习惯用法。
不要省略new
前缀的方式来调用构造函数:
// 不要这么做!
Person('bob'); //=> undefined
这个函数只会返回undefined,并且执行上下文是window(全局)对象,你无意间创建了一个全局变量name。调用构造函数时不要丢掉new
关键字。
当使用new
关键字来调用构造函数时,执行上下文从全局对象(window)变成一个空的上下文,这个上下文代表了新生成的实例。因此,this
关键字指向当前创建的实例。尽管理解起来有些绕,实际上其他语言内置类机制的实现也是如此。默认情况下,如果构造函数中没有返回任何内容,就会返回this
——当前的上下文。要不然就返回任意非原始类型的值。
创建自己的类模拟库
var Class = function(){
var klass = function(){
this.init.apply(this, arguments);
};
klass.prototype.init = function(){};
return klass;
};
var Person = new Class;
Person.prototype.init = function(){
// 基于Person 的实例做初始化
};
// 用法:
var person = new Person;
由于 JavaScript 2 规范从未被实现过,
class
一直都是保留字。最常见的做法是将变量名class
改为_class
或klass
。
给类添加函数
在JavaScript 中,在构造函数中给类添加函数和给对象添加属性是一样的:
Person.find = function(id){ /*...*/ };
var person = Person.find(1);
要想给构造函数添加实例函数,则需要用到构造函数的prototype :
Person.prototype.breath = function(){ /*...*/ };
var person = new Person;
person.breath();
一种常用的模式是给类的 prototype 起一个别名fn,写起来也更简单:
Person.fn = Person.prototype;
Person.fn.run = function(){ /*...*/ };
这种模式在jQuery 的插件开发中是很常见的,将函数添加至jQuery.fn 中也就相当于添加至jQuery 的原型中。
给实现了类机制的类库添加方法
给类添加属性和给构造函数添加属性是一样的。
直接给类设置属性和设置其静态成员是等价的:
var Person = new Class;
// 直接给类添加静态方法
Person.find = function(id){ /* ... */ };
// 这样我们可以直接调用它们
var person = Person.find(1);
给类的原型设置的属性在类的实例中也是可用的:
var Person = new Class;
// 在原型中定义函数
Person.prototype.save = function(){ /* ... */ };
// 这样就可以在实例中调用它们
var person = new Person;
person.save();
这样很难一眼就分辨出类的静态属性和实例的属性。因此我们采用另外一种不同的方法来给类添加属性,这里用到了两个函数extend()
和include()
:
var Class = function () {
var klass = function () {
this.init.apply(this, arguments);
};
klass.prototype.init = function () {};
// 定义 prototype 的别名
klass.fn = klass.prototype;
// 定义类的别名
klass.fn.parent = klass;
// 给类添加属性
klass.extend = function (obj) {
var extended = obj.extended;
for (var i in obj) {
klass[i] = obj[i];
}
if (extended)
extended(klass)
};
// 给实例添加属性
klass.include = function (obj) {
var included = obj.included;
for (var i in obj) {
klass.fn[i] = obj[i];
}
if (included)
included(klass)
};
return klass;
};
这段代码使用extend() 函数来生成一个类,这个函数的参数是一个对象。通过迭代将对象的属性直接复制到类上:
var Person = new Class;
Person.extend({
find: function(id) { /* ... */ },
exists: functions(id) { /* ... */ }
});
var person = Person.find(1);
include() 函数的工作原理也一样,只不过不是将属性复制至类中,而是复制至类的原型中。换句话说,这里的属性是类实例的属性,而不是类的静态属性。
var Person = new Class;
Person.include({
save: function(id) { /* ... */ },
destroy: functions(id) { /* ... */ }
});
var person = new Person;
person.save();
同样地,这里的实现支持extended 和included 回调。将属性传入对象后就会触发这两
个回调:
Person.extend({
extended: function(klass) {
console.log(klass, " was extended!");
}
});
这种写法已经可以支持模块了。模块是可重用的代码段,用这种方法可以实现各种继承,用来在类之间
共享通用的属性。
var ORMModule = {
save: function(){
// 共享的函数
}
};
var Person = new Class;
var Asset = new Class;
Person.include(ORMModule);
Asset.include(ORMModule);