JavaScript的"类"
1. 基本创建“类”方式
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 的实例做初始化 }; Person.find = function(id){ /*...*/ }; Person.prototype.breath = function(){ /*...*/ }; Person.fn = Person.prototype; Person.fn.run = function(){ /*...*/ }; // 用法 var person = new Person(); person.find(1); person.breath(); person.run();
2. 分辨类的静态属性和实例的属性
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; }; var Person = new Class(); // 添加静态属性 Person.extend({ find: function(id) { /* ... */ }, exists: function(id) { /* ... */ } }); //添加实例属性 Person.include({ save: function(id) { /* ... */ }, destroy: function(id) { /* ... */ } }); var person = new Person(); person.save();
在类之间共享通用的属性
var ORMModule = { save: function(){ // 共享的函数 } }; var Person = new Class(); var Asset = new Class(); Person.include(ORMModule); Asset.include(ORMModule);
JavaScript 是基于原型的编程语言,原型用来区别类和实例,这里提到一个概念:原型对象(prototypical object)。原型是一个“模板”对象,它上面的属性被用做初始化一个新对象。任何对象都可以作为另一个对象的原型对象,以此来共享属性。
当读取一个对象的属性时,JavaScript 首先会在本地对象中查找这个属性,如果没有找到,JavaScript 开始在对象的原型中查找,若还未找到还会继续查找原型的原型,直到查找到Object.prototype。如果找到这个属性,则返回这个值,否则返回undefined。
为了让子类继承父类的属性,首先需要定义一个构造函数。然后需要将父类的新实例赋值给构造函数的原型。
var Animal = function(){}; Animal.prototype.breath = function(){ console.log('breath'); }; var Dog = function(){}; // Dog 继承了Animal Dog.prototype = new Animal(); Dog.prototype.wag = function(){ console.log('wag tail'); }; var dog = new Dog(); dog.wag(); dog.breath(); // 继承的属性
3. 添加继承,通过传入一个可选的父类来创建新类
var Class = function(parent) { var klass = function() { this.init.apply(this, arguments); }; // 改变klass 的原型 只有实例的属性才会被继承,而非类的属性 if (parent) { var subclass = function() { }; subclass.prototype = parent.prototype; klass.prototype = new subclass; }; klass.prototype.init = function() {}; //定义 prototype 的别名 klass.fn = klass.prototype; // 定义类的别名 klass.fn.parent = klass; klass._super = klass.__proto__; // 给类添加属性 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; }; var Animal = new Class(); Animal.include({ breath: function(){ console.log('breath'); } }); var Cat = new Class(Animal); var tommy = new Cat(); tommy.breath();
apply() 函数有两个参数:第1 个参数是上下文对象,第2 个参数是参数组成的数组。如果上下文对象是null,则使用全局对象代替。
call() 函数的行为和apply() 函数类似,只是使用方法不一样。call() 的第1 个参数是上下文对象,后续是实际传入的参数序列。
functionName.apply(this, [1, 2, 3]); functionName.call(this, 1, 2, 3);
使用apply() 和call() 来更改上下文对象
//为了访问原始上下文对象,可以将this 的值存入一个局部变量中。 var clicky = { wasClicked: function(){ }, addListeners: function(){ var self = this; $('.clicky').click(function(){ self.wasClicked(); }); } }; clicky.addListeners(); //可以使用apply 来将这段代码变得更干净一些,通过将回调包装在另外一个匿名函数中,来保持原始的上下文 var proxy = function(func, thisObject){ return function(){ return func.apply(thisObject, arguments); }; }; var clicky = { wasClicked: function(){ }, addListeners: function(){ var self = this; $('.clicky').click(proxy(this.wasClicked, this)); } }; clicky.addListeners();
4. 控制“类”的作用域 在类和实例中都添加proxy函数
var Class = function(parent){ var klass = function(){ this.init.apply(this,arguments); } //继承 if (parent) { var subclass = function() { }; subclass.prototype = parent.prototype; klass.prototype = new subclass(); } klass.prototype.init = function(){}; klass.fn = klass.prototype; klass.fn.parent = klass; klass._super = klass.__proto__; //类 静态属性 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); } //类 作用域 klass.proxy = function(func){ var self = this; return(function(){ return func.apply(self, arguments); }); } klass.fn.proxy = klass.proxy; return klass; } var Button = new Class(); Button.include({ init: function(element){ this.element = jQuery(element); // 代理了这个click 函数 this.element.click(this.proxy(this.click)); }, click: function(){ } });