1. 程式人生 > >JavaScript的原型原型鏈的深刻理解及運用

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();

好了,到這裡就結束了,,如果有什麼錯誤,歡迎留言指點。