首先,推薦一篇博客豪情的博客JS提高: http://www.cnblogs.com/jikey/p/3604459.html ,里面的鏈接全是精華, 一般人我不告訴他;
我們會先從JS的基本的設計模式開始,由淺入深, 會描述prototype,__proto__,consturctor等基礎知識和JS的常見繼承方式, 以及四個類工廠的推薦和使用(包括JS.Class,prototype的類工廠,john resig寫的一個簡潔類工廠庫,以及Pjs一個很飄逸的繼承庫,很飄逸-_-),最后有3個參考資料鏈接:,最后有我個人的視頻,歡迎拍磚哇, ///(o)_(o)////。
工廠模式:因為使用用一個接口創建很多對象會產生大量的重復代碼,為了解決這個問題,人們就開始使用工廠模式:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> //工廠模式:因為使用用一個接口創建很多對象會產生大量的重復代碼,為了解決這個問題,人們就開始使用工廠模式: function Person(hairs,face, eye) { var o = new Object(); o.hairs = hairs; o.face = face; o.eye = eye; o.say = function(){ console.log("say someting to me!"); }; return o; }; //我們通過 Person(0,1,2)就可以創建一個包含特定信息的Person對象, 以后要生成對象直接執行Person然后給他傳參數就好了; //比如我們要生成10個Person, 很方便很快; var c = 10; while( c-- ) { console.log(Person(c,c,c)) }; </script> </body> </html>
構造函數模式:使用構造函數模式我們能少些更多代碼,比如上面的工廠模式我們可以改寫成:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> function Person(hairs, face, eye) { this.hairs = hairs; this.face = face; this.eye = eye; }; //同樣, 我們再生成10個小朋友 var c = 10; while( c-- ) { console.log( new Person(c,c,c) ); }; </script> </body> </html>
//知識點1: 那么工廠模式和構造函數模式有什么區別呢:
/*
* 沒有顯式地創建對象
* 直接把屬性和方法賦值給了this
* 沒有return語句
* 構造函數的前面有一個new;
* */
// 知識點2:
/*
* 通過new的構造函數會經過四個階段:
* 1:創建一個新對象;
* 2:把this賦值給這個新對象
* 3:執行構造函數中的代碼
* 4:返回新對象
* */
原型是神馬? 原型就是公用的方法或者屬性,這么理解最簡單, 當然:
1、prototype本質上還是一個JavaScript對象;
2、每個函數都有一個默認的prototype屬性;
3、通過prototype我們可以擴展Javascript的內建對象
一些代碼弄懂原型的本質:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> function Memo() {}; Memo.prototype.hehe = 1; Memo.prototype.shut = function() { console.log("miaomiao") }; var m0 = new Memo(); var m1 = new Memo(); console.log(m0.shut === m1.shut); //ture, 因為m0的shut和m1的shut就是指向了Memo.prototype.shut這個方法,所以他們相等; //Object.getPrototypeOf會返回實例的原型; console.log(Object.getPrototypeOf(m0)) //輸出了:Memo {hehe: 1, shut: function} console.log( Memo.prototype.isPrototypeOf(m0) )// 輸出: true; //原型對象有一個很大的問題要非常注意 ==》》 原型的屬性和方法是被共享的, 比如: Memo.prototype.shut = "W_W"; l(m0.shut) //輸出:W_W, 悲劇了吧, 本來原型上的shut是個方法, 被改成字符串以后, 實例上的shut也發生了改變; l(m1.shut) //輸出:W_W; m0.__proto__.shut = 1111; //m0的__proto__指向了Memo.prototype.shut,__proto__在標準瀏覽器下面是不能枚舉到的,但確實是存在的一個屬性; l(m1.shut) //輸出了1111 //只要原型上的屬性或者方法被改了, 實例上的也會發生改變; </script> </body>
</html>
知識點:Object.getPrototypeOf;
Fn.prototype.isPrototypeOf( instance );
好的, 現在再來了解一下constructor, 如果你已經知道constructor是什么的話, 這段略過, constructor是默認指向創建當前對象的構造函數, 但是這里面有一些坑要注意, 比如你的原型prototype被改了, 實例的constructor就變了 ,
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> var Fn = function(){ //我是構造函數Fn; }; l("Fn的constructor"); l(Fn.prototype.constructor); /*輸出function (){ //我是構造函數Fn; } */ var f = new Fn; l("f的constructor"); l(f.constructor); /*輸出; * function (){ //我是構造函數Fn; }*/ //當函數創建的時候默認就為prototype新建一個constructor指向自己; l(Fn.constructor === Function); //輸出true, Function這個構造函數是Function; l(Fn.constructor) // 輸出function Function() { [native code] } ,意思是Function這個構造函數 ; l(Fn.constructor === Fn.__proto__.constructor) //輸出true; Fn的constructor實際上是指向Fn.__proto__的; //好的, 現在重新定義一個Fn; var Fn = function(){ //我是構造函數Fn; }; Fn.prototype = {}; l(Fn.prototype.constructor) /*打印出了內部的代碼, 為什么呢? 因為我們改變了Fn的原型, Fn的constructor指向了{}空對象的Contructor; * function Object() { [native code] } */ </script> </body> </html>
大家一定要懂的是實例上的__proto__就是指向原型上的prototype, 這樣會少走一些彎路,可以節約更多的時間用來看片, 你懂的;
每一個函數新建的時候都有一個默認的prototype, prototype這個對象上面默認有一個指向自己的constructor;
prototype和__proto__的區別:__proto__是實例和Person.prototype之間的關系,而constructor是實例和Person之間的關系
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> var Fn = function() {}; Fn.prototype.hehe = 1; Fn.prototype.lala = 2; var f = new Fn; l(f) //輸出了Fn {hehe: 1, lala: 2} ; l(f.__proto__) //輸出了Fn {hehe: 1, lala: 2} ; l(Fn.prototype === f.__proto__) //輸出了true, 這里要懂的東西是實例上的__proto__這個屬性指向了構造函數的prototype; l(f === f.__proto__) //輸出false; 這里要懂的是f是new出來的實例; //因為f上面的hehe和lala都是繼承的屬性, 所以這里面的log并沒有被輸出; for(var p in f){ l("輸出所以屬性:" + p); //輸出所以屬性:hehe demo.html:11; 輸出所以屬性:lala //過濾非私有屬性; if( f.hasOwnProperty(p) ) l("輸出私有屬性" + p); //因為f沒有私有屬性,所以這邊沒有log出來; }; </script> </body> </html>
有了上面的基礎, 我們開始說說JS的面向對象和繼承吧;
1:組合使用構造器和原型模式, 這種模式是構造函數和原型混合的模式, 使用最廣泛, 認同度也最高的一種模式, 也是最基礎的模式;
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> function Duck( name ,word) { this.name = name; this.word = word; }; Duck.prototype.say = function() { console.log( this.name+" say : " + this.word ) }; var duck = new Duck("nono","hehe"); duck.say(); </script> </body> </html>
寄生構造模式; 聽名字真的很玄乎..其實跟工廠模式一模一樣的, 其實就是自定義模型的封裝;
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> function Foxy(name , word) { var result = new Object(); result.name = name; result.word = word; return result; }; l( new Foxy("nono","say someting") ); //輸出:Object {name: "nono", word: "say someting"} ; function Monkey( ) { var aResult = []; //技巧:通過apply把arguments保存到數組; aResult.push.apply(aResult, arguments); return aResult; }; l( new Monkey("nono","dino","kite","sam") ); //打印出了這個:["nono", "dino", "kite", "sam"]; //要注意的是使用寄生模式的返回的對象和構造函數一點關系都沒有; </script> </body> </html>
JS的原型繼承, 繼承是依賴于原型鏈的;那么JS原型鏈是什么呢:
/* 這段話慢慢讀, 從搞基程序設計三抄過來的,很重要, 實體書最好自己看一看哇;
* ECMAScript中描述了原型鏈的概念, 并將原型鏈作為實現繼承的主要方法, 基本思想是利用引用類型繼承另一個引用類型的屬性和方法。
* 簡單回顧一下構造函數,原型和實例的關系:每一個函數都有一個原型對象, 每一個原型對象都有一個指向構造函數的指針,
* 而實例包含了一個指向原型對象的內部(不可見的)指針。 那么我們讓原型對象等于另一個類型的實例, 那么這個原型對象將會包含指向
* 另一個原型對象的指針,如果另一個原型對象又是指向了別的原型的一個實例, 這樣層層嵌套, 就形成了原型鏈;
* */
那么我們利用原型的原型鏈相互繼承來寫一個基本的例子:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> var Fn = function() { this.property = true; } Fn.prototype.getFnProperty = function() { console.log( this.property ); }; var SubFn = function() { this.subProperty = false; }; //SubFn繼承了Fn的實例 SubFn.prototype = new Fn(); //為實例添加額外的實例方法; SubFn.prototype.getSubProperty = function(){ console.log(this.subProperty); }; var subFn = new SubFn(); subFn.getFnProperty(); //輸出了true subFn.getSubProperty(); //輸出了false /*現在subFn的constructor 是 function () { this.property = true; }; 所以要修正SubFn.prototype.constructor = SubFn */ </script> </body> </html>
原型式繼承第二個例子:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> // 首先, 準備一個方法; var inherit = function(o) { if(!typeof o === "object")return; function F () {} F.prototype = o; F.prototype.constructor = F; return new F(); }; var Fn = function() { this.property = true; } Fn.prototype.getFnProperty = function() { console.log( this.property ); }; var SubFn = function() { this.subProperty = false; }; //SubFn繼承了Fn的實例 SubFn.prototype = new Fn(); //為實例添加額外的實例方法; SubFn.prototype.getSubProperty = function(){ console.log(this.subProperty); }; var subFn = new SubFn(); //這個方法的內部, 臨時創建了一個構造函數, 然后將傳入的對象作為這個構造函數的原型, 最后返回一個臨時的新實例; //ECMASscript 5 有新增了一個Object.create 效果和inherit一模一樣, 它可以接收第二個參數, // 第二個參數要通過defineProperties的方式設置,會覆蓋原型的屬性, 比如: Object.create(subFn, { getFnProperty: { value:1 } }); var Fn = function() {}; //如果我們inherit傳對象; Fn.prototype = inherit( {0:0,1:1,2:2,3:3,4:4} ); l( new Fn ) //==>Fn {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, constructor: function(){....} 繼承了哦 //如果我們給inherit傳一個構造函數的實例; Fn.prototype = inherit( new SubFn ); l(new Fn); </script> </body> </html>
有時候看到原型的各種引用會尿失禁, 引來引去的,坑爹啊, 不說了去洗褲子了....
寄生組合式繼承
組合繼承是JS的常用繼承模式, 但是也有自己的不足, 組合繼承最大的問題的無論是什么情況下, 都會兩次調用超類的構造函數;
一個是在創建子類原型的時候, 另一個是在子類構造函數的內部, 那么子類的原型會包含所有超類實例的全部屬性,
寄生組合式繼承就是為了解決子類原型包含所有超類實例全部屬性這個問題而存在的;
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> //最好所有的代碼自己打一遍, 增加印象; function l ( arg ) { console.log( arg ); }; </script> <script> var inherit = function(o) { if(!typeof o === "object")return; function F () {} F.prototype = o; F.prototype.constructor = F; return new F(); }; //首先要準備inheritPrototype方法; var util = util || {}; util.inherit = inherit; util.inheritPrototype = function(subType, superType) { var _prototype = this.inherit( superType.prototype ); _prototype.constructor = subType; subType.prototype = _prototype; }; function F( name ) { this.name = name; this.type = "human"; this.habits = ["dance","code"]; }; F.prototype.laugh = function() { console.log("heha!"); }; var InheritF = function() { F.apply( this, arguments ); }; util.inheritPrototype(InheritF, F); InheritF.prototype.letsGo = function() { l("1,2,3,4") }; var nono = new InheritF("nono"); nono.habits.push("read books"); l(nono.habits) var nonono = new InheritF("nono"); l( nonono.habits ); //繼承的方法千萬種,萬變不離其宗; </script> </body> </html>
JS各種使用了繼承庫的類庫推薦, 可以加深印象:
首先, JS.Class 是一個mootools式的類工廠 基于 lunereaper<![[dawid.kraczkowski[at]gmail[dot]com]]>的項目進行修改, 讓子類的實現更簡潔;

<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> JS = {}; // 這個是庫的源代碼; JS.Class = function(classDefinition) { //返回目標類的真正構造器 function getClassBase() { return function() { //它在里面執行用戶傳入的構造器construct //preventJSBaseConstructorCall是為了防止在createClassDefinition輔助方法中執行父類的construct if (typeof this['construct'] === 'function' && preventJSBaseConstructorCall === false) { this.construct.apply(this, arguments); } }; } //為目標類添加類成員與原型成員 function createClassDefinition(classDefinition) { //此對象用于保存父類的同名方法 var parent = this.prototype["parent"] || (this.prototype["parent"] = {}); for (var prop in classDefinition) { if (prop === 'statics') { for (var sprop in classDefinition.statics) { this[sprop] = classDefinition.statics[sprop]; } } else { //為目標類添加原型成員,如果是函數,那么檢測它還沒有同名的超類方法,如果有 if (typeof this.prototype[prop] === 'function') { var parentMethod = this.prototype[prop]; parent[prop] = parentMethod; } this.prototype[prop] = classDefinition[prop]; } } } //其實就是這樣的Base = function() {};別想太多了; var preventJSBaseConstructorCall = true; var Base = getClassBase(); preventJSBaseConstructorCall = false; createClassDefinition.call(Base, classDefinition); //用于創建當前類的子類 Base.extend = function(classDefinition) { //其實就是這樣的Base = function() {};別想太多了; preventJSBaseConstructorCall = true; var SonClass = getClassBase(); SonClass.prototype = new this();//將一個父類的實例當作子類的原型 preventJSBaseConstructorCall = false; createClassDefinition.call(SonClass, classDefinition); SonClass.extend = this.extend; return SonClass; }; return Base; }; </script> <script> //這是實際案例; var Animal = JS.Class({ construct: function(name) { this.name = name; }, shout: function(s) { console.log(s); } }); var animal = new Animal(); animal.shout('animal'); // animal var Dog = Animal.extend({ construct: function(name, age) { //調用父類構造器 this.parent.construct.apply(this, arguments); this.age = age; }, run: function(s) { console.log(s); } }); var dog = new Dog("dog", 4); console.log(dog.name); dog.shout("dog"); // dog dog.run("run"); // run console.log(dog.constructor + "") var Shepherd = Dog.extend({ statics: {//靜態成員 TYPE: "Shepherd" }, run: function() {//方法鏈,調用超類同名方法 this.parent.run.call(this, "fast"); } }); console.log(Shepherd.TYPE);//Shepherd var shepherd = new Shepherd("shepherd", 5); shepherd.run();//fast var a = new Animal("xx"); console.log(a.run); </script> </body> </html>
prototype這個( ▼-▼ )庫以前牛逼著呢, 但是一百年河東一百年河西, prototype當前的版本是最新版本的1.7穩定版,prototype里面的類工廠創建的主代碼也被我單獨裁出來了, 源碼也差不多, 源碼有我加的注釋,自己個人方便閱讀, HTML代碼的最后自己寫的幾種 創建類工廠的方式, 可以借鑒(我沒看api, 源碼就是api....)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>prototype Demo</title> </head> <body> <script> var Class = (function() { var IS_DONTENUM_BUGGY = (function(){ for (var p in { toString: 1 }) { if (p === 'toString') return false; } return true; })(); Object.keys = Object.keys || (function() { if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } var results = []; for (var property in object) { if (object.hasOwnProperty(property)) { results.push(property); } } return results; })(); Object.isFunction = function(fn) { return typeof fn === "function"; }; function argumentNames() { var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') .replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; }; function $A(iterable) { if (!iterable) return []; if ('toArray' in Object(iterable)) return iterable.toArray(); var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; }; function update(array, args) { var arrayLength = array.length, length = args.length; while (length--) array[arrayLength + length] = args[length]; return array; }; //wrapper表示的是現在的方法; function wrap(wrapper) { //this指向的返回的閉包; var __method = this; return function() { //a是指把orginalFu和當前的參數混合成一個數組; var a = update([__method.bind(this)], arguments); return wrapper.apply(this, a); }; }; Object.extend = extend; Function.prototype.argumentNames = argumentNames; Function.prototype.wrap = wrap; //把第二個參數的屬性繼承到第一個參數上; function extend(destination, source) { for (var property in source) destination[property] = source[property]; return destination; }; function subclass() {}; function create() { //相當于對參數進行slice; var parent = null, properties = $A(arguments); //如果第一個是函數就把這個函數作為超類; if (Object.isFunction(properties[0])) parent = properties.shift(); //這個是返回的構造函數; function klass() { this.initialize.apply(this, arguments); }; //為klass添加addMethods方法; Object.extend(klass, Class.Methods); //保存超類; klass.superclass = parent; klass.subclasses = []; if (parent) { subclass.prototype = parent.prototype; klass.prototype = new subclass; //把當前的類保存到父級的超類, 有什么意義呢; parent.subclasses.push(klass); }; for (var i = 0, length = properties.length; i < length; i++) klass.addMethods(properties[i]); //避免沒有initialize而報錯; if (!klass.prototype.initialize) klass.prototype.initialize = function() {}; //這個避免constructor被properties覆蓋了, 重新賦值; klass.prototype.constructor = klass; return klass; } function addMethods(source) { var ancestor = this.superclass && this.superclass.prototype, properties = Object.keys(source); //IE8以下可以遍歷到toString和valueOf; if (IS_DONTENUM_BUGGY) { if (source.toString != Object.prototype.toString) properties.push("toString"); if (source.valueOf != Object.prototype.valueOf) properties.push("valueOf"); } for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; //沒有父級就沒必要走下去了 , 如果有父級, 而且value不是function那么就沒必要走了; //如果你傳:Class.create(Fn, {0:function($super){alert(1)},1:1,2:2,3:3,4:4}) //那么$super這個方法是指向ancestor的閉包, 只要執行$super那么超類的同名方法會被執行; if (ancestor && Object.isFunction(value) && value.argumentNames()[0] == "$super") { var method = value; //返回一個閉包; value = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; })(property).wrap(method); /* 如果不用簡寫的話,寫成這樣子也是ok的,他那么寫, 一下子高端起來了,我C, 就是兩個閉包哇; * var closureFn = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; })(property); value = function() { var args = Array.prototype.slice.call(arguments); args.unshift( closureFn ); //是誰執行了value,當前的this就是誰,剛好是實例對象; method.apply(this, args) }; * */ //修正這個閉包的valueOf為method這個方法.... value.valueOf = method.valueOf.bind(method); value.toString = method.toString.bind(method); }; //Class.create({0:0,1:1,2:2,3:3,4:4})會走這邊; this.prototype[property] = value; }; return this; }; return { create: create, Methods: { addMethods: addMethods } }; })(); </script> <script> //__________________________________________ //創建一個類工廠 var Fn = Class.create({ method0 : function() { console.log("method0"); console.log(arguments); }, method1 : function() { console.log("method1"); console.log(arguments); } }); //實例化該類工廠; var fn = new Fn(); fn.method0(); //輸出 : method0 [] fn.method1(0,1,2,3,4); //輸出 : method1 , [0, 1, 2, 3, 4]; //SubFn , 繼承了Fn; var SubFn = Class.create(Fn, { method2 : function() { console.log(2) } }); (new SubFn).method2() //輸出 :2 //__________________________________________ //__________________________________________ //子類調用超類的同名方法的使用; //子類; var Fn = Class.create({ 0:function(){ console.log(arguments); return "hehe"; }, 1:1, 2:2, 3:3, 4:4 }); //超類; //Fn0繼承了Fn; var Fn0 = Class.create(Fn, { 0:function($super,arg0,arg1,arg2,arg3){ console.log( $super( arguments ) ); console.log(arg0+" "+arg1+" "+arg2+ " " +arg3); } }); //實例化Fn0,執行0的方法傳進去幾個參數, 都會傳到Fn的0方法里面, Fn0如果叫做$super的參數, $super代表超類的同名方法; (new Fn0)[0](10,9,8,7); //輸出 三個字段,包含超類的的arguments和超類的返回,以及子類的輸出: arguments5 , hehe , 10,9,8,7 console.log(new Fn0); //輸出一個實例; //__________________________________________ //為Fn0的原型新加一個“nono”方法; Fn0.addMethods({"nono" : function() { console.log("you are super man"); }}); //實例化原型執行nono方法: var instance = new Fn0; instance.nono() //輸出了:you are super man; //__________________________________________ //我們現在的類方法和屬性都是原型上繼承下來的,只要超類的方法或者屬性發生了改變, 那么子類的方法也發生了改變; //比如 Fn0.prototype.nono = "someting wrong!!"; instance.nono // 輸出了== >"someting wrong!!" ...臥槽,這樣可不行啊; //我們需要創建私有屬性 var FnBar = Class.create(Fn,{ initialize : function() { this.hehe = "haha", this.say = function( words ) { console.log( words ||this.hehe ); }; } }); var instanceBar = new FnBar(); var FnFoo = Class.create(FnBar,{ initialize : function() { this.say = function() { console.log("fuck! you duck!!!"); }; } }); //prototype是基于原型繼承原型的庫, 而不是基于原型繼承實例, 所以用起來也挺方便的; (new FnFoo).say(); </script> </body> </html>
這款繼承創建的作者是jQ的作者,你懂的, 不繞;
https://github.com/html5crew/simple-inheritance 《《== 這個是代碼的源地址:

<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <script> /* source: https://gist.github.com/shakyShane/5944153 * * Simple JavaScript Inheritance for ES 5.1 ( includes polyfill for IE < 9 ) * based on http://ejohn.org/blog/simple-javascript-inheritance/ * (inspired by base2 and Prototype) * MIT Licensed. */ (function (global) { "use strict"; if (!Object.create) { Object.create = (function () { function F() { } return function (o) { if (arguments.length !== 1) { throw new Error("Object.create implementation only accepts one parameter."); } F.prototype = o; return new F(); }; })(); } var fnTest = /xyz/.test(function () { /* jshint ignore:start */ xyz; /* jshint ignore:end */ }) ? /\b_super\b/ : /.*/; // The base Class implementation (does nothing) function BaseClass() { } // Create a new Class that inherits from this class BaseClass.extend = function (props) { var _super = this.prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor) var proto = Object.create(_super); // Copy the properties over onto the new prototype for (var name in props) { // Check if we're overwriting an existing function proto[name] = typeof props[name] === "function" && typeof _super[name] === "function" && fnTest.test(props[name]) ? (function (name, fn) { return function () { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, props[name]) : props[name]; } // The new constructor var newClass = function () { if (typeof this.init === "function") { this.init.apply(this, arguments); } }; // Populate our constructed prototype object newClass.prototype = proto; // Enforce the constructor to be what we expect proto.constructor = newClass; // And make this class extendable newClass.extend = BaseClass.extend; return newClass; }; // export global.Class = BaseClass; })(this); </script> <script> var Fn = function() {}; //對繼承的插件進行引用; Fn.extend = window.Class.extend; Fn.prototype.lala = 1; Fn.prototype.say = function() { alert( this.lala ); }; var Foo = Fn.extend({ dudu:2, init:function(){ this.name="nono" }, dayDudu:function(){ alert(this.dudu); } }) </script> </body> </html>
執行 new Foo打印出來對象結構如下:
pjs這個類工廠的github地址是:git://github.com/jayferd/pjs ,挺有名的js類工廠庫, 寫了源碼分析, 關于代碼我就不吐槽了,你看了就懂了, 不過真心挺方便的;

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>無標題文檔</title> </head> <body> <script> var P = (function(prototype, ownProperty, undefined) { // helper functions that also help minification function isObject(o) { return typeof o === 'object'; } function isFunction(f) { return typeof f === 'function'; } // a function that gets reused to make uninitialized objects function BareConstructor() {} function P(_superclass /* = Object */, definition) { // handle the case where no superclass is given //如果沒有超類,那么超類就設置為Object,definition為對象或者是fn都行 //是對象的話會把對象的所有屬性和方法復制到返回的類原型, //是函數會傳給函數有關返回類的原型(引用)等參數; if (definition === undefined) { definition = _superclass; _superclass = Object; }; // C is the class to be returned. // // It delegates to instantiating an instance of `Bare`, so that it // will always return a new instance regardless of the calling // context. // // TODO: the Chrome inspector shows all created objects as `C` // rather than `Object`. Setting the .name property seems to // have no effect. Is there a way to override this behavior? //無論你是使用 new還是直接運行都會返回C的實例; function C() { var self = new Bare; //如果創建的實例有init會執行init方法, init這個函數里面放的是私有的屬性; if (isFunction(self.init)) self.init.apply(self, arguments); return self; } // C.Bare is a class with a noop constructor. Its prototype is the // same as C, so that instances of C.Bare are also instances of C. // New objects can be allocated without initialization by calling // `new MyClass.Bare`. function Bare() {} C.Bare = Bare; // Set up the prototype of the new class. //指定超類的原型 到BareConstructor, 公用一個函數到BareConstructor,專門用來實例化超類; var _super = BareConstructor[prototype] = _superclass[prototype]; //proto為Bare原型的引用; //實例化的超類的實例指向了C和Bare的Prototype,主要是Bare的prototype,因為類工廠返回的實例就是Bare的實例; var proto = Bare[prototype] = C[prototype] = new BareConstructor; // other variables, as a minifier optimization var extensions; // set the constructor property on the prototype, for convenience proto.constructor = C; //這個超類的mixin會重新調用P返回原型重新賦值給Bare和C,返回C, 方便無new實例化; C.mixin = function(def) { Bare[prototype] = C[prototype] = P(C, def)[prototype]; return C; }; //C.open 這個方法可以用來為這個類添加原型 return (C.open = function(def) { //重新定義extensions為一個空對象, C.open打開或者第一次打開的時候重新定義, 寫成var extensions = {}; 也行,個人感覺沒有問題; extensions = {}; if (isFunction(def)) { // call the defining function with all the arguments you need // extensions captures the return value. //proto指向了C和Bare的原型,要添加原型就往這個對象extend方法即可; //也可以為這個函數返回對象作為原型的屬性; extensions = def.call(C, proto, _super, C, _superclass); } else if (isObject(def)) { // if you passed an object instead, we'll take it extensions = def; }; //繼承屬性到超類; // ...and extend it if (isObject(extensions)) { for (var ext in extensions) { if (ownProperty.call(extensions, ext)) { proto[ext] = extensions[ext]; }; }; }; //沒有init就把init設置為超類; // if there's no init, we assume we're inheriting a non-pjs class, so // we default to applying the superclass's constructor. if (!isFunction(proto.init)) { proto.init = _superclass; } return C; })(definition); } // ship it return P; // as a minifier optimization, we've closured in a few helper functions // and the string 'prototype' (C[p] is much shorter than C.prototype) })('prototype', ({}).hasOwnProperty); </script> <script> //直接創建一個類 var Fn = P({ 0:0, 1:1 }); console.log( new Fn() ); //輸出: Bare {0: 0, 1: 1, constructor: function, init: function} console.log( (new Fn()).init() ) //如果沒有init, 那么init出來默認為 Object {} //讓FnBar繼承Fn var FnBar = P(Fn,{ 2:2, 3:3, 4:4 }); console.log( new FnBar ); //輸出: Bare {2: 2, 3: 3, 4: 4, constructor: function, 0: 0, 1: 1, init: function} //為FnFoo添加額外的方法和屬性; var FnFoo = FnBar.open({ 5:5, 6:6 }); console.log( new FnFoo ); //輸出: Bare {2: 2, 3: 3, 4: 4, 5: 5, 6: 6, constructor: function, 0: 0, 1: 1, init: function} //各種各樣的繼承方式 你都可以用 var FnFoo0 = FnFoo.mixin({ 7:7, 8:8 }); console.log( new FnFoo0); //輸出: Bare {7: 7, 8: 8, constructor: function, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 0: 0, 1: 1, init: function} //____________________________________-_-_______________ //直接創建一個類 var Fn = P({ 0:0, 1:1, init : function() { this.name = "nono"; console.log(this); } }); (new Fn).init(); // 輸出: Bare {name: "nono", 0: 0, 1: 1, constructor: function, init: function}; //通過C.open的方式為Fn添加超類 var FnBar = Fn.open(function( proto, _super, C, _superclass ) { proto.style = "gangnanStyle"; proto.job = "superWebDevoloper"; proto.say = function() { console.log( this.job + " __ " + this.job); }; }); (new FnBar).say(); //輸出: superWebDevoloper __ superWebDevoloper //通過傳入function的方式為構造函數添加超類; // 該方法不但繼承了FnBar, 而且在第二個Fn的參數中為構造函數添加了sayWhat方法, 該函數的返回也會作為原型被添加到類原型上。。。。; var FF = P(FnBar, function( proto, _super, C, _superclass ) { proto.sayWhat = function() { console.log( "funky world!" ); }; return { sayHi : function() { console.log( "monkey world!" ); } }; }); console.log( new FF );//輸出: Bare {name: "nono", constructor: function, sayWhat: function, 0: 0, 1: 1, init: function, style: "gangnanStyle"…}; (new FF).sayWhat(); //輸出: funky world! (new FF).sayHi(); //輸出:monkey world! </script> </body> </html>
每一個人都要有一片屬于自己的寧靜天空, 在那里沒有壓力, 只有自己 , 和自己身體的對話;
最后提供一些參考的鏈接:
javascript 類屬性、類方法、類實例、實例屬性、實例方法、prototype、__proto__ 測試與小結:
http://www.cnblogs.com/mrsunny/archive/2011/05/09/2041185.html
JS的構造函數:
http://www.cnblogs.com/jikey/archive/2011/05/13/2045005.html
淺析Javascript原型繼承:
文章列表