題外話:昨天手欠,試圖用alert(window.appName)到ff之下去查看瀏覽器版本,結果彈出的竟然是Netscape,咋不是 firefox。繼而又跑去chrome下試驗,又一次彈出了Netscape。baidu搜 Netscape 竟然發現js就出自Netscape公司。慚愧啊慚愧!!!研究了這么久的js都不認識祖師爺。於是又跑去找了找族譜,原來js出自Brendan Eich之手,95年他創造js時候,也不過就31歲。哎呀,真是白活了,如他一般老的我,到現在都學不會js,真是人比人氣死人。。js當初設計的時候,沒有想到自己能從一部打電話用的手機變成集拍照,上網,游戲,電話於一身的智能機。真是造化弄人!!!也許各中的神奇,連Brendan Eich本人都沒有想到。應該說Brendan Eich創造了js,而一大批的js牛人成就了今天如此復雜的js。
js不是木有類么?沒關系,人家不是設計了原型屬性么~
js不是木有塊級作用域么?沒關系,人家不是有作用域鏈么~
js怎樣實現成員變量私有化?哦,用閉包解決吧~
哦,這么多基本概念,徹底的暈掉了,路漫漫其修遠兮。
言歸正傳,本文討論幾種js創建對象的方法,先從最好理解的工廠模式開始:
function createPerson(name,age,job){ var o = {}; o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var tanya = createPerson("tanya","30","female"); var ansel = createPerson("ansel","30","male"); tanya.sayName(); ansel.sayName();
這里先定義o為一個空的對象,然后為o設置了一堆屬性。其實也可以直接給o屬性的嘛,所以如果這樣寫也是ok的。
function createPerson(name,age,job){ var o = { name : name, age : age, job : job, sayName : function(){ alert(this.name); } }; return o; } var tanya = createPerson("tanya","30","female"); var ansel = createPerson("ansel","30","male"); tanya.sayName(); ansel.sayName();
還有一種辦法是利用無敵的this,因為this就表示當前運行時的對象,將構造函數this的作用域指向新對象,將當前運行對象的屬性和方法都賦給新對象,這樣對象模式稱為構造函數模式
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var tanya = new Person("tanya","30","female"); var ansel = new Person("ansel","30","male"); tanya.sayName(); ansel.sayName();
在這個例子中,tanya和ansel都有一個constructor屬性,該屬性指向person。
考慮一下如下的情況:
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } Person("tanya","30","female"); Person("ansel","30","male"); window.sayName(); window.sayName();
發現兩次彈出的都是ansel,這是因為不用new的話,就不是一個person的實例,而僅僅在執行函數。而在全局作用域調用一個函數時this總是指向Global對象。而Global對象在瀏覽器中就是window對象。
我們還可以用構造模式在另外一個對象中調用sayName方法,還記得Apply和call么,來吧再考慮另外一種情況,
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var olivia = {}; Person.call(olivia,"tanya","30","female"); olivia.sayName(); var philip = {} Person.apply(philip,["ansel","30","male"]); philip.sayName();
原型模式就要考慮原型鏈了,分析一下,sayName方法在實例中被重復定義了兩次,但其實沒有必要創造兩個一樣的副本。使用原型方法,可以使是tanya和ansel的共享一個sayName方法。
於是原型模式的寫法如下:
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; } Person.prototype.sayName= function(){ alert(this.name); }; var tanya = new Person("tanya","30","female"); var ansel = new Person("ansel","30","male"); tanya.sayName(); ansel.sayName();
實際應用時,不是一成不變的套用某種模式,活學活用。需要共享方法的時候就用原型模式,需要使用副本的時候就用構造模式,還可以結合起來,把所有信息都封裝在構造函數中,而通過在構造函數中初始化原型,使得對象保持了同時使用構造函數和原型的優點。
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; if (typeof sayName != "function" ){ Person.prototype.sayName= function(){ alert(this.name); }; } } var tanya = new Person("tanya","30","female"); var ansel = new Person("ansel","30","male"); ansel.sayName = function () { alert("Hi ansel, how hansome you are!"); } tanya.sayName(); ansel.sayName();
