1,解決上篇的問題先
前面一篇文章,我發了一堆的牢騷,想來也是很愚蠢的,只是被JavaScript搞的頭疼,不爽而已。像許多東西你不懂的時候以為他是屎,當你懂了時候才知道他是寶。
書也是讀第二遍的時候才能懂,08年能就讀了愛民的《JavaScript語言精髓與編程實踐》,而且也通讀了語言精髓的部分,而且還和他通郵件聊了一點。不過當前我也沒有讀懂,我在書上有所記錄。
現在看來Crockford的《JavaScript:Good Parts》中說的也對JavaScript確實有比較狗屎的地方,當然瑕不掩玉,JavaScript確定有著另人吃驚的能力。先來解決上篇的問題,我們再看看上篇的代碼:
function Shape(){ this.area = function(){};} function Point(){ this.x = 0; this.y = 0;} var p = new Point; //為啥是undefine,因為instace沒有prototype屬性,prototype對於p來說只是一個普通地跟p.val一樣是一個undefined的屬性。console.log(p.prototype); //為啥又是指向Point的function,因為p沒有constructor,只有Point.prototype有,通過原型尋找,即p.constructor == Point.prototype.constructor == Point;console.log(p.constructor);console.log(p.constructor == Point.prototype.constructor); console.log(p.constructor == Point); console.log("-----------------------------"); p.prototype = new Shape();console.log(p.area); //為啥又是undefine,不是設定了原型對象嘛,我操。//var p = new Point;做了下面三步:p={}; p.__proto__=Point.prototype=new Object(); Point.apply(p);//也就是說p的原型已經確定是new Object(),即一個Null 物件。 Point.prototype = new Shape(); //設定原型console.log(p.area); //TMD還是不行,我設定了類型的原型對象啊。p中沒有area屬性,p的原型new Object()中也沒有area屬性,當然就是undefined,無比的正確 Point.prototype.area = function(){};console.log(p.area); //你媽,為什麼啊。同上,p的原型在定義時已經確定就他媽的是一個Null 物件 var p2 = new Point;console.log(p2.area); //我操,這時又可以了@#¥%……&*(//var p2 = new Point;的工作過程:p2={},p2.__proto__=Point.prototype=new Shape();Ponit.apply(p2);//關鍵是第27行我們已經設定了Point.prototype這個原型為new Shape(); Point.prototype.val = 10;console.log(p2.val); //這下好理解了,p2中未定義val,這時去原型Point.prototype中找,找到了為10.Shape.prototype.val2 = 20;console.log(p2.val2); //p2中沒有val2,去Point.prototype(即new Shape())中找,new Shape()的__proto__為Shape.prototype,終於找到了。
2,一些JavaScript的總結
先謝謝部落格園的湯姆的一張圖:
2.1 JavaScript中函數也是對象
如果感覺這不好理解,看下面的代碼
//函數第一種寫法,更能看出函數也是objvar f1 = function Point(){ this.x = 0; this.y = 0;}//函數第二種寫法,與上面等效function f1(){ this.x = 0; this.y = 0;}
除此之外,函數的原型是f1.__proto__ == Function.prototype; Function.__proto__ == Object.prototype; Object.__proto__ = null;
2.2 利用new 建構函式()來建立對象做了些什麼
比如 var p = new Point();這句話做下以下工作:
var p = {};
p.__proto__ == Point.prototype; //很關鍵的一步,原型繼承來自於這裡
Point.apply(p);
剛才看Crockford的視頻,看到了new的JavaScript實現:
function new(func,arguments){ var that = Object.create(func.prototype); //建立一個對象,並將期__proto__設定為func.prototype指向的object instance. result = func.apply(that, arguments); return (typeof result === 'object' && result) || that;}
2.3 一些注意點
a,只有構造器有prototype屬性,即Point.prototype。prototype是(指向)一個執行個體(instance)
b,執行個體中有__proto__屬性,即p.__proto__;原型回溯時就通過些屬性。
c,p.prototype只是p的一個普通屬性。系統沒有對此有約定和特殊照顧。
d,構造器.prototype.constructor指向構造器自身,即Point.prototype.constructor == Point;
e, p.constructor == Point.prototype.constructor == Point;
2.4原型鏈的維護
如果你不想通過公開屬性比如constructor來回溯整個原型鏈,你不用考慮太多。
但若你想回溯,就必須留心了。比如
function MyObject(){};function MyObjectEx(){};MyObjectEx.prototype = new MyObject(); //這句話會使得下面成立var o = new MyObjectEx();console.log(o.constructor == MyObject); //我們在建立原型鏈時,將constructor指向打亂了。//解法1MyObjectEx.prototype = new MyObject();MyObjectEx.prototype.constructor = MyObjectEx; //手動修改constructor指向//上面解法的問題是MyObject.prototype.constructor其實應該指向MyObject這樣才能完成原型回溯,這便有瞭解法2//解法2function MyObjectEx(){ this.constructor = arguments.callee; //Or, this.constructor = MyObjectEx;}MyObjectEx.prototype = new MyObejct();//這樣MyObjectEx.Instance.constructor == MyObjectEx//並且MyObjectEx.prototype.constructor = MyObject
2.5 學JavaScript不能太糾結於實現的細節
你可以糾結與語言的細節,但不要糾結於實現的細節(SpideMonkey,JScript)。有些問題就是很奇怪。
比如:
var b = {};
console.log(b.__proto__ instanceof Object) //此處是false即使我用debugger看到他是Object
如果你還想看一些詭異的地方,移步至《Wat》