在改章节中,我们重要介绍继承类的内容,自我感觉有个不错的建议和大家分享下
javascript是个入门槛门很低的语言,甚至一个从来没有触接过javascript的技术人员,几小时内就能够写出一个简略有效的程序代码。
但是如果因此你就下论断:javascript是门简略的语言。那你就大错特错了。想写出高性能的代码,一样须要具有一个高等程序员的基本素养。
一个java或者c++程序员,纷歧定能写出高性能的javascript代码,但更易容写出高性能的javascript代码。
javascript的简略是基于它“襟胸辽阔”的包容性。它声明时,不须要指定型类,甚至可以意任的换转型类。它面向对象,却没有类(Class)的制限。它是一门崇尚由自又非常谨严的语言,如果你是一个由自主义者,那么,拥抱javascript吧!
面向对象编程 (OOP) 是一种风行的编程方法。但javascript的OOP,较之JAVA、c++有很大的同,重要表现它的继承式方不同。javascript是基于原型PROTOTYPE继承的。有所对象都是基于原型链,终最追述到Object对象。
这里不想论讨过量的关于javascript的继承式方和其它语言的继承式方的不同之处。重要论讨如何封装javascript的Class,以便更好的理管和维护础基代码,增加重复代码,以及更好的模块化编程。
面下是几个github上找到的较比好的Class封装类库:
一、MY-CLASS
目项址地:https://github.com/jiem/my-class
先看基本用法:
a、建新一个类
(function() { // 建新类 var Person = my.Class({ // 加添静态方法 STATIC: { AGE_OF_MAJORITY: 18 }, // 结构函数 constructor: function(name, age) { this.name = name; this.age = age; }, // 实例方法 sayHello: function() { console.log('Hello from ' + this.name + '!'); }, // 实例方法 drinkAlcohol: function() { this.age < Person.AGE_OF_MAJORITY ? console.log('Too young! Drink milk instead!') : console.log('Whiskey or beer?'); } }); // 暴露给命名空间 myLib.Person = Person; })(); var john = new myLib.Person('John', 16); john.sayHello(); //log "Hello from John!" john.drinkAlcohol(); //log "Too young! Drink milk instead!"
(function() { //Dreamer 继承 Person var Dreamer = my.Class(Person, { // 结构方法 constructor: function(name, age, dream) { Dreamer.Super.call(this, name, age); this.dream = dream; }, // 实例方法 sayHello: function() { superSayHello.call(this); console.log('I dream of ' + this.dream + '!'); }, // 实例方法 wakeUp: function() { console.log('Wake up!'); } }); // Super拜访类父 var superSayHello = Dreamer.Super.prototype.sayHello; // 暴露给全局命名空间 myLib.Dreamer = Dreamer; })(); var sylvester = new myLib.Dreamer('Sylvester', 30, 'eating Tweety'); sylvester.sayHello(); //log "Hello from Sylvester! I dream of eating Tweety!" sylvester.wakeUp(); //log "Wake up!"
// 给myLib.Dreamer加添新方法 my.extendClass(myLib.Dreamer, { // 加添静态方法 STATIC : { s_dongSomeThing : function(){ console.log("do some thing!"); } }, // 加添实例方法 touchTheSky: function() { console.log('Touching the sky'); }, // 加添实例方法 reachTheStars: function() { console.log('She is so pretty!'); } });
// 声明一个新类 myLib.ImaginaryTraveler = my.Class({ travel: function() { console.log('Traveling on a carpet!'); }, crossOceans: function() { console.log('Saying hi to Moby Dick!'); } }); (function() { //Dreamer 继承 Person 现实 ImaginaryTraveler的方法 var Dreamer = my.Class(Person, ImaginaryTraveler, { // 结构方法 constructor: function(name, age, dream) { Dreamer.Super.call(this, name, age); this.dream = dream; } // ... }); // 暴露给全局命名空间 myLib.Dreamer = Dreamer; })(); var aladdin = new Dreamer('Aladdin'); aladdin instanceof Person; //true aladdin instanceof ImaginaryTraveler; //false aladdin.travel(); aladdin.wakeUp(); aladdin.sayHello();
var Person = my.Class({ //you can now call the constructor with or without new constructor: function(name, city) { if (!(this instanceof Person)) return new Person(name, city); this.name = name; this.city = citye; } });
my.Class现实路思基本是这样的,如果只有一个参数,那么声明的是一个础基类,这个参数是用来声明新类的方法和属以及结构函数。它不是继承而来,但它可以被继承。
继承的路思,就是如果有两个参数,第一个参数做为类父被继承,第二参数用来声明新类的方法和性属以及结构函数,它一样可以被继承。
如果有三个以上参数那么,除出第一个参数做为继承的类父,最后一个参数用声明新类的方法和性属以及结构函数。间中的参数是用类来扩展新类的方法。当然也可以通过my.extendClass扩展新方法。
同时,类库为commonJS和览浏环境都供提了持支!
/*globals define:true, window:true, module:true*/ (function () { // Namespace object var my = {}; // 保障AMD分模块可用 if (typeof define !== 'undefined') define([], function () { return my; }); else if (typeof window !== 'undefined') // 保障客户端可用 window.my = my; else // 保障后台可用 module.exports = my; //============================================================================ // @method my.Class // @params body:Object // @params SuperClass:function, ImplementClasses:function..., body:Object // @return function my.Class = function () { var len = arguments.length; var body = arguments[len - 1]; // 最后一个参数是指定本身的方法 var SuperClass = len > 1 ? arguments[0] : null; // 第一个参数是指继承的方法,实例和静态分部均继承 var hasImplementClasses = len > 2; // 如果有第三个参数,那么第二个就是implementClass,这里其实只继承实例对象 var Class, SuperClassEmpty; // 保障结构方法 if (body.constructor === Object) { Class = function() {}; } else { Class = body.constructor; // 保障面后不盖覆constructor delete body.constructor; } // 处置superClass分部 if (SuperClass) { // 间中件现实实例性属的继承 SuperClassEmpty = function() {}; SuperClassEmpty.prototype = SuperClass.prototype; Class.prototype = new SuperClassEmpty(); // 原型继承,消除引用 Class.prototype.constructor = Class; // 保障constructor Class.Super = SuperClass; // 父对象拜访接口 // 静态方法继承,重载superClass方法 extend(Class, SuperClass, false); } // 处置ImplementClass分部,其实只继承实例性属分部,除SuperClass #arguments[0]# 和 body #arguments[length-1]# if (hasImplementClasses) for (var i = 1; i < len - 1; i++) // implement是继承的实例性属分部, 重载父对象implementClass方法 extend(Class.prototype, arguments[i].prototype, false); // 处置本身声明body分部,静态要STATIC指定,实例分部要删除STATIC分部 extendClass(Class, body); return Class; }; //============================================================================ // @method my.extendClass // @params Class:function, extension:Object, ?override:boolean=true var extendClass = my.extendClass = function (Class, extension, override) { // 静态分部继承静态分部 if (extension.STATIC) { extend(Class, extension.STATIC, override); // 保障实例分部不继承静态方法 delete extension.STATIC; } // 实例性属继继承实例部 extend(Class.prototype, extension, override); }; //============================================================================ var extend = function (obj, extension, override) { var prop; // 其实这里的flase是标明,盖覆父对象的方法 if (override === false) { for (prop in extension) if (!(prop in obj)) obj[prop] = extension[prop]; } else { // 这里其实不盖覆父对象的方法,括包toString for (prop in extension) obj[prop] = extension[prop]; if (extension.toString !== Object.prototype.toString) obj.toString = extension.toString; } }; })();
二、KLASS
目项址地:https://github.com/ded/klass
先看应用方法:
a、建新一个类
// 声明一个类 var Person = klass(function (name) { this.name = name }) .statics({//静态方法 head: ':)', feet: '_|_' }) .methods({//实例方法 walk: function () {} })
// SuperHuman 继承 Person var SuperHuman = Person.extend(function (name) { // 动自调用类父的结构方法 }) .methods({ walk: function() { // 显式声明调用类父的walk方法 this.supr() this.fly() }, fly: function() {} }) new SuperHuman('Zelda').walk()
var Foo = klass({ foo: 0, initialize: function() { this.foo = 1 }, getFoo: function () { return this.foo }, setFoo: function (x) { this.foo = x return this.getFoo() } })
因为有时候你可能望希覆写或者混合一个实例方法,可以这样:
// 可以传递一个字面量去继承 var Alien = SuperHuman.extend({ beam: function() { this.supr() // beam into space } }) var Spazoid = new Alien('Zoopo') if (beamIsDown) { // 覆写beam方法 Spazoid.implement({ beam: function() { this.supr() // fallback to jets this.jets() } }) }
klass的基本计划路思很确明,极力的模拟其它语言的继承式方。比如:子类结构方法调用类父的结构方法,还可以显式的声明调用类父的方法。
这类断判都是基于正则匹配:fnTest = /xyz/.test(function () {xyz;}) ? /\bsupr\b/ : /.*/;关键字"super"
如果示显的声明白要调用类父的方法,那么声明方法的时候,就包装成一个部内调用类父方法且回返同相值的函数,给前当类的方法。
另一方面,结构方法,也是较比活灵的。如果示显的声明白initialize,那么这就是结构方法。否则如果参数是个function那么它就做为结构方法,否则就用类父的结构方法。
通过statics式方加添静态方法,通过实例的implements和静态方法methods加添实例方法。
通过类父的extend现实继承。
同时,类库为commonJS和览浏环境都供提了持支!
/** * Klass.js - copyright @dedfat * version 1.0 * https://github.com/ded/klass * Follow our software http://twitter.com/dedfat :) * MIT License */ !function (context, f) { // fnTest用来验证是不是可能通过正则找出调用super类父方法的方法 var fnTest = /xyz/.test(function () {xyz;}) ? /\bsupr\b/ : /.*/, noop = function (){}, proto = 'prototype', isFn = function (o) { return typeof o === f; }; // 础基类 function klass(o) { return extend.call(typeof o == f ? o : noop, o, 1); } // 包装成一个借用super同名方法的函数 function wrap(k, fn, supr) { return function () { // 缓存原this.super var tmp = this.supr; // 暂把this.super改成造借用super的同名方法above // 供o里显式的声明(fnTest.text(fn)==true)要借用super的同名方法应用 this.supr = supr[proto][k]; // 借用行执并保存回返值 var ret = fn.apply(this, arguments); // 恢恢复this.super this.supr = tmp; // 回返回返值,保障wrap后的回返值跟来原分歧 return ret; }; } // 如果o和super有同名方法,且o显式声明借用super的同名方法,就wrap成一个待行执函数供应用 // 如果没有显式的声明借用super的同名方法,或者是o有独的方法,或者不是方法就直接用 function process(what, o, supr) { for (var k in o) { // 如果非是继承方法,按方法注释规矩行执,终最都放进what if (o.hasOwnProperty(k)) { what[k] = typeof o[k] == f && typeof supr[proto][k] == f && fnTest.test(o[k]) ? wrap(k, o[k], supr) : o[k]; } } } // 继承方法的现实,fromSub是用来制控是不是继承而来,面下的klass面里fromSub是1,标明非继承而来,结构函数不借用super行执 function extend(o, fromSub) { // noop做为介媒类现实原型继承的消除引用 noop[proto] = this[proto]; var supr = this, prototype = new noop(), // 创立实例对象供原型继承应用,消除引用 isFunction = typeof o == f, _constructor = isFunction ? o : this,// 如果o是一个结构方法就用,否则由this来决议结构函数 _methods = isFunction ? {} : o, // 如果o是一个{...}应该用methods放到fn原型里,如果面里有initialize就是结构函数,如果o是函数就由面下_constructor决议o是结构函数 fn = function () { // 因为kclass借助了kclass,所以终最实际上回返的就是fn,fn其实就新类的结构函数 //1 如果o是{...}就会被methods直接过滤并加添到fn的原型里,如果o面里有initialize,那么fn的原型里就有initialize,那么它就是结构方法 //2 如果o是function,methods什么也加添不到fn的原型里,但是_constructor会受接o当结构函数 //3 如果o是{....},同时面里也没有initialize,那么就是this当结构函数,如果在klass里由call决议,明显结构函数是noop,如果在非础基类里,结构函数就是类父的结构函数 // 由于o不是函数不会动自调用类父的结构函数,只是把类父的结构函数做当前当类的结构函数----这都是由于this的指向决议的 console.log(this); if (this.initialize) { this.initialize.apply(this, arguments); } else { // 调用类父结构方法 // 如面下3,o不是函数,不会调用类父的结构方法 // 础基类无类父,不会调用类父结构方法 fromSub || isFn(o) && supr.apply(this, arguments); // 调用本类结构方法 // 参考面下2,3要么是noop要么是o console.log(_constructor==noop); _constructor.apply(this, arguments); } }; // 结构原型方法的接口 fn.methods = function (o) { process(prototype, o, supr); fn[proto] = prototype; return this; }; // 行执现实新类原型,保障新类的constructor fn.methods.call(fn, _methods).prototype.constructor = fn; // 保障新类可以被继承 fn.extend = arguments.callee; // 加添实例方法或者静态方法,statics:静态方法,implement实例方法 fn[proto].implement = fn.statics = function (o, optFn) { // 保障o是一个object对象,如果o是一个字符串,那么就是添一个方法的情况,如果o是一个object对象说明是批量加添的 // 因为要从o面里贝拷 o = typeof o == 'string' ? (function () { var obj = {}; obj[o] = optFn; return obj; }()) : o; // 加添实例方法或者静态方法,statics:静态方法,implement实例方法 process(this, o, supr); return this; }; return fn; } // 后台用,nodejs if (typeof module !== 'undefined' && module.exports) { module.exports = klass; } else { var old = context.klass; // 防突冲 klass.noConflict = function () { context.klass = old; return this; }; // 前台览浏器用 //window.kclass = kclass; context.klass = klass; } }(this, 'function');
现实路思很简略,就是利用ECMAScript 5 原型式继承Object.create方法,封装成一个方法,如果不持支ECMAScript5的环境,就平移退化到
function F() {}; F.prototype = superCtor.prototype; ctor.prototype = new F(); ctor.prototype.constructor = ctor;
var Class = (function() { /** * Inherits function.(node.js) * * @param ctor subclass's constructor. * @param superctor superclass's constructor. */ var inherits = function(ctor, superCtor) { // 显式的指定类父 ctor.super_ = superCtor; // ECMAScript 5 原型式继承并消除引用 if (Object.create) { ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); } else { // 无Object.create方法的稳平退化 function F() {}; F.prototype = superCtor.prototype; ctor.prototype = new F(); ctor.prototype.constructor = ctor; } }; /** * Class function. */ return function() { // 最后一个参数是新类方法、性属和结构函数声明 var subClazz = arguments[arguments.length - 1] || function() {}; // initialize是结构函数,否结构函数就是一个空函数 var fn = subClazz.initialize == null ? function() {} : subClazz.initialize; // 继承除最一个参数以的类,多继承,也可以用作扩展方法 for (var index = 0; index < arguments.length - 1; index++) { inherits(fn, arguments[index]); } // 现实新类的方法 for (var prop in subClazz) { if (prop == "initialize") { continue; } fn.prototype[prop] = subClazz[prop]; } return fn; } })();
/** * The definition of Cat Class. */ var Cat = Class({ /** * Constructor. * * @param name Cat's name */ initialize: function(name) { this.name = name; }, /** * Eat function. */ eat: function() { alert(this.name + " is eating fish."); } }); /** * The definition of Black Cat Class. */ var BlackCat = Class(Cat, { /** * Constructor. * * @param name Cat's name. * @param age Cat's age. */ initialize: function(name, age) { // call the constructor of super class. BlackCat.super_.call(this, name); this.age = age; }, /** * Eat function. */ eat: function() { alert(this.name + "(" + this.age + ") is eating dog."); } }); /** * The definition of Black Fat Cat Class. */ var BlackFatCat = Class(BlackCat, { /** * Constructor. * * @param name Cat's name. * @param age Cat's age. * @param weight Cat's weight. */ initialize: function(name, age, weight) { // call the constructor of super class. BlackFatCat.super_.call(this, name, age); this.weight = weight; }, /** * Eat function. */ eat: function() { alert(this.name + "(" + this.age + ") is eating dog. My weight: " + this.weight); } }); /** * The definition of Dog Class. */ var Dog = Class({}); var cat = new BlackFatCat("John", 24, "100kg"); cat.eat(); // true alert(cat instanceof Cat); // true alert(cat instanceof BlackCat); // true alert(cat instanceof BlackFatCat); // true alert(cat.constructor === BlackFatCat); // false alert(cat instanceof Dog);
码源剖析可以看这里:http://www.cnblogs.com/hmking/archive/2011/09/30/2196504.html
看体具用法:
a、建新一个类
var Cat = new Class({ initialize: function(name){ this.name = name; } }); var myCat = new Cat('Micia'); alert(myCat.name); // alerts 'Micia' var Cow = new Class({ initialize: function(){ alert('moooo'); } });
var Animal = new Class({ initialize: function(age){ this.age = age; } }); var Cat = new Class({ Extends: Animal, initialize: function(name, age){ this.parent(age); // calls initalize method of Animal class this.name = name; } }); var myCat = new Cat('Micia', 20); alert(myCat.name); // alerts 'Micia'. alert(myCat.age); // alerts 20.
var Animal = new Class({ initialize: function(age){ this.age = age; } }); var Cat = new Class({ Implements: Animal, setName: function(name){ this.name = name } }); var myAnimal = new Cat(20); myAnimal.setName('Micia'); alert(myAnimal.name); // alerts 'Micia'.
先看用法实例
a、创立类
// 创立类Person var Person = Class(object, { Create : function(name, age) { this.name = name; this.age = age; }, SayHello : function() { alert("Hello, I'm " + this.name + ", " + this.age + " years old."); } }); var BillGates = New(Person, ["Bill Gates", 53]); BillGates.SayHello();
// Employee继承Person var Employee = Class(Person, { Create : function(name, age, salary) { Person.Create.call(this, name, age); //调用基类的结构函数 this.salary = salary; }, ShowMeTheMoney : function() { alert(this.name + " $" + this.salary); } }); var SteveJobs = New(Employee, ["Steve Jobs", 53, 1234]); SteveJobs.SayHello(); SteveJobs.ShowMeTheMoney();
//创立类的函数,用于声明类及继承关系 function Class(aBaseClass, aClassDefine) { //创立类的临时函数壳 function class_() { this.Type = aBaseClass; //我们给每个类约定一个Type性属,引用其继承的类 for (var member in aClassDefine) this[member] = aClassDefine[member]; //制复类的全体义定到前当创立的类 }; class_.prototype = aBaseClass; return new class_(); }; //创立对象的函数,用于意任类的对象创立 function New(aClass, aParams) { //创立对象的临时函数壳 function new_() { this.Type = aClass; //我们也给每个对象约定一个Type性属,据此可以拜访到对象所属的类 if (aClass.Create) aClass.Create.apply(this, aParams); //我们约定有所类的结构函数都叫Create,这和DELPHI较比相似 }; new_.prototype = aClass; return new new_(); };
由于写的较比统笼,可能有很多地方没有剖析到,也可能有不确准的地方,还望正指。
看完面下几种剖析,相信息自己也可以写出一个自己的封装类库出来,至于,怎么现实看个人喜好了。但基本的思都是一样的基于原型的继承式方和循环贝拷新方法。
文章结束给大家分享下程序员的一些笑话语录: 程序语言综述
CLIPPER 程序员不去真的猎捕大象,他们只是购买大象部分的库然后花几年的时间试图综合它们。
DBASE 程序员只在夜间猎捕大象,因为那时没人会注意到他们还在使用石弓。
FOXPRO 程序员开始使用更新更好的步枪,这使他们花掉比实际狩猎更多的时间学习新的射击技术。
C 程序员拒绝直接购买步枪,宁可带着钢管和一个移动式机器车间到非洲,意欲从零开始造一枝完美的步枪。
PARADOX 程序员去非洲时带着好莱坞关于猎捕大象的电影剧本,他们认为照剧本行事就会逮到一头大象。
ACCESS 程序员在没有任何猎象经验的经验下就出发了,他们穿着华丽的猎装、带着全部装备,用漂亮的望远镜找到了大象,然后发觉忘了带扳机。
RBASE 程序员比大象还要稀少,事实上,如果一头大象看到了一个RBASE程序员,对他是个幸运日。
VISUAL ACCESS 程序员装上子弹、举起步枪、瞄准大象,这使大象感到可笑,究竟谁逃跑。他们无法抓住大象,因为由于他们对多重控制的偏爱,他们的吉普车有太多的方向盘因而无法驾驶。
ADA、APL和FORTRAN 程序员与圣诞老人和仙女一样是虚构的。
COBOL 程序员对和自己一样濒临灭绝的大象寄予了深切的同情。