JavaScript的原型原型鏈的深刻理解及運用
*今天總結一下JavaScript的prototype原型和*____proto____原型鏈,瞭解這倆對我們深刻理解 js ,封裝常用小技巧很有幫助。
ES5中js本身是沒有類的,在ES5中js類就是函式function,而function本身也是物件。
一、js中的繼承是通過原型鏈 __proto__來實現的,物件與物件以及原型prototype(也是物件)就是通過__proto__原型鏈來來連結的,具體過程我們來上程式碼說明。
function obj (name){
this.name=name,
this.age="23"
this .fun=function(){
return this.name
};
obj.prototype.city="china";
};
obj.prototype.fun1=function(){
return this.age;
};
var obj1= new obj("laowang");
var obj2= nwe obj("hello");
這一段程式碼格式應該很常見,不過有個問題,var obj1 =new obj(“laowang”)是在全域性作用域中的,如果頁面這種寫法比較多 ,很容易勿讓汙染全域性。
可以寫成這樣:
(function(){
var obj1 = new obj("laowang");
//這樣也能正常執行,而且不被汙染全域性環境
})()
回到正題, 當我們執行 執行下列程式碼時:
obj1.name; //結果 laowang
obj1.city; //結果 China
obj1.fun(); //結果 laowang
obj1.fun2(); //結果 23
我們會看到 obj1 擁有了obj 的屬性和方法,也擁有了obj原型的方法。
分析:obj目前有name 和age 私有屬性和fun的私有方法,我們在obj原型上添加了 city屬性,和fun1方法
obj.prototype.city="china";
obj.prototype.fun1=function(){
return this.age;
};
而我們用new建立的建構函式,並賦給物件obj1,這個過程js為我們做了類似:(此處程式碼僅說明道理)
var obj1= (function(){
var o={}; //建立一個新物件
o.__proto__=obj.prototype; //將新物件的原型鏈指向obj的原型
obj.call(o,"hello") //執行obj函式,將o物件指標指向obj函式,及o擁有了obj原型的屬性和方法(引用)
return o;
})()
//我們可以嘗試一下,
obj1.__proto__===obj.prototype
// 結果true (__proto__非標準IE不支援,標準Object.getPrototypeOf()方法)
說明obj1的原型鏈指向obj的原型
__proto__
方法非獲取例項原型的標準方法,標準方法請使用Object.getPrototypeOf()
來獲取例項原型
obj1.name===obj2.name //結果 true 說明obj1和obj2共同引用obj的屬性name
需要說明一點,用new構造的函式,是沒有原型的 例如:
obj1.prototype //結果 underfind 證實了我們的猜想
二、上面的說完了,但是還有個問題,如果我想建立一個函式myObj;讓該函式擁有obj原型的方法和屬性該怎麼辦。
1.首先你可能會這樣做:
var myObj = function (){};
myObj.prototype=obj.prototype;
myObj.city //結果 underfind
這是因為 myObj的原型中的__proto__指向了obj的原型。myObj並沒有實現繼承obj的原型方法和屬性, 並且還會帶來一個問題是,當你修改myObj原型中的屬性(city)或方法( fun1)時,obj1和obj2中 屬性(city)和方法(fun1)也會跟著改變。
2.或者你有這樣改了:
myObj.__proto__=obj.prototye
但是帶來的問題是 __proto__
是不標準的,在ie和火狐上有時會報錯。
3.正確的寫法應該這樣寫:
Object.creat=function (aa) {
var o = function(){}; //建立新函式
o.prototype=aa; //讓該函式的原型等於obj的原型
return new o(); //返回一個o的建構函式
}
myObj.prototype=Object.creat(obj.prototye);
// 讓函式的原型等與Objet.creat物件,
此時myObj的原型物件就擁有了obj的原型方法和屬性,並且當myObj的原型屬性和方法改變是 ,不會影戲obj原型的屬性和方法
但是此時還有幾個問題:
1. myObj.prototype.constructor 返回的是例項obj,本身constructor是原型中指向自己的例項的,而此時,myObj.prototype.constructor和obj.prototype.constructor都指向例項obj;
2. myObj.city 結果會是 underfind; 我們只能通過訪問 myObj的原型去訪問city屬性;
3. 如果第二個問題解決了,我們會發現myObj.fun1(); 結果是underfind;
好下來咱們一個一個解決
第一個問題:我們可以這樣
myobj.prototype.constructor=myObj; //讓myObj的原型constructor指向myObj本身。
第二個問題:我們可以這樣:
var ob= new myobj(); //在用 new 建立一個新例項。
第三個問題:我們可以這樣:
var myObj = function () {
obj.call(this); //解決ob.fun1() underfind
};
好了問題基本解決了,最後我放上所有程式碼
function obj (name){
this.name=name,
this.age="23"
this.fun=function(){
return this.name
};
obj.prototype.city="china";
};
obj.prototype.fun1=function(){
return this.age;
};
var obj1= new obj("laowang");
var obj2= new obj("hello");
// new建構函式內的操作(程式碼僅說明)
// obj1=(function(){
// var o={};
// o.__proto__=obj.prototype;
// obj.call(o,"hello")
// return o;
// })();
var myObj = function () {
obj.call(this); //解決ob.fun1() underfind
};
Object.create=function(aa){
var o = function(){};
o.prototype=aa;
return new o();
}
myObj.prototype=Object.create(obj.prototype);
// 糾正myObj原型的constructor的指向
myobj.prototype.constructor=myObj;
// 解決用ob.city直接訪問
var ob= new myobj();
好了,到這裡就結束了,,如果有什麼錯誤,歡迎留言指點。