JavaScript 封裝問(wèn)題

2018-06-16 18:35 更新

為什么會(huì)用這樣一個(gè)題目呢,這是要說(shuō)封裝的什么問(wèn)題,本文并不講高深的封裝理論,只是解決一個(gè)小問(wèn)題。

問(wèn)題來(lái)源

今天在百度知道上閑逛,遇到一個(gè)網(wǎng)友的問(wèn)題,問(wèn)題如下,問(wèn)題的地址見這里

下面先不看看其他網(wǎng)友給的答案:

網(wǎng)友大部分回答不能一起定義,那么我們來(lái)分析下為什么這樣做是錯(cuò)的,然后給出相應(yīng)的解決辦法。

重現(xiàn)問(wèn)題

先來(lái)說(shuō)說(shuō)為什么調(diào)用出錯(cuò),我在自己的瀏覽器里重現(xiàn)了問(wèn)題,處于實(shí)驗(yàn)并未全部復(fù)原代碼,并且用到了全局變量哦:

function Dialog(){
    
    Dialog.prototype = {
        init:function(){
            console.log("ok");
        }
    }
}

var a = new Dialog();
a.init();

下面是火狐提示的錯(cuò)誤:

分析問(wèn)題

init不是一個(gè)方法,這是為什么呢,我們將調(diào)用代碼修改下,出于演示,并未遵循JsLint代碼規(guī)范:

var a = new Dialog();
typeof a.init;

結(jié)果顯示為undefined,也就是init沒(méi)有被定義,或者說(shuō)在求值過(guò)程中未找到init標(biāo)識(shí)符的值,這是因?yàn)樵谡{(diào)用函數(shù)時(shí)函數(shù)里面的內(nèi)容才會(huì)被解析執(zhí)行,所以在調(diào)用new Dialog(),時(shí)其內(nèi)部的代碼尚未執(zhí)行,所以設(shè)置Dialog的原型的語(yǔ)句尚未執(zhí)行,通過(guò)這個(gè)例子可以看出綁定this的prototype的過(guò)程是在執(zhí)行構(gòu)造函數(shù)內(nèi)部代碼之前,可以用下面的代碼來(lái)解釋下:

function Dialog(){
    var that = Object.create(Object.getPrototypeOf(this));
    
    Dialog.prototype = {}         
}

“巧妙解決”

很明顯獲取原型時(shí)尚未設(shè)置原型,但當(dāng)我們?cè)俅握{(diào)用new 時(shí)情況將發(fā)生改變:

var a = new Dialog();
var b = new Dialog();
typeof b.init;

在此調(diào)用時(shí)奇跡發(fā)生了,我們看到第二次調(diào)用時(shí)成功獲取了值,只有第一次時(shí)值是未獲去,那我們是不是可以改造下我們的dialog函數(shù)呢:

function Dialog(){
    
    Dialog.prototype = {
        init:function(){
            console.log("ok");
        }
    }
}
new Dialog();


var a = new Dialog();
typeof a.init;

我們只需先調(diào)用下構(gòu)造函數(shù)便解決了問(wèn)題,如果你覺(jué)得上面的代碼還是有悖于封裝我們可以再做改變:

var Dialog = (function(){
    function Dialog(){
    
        Dialog.prototype = {
            init:function(){
                console.log("ok");
            }
        }
    }
    
    new Dialog();
    
    return Dialog;
}());

問(wèn)題中的問(wèn)題

上面真的解決問(wèn)題了嗎,你難道沒(méi)有疑問(wèn)呢,如果你已經(jīng)看出問(wèn)題所在在了,那你也一定能想出解決辦法,而且你應(yīng)該是一個(gè)高手,那么讓我們看看這樣巧妙的解決辦法有什么問(wèn)題,為此我們來(lái)構(gòu)造下面的代碼:

var a = new Dialog();
typeof a instanceof Dialog;

也許你會(huì)問(wèn)為什么會(huì)這樣寫,也許下面的結(jié)果更讓你吃驚:

結(jié)果為false,為什么我的a不是Dialog的實(shí)例呢,我的a明明是Dialog創(chuàng)建的,要想搞清這個(gè)問(wèn)題我們先得說(shuō)清楚 instanceof關(guān)鍵字的工作原理,當(dāng)我們調(diào)用類似a instanceof Dialog 這樣的語(yǔ)句時(shí),解釋器是怎么判斷a是Dialog創(chuàng)建的對(duì)象的呢,原來(lái)解釋器是判斷a的原型是否為Dialog的prototype屬性所指向的對(duì)象也就是說(shuō)如果a的原型和Dialog的prorotype屬性指向同一個(gè)對(duì)象就認(rèn)為a是Dialog的對(duì)象,當(dāng)然在判斷時(shí)并不是至判斷a的的原型,而是判斷原型鏈中的每個(gè)對(duì)象,例如:

var a = [];

a instanceof Array;
a instanceof Object;

上面的兩條語(yǔ)句都會(huì)返回true,因?yàn)閍的原型鏈中包含這兩個(gè)對(duì)象。

而上面我們的代碼為什么結(jié)果為false呢,那是因?yàn)楫?dāng)我們每次調(diào)用Dialog構(gòu)造函數(shù)時(shí)都會(huì)在內(nèi)部重寫Dialog的原型,而已經(jīng)創(chuàng)建的對(duì)象的原型會(huì)指向原來(lái)的原型對(duì)象,解釋器在判斷兩個(gè)對(duì)象是否相等時(shí),要判斷兩個(gè)對(duì)象是否引用同一塊地址,而不是兩個(gè)對(duì)象是否有相同的屬性和方法,所以上面出現(xiàn)false的原因就很清楚了,所以上面的解決辦法就出現(xiàn)問(wèn)題了,而且是很大的問(wèn)題。顯然這種方法行不通。

看清本質(zhì)

那我們有沒(méi)有辦法解決問(wèn)題呢,讓我們先來(lái)看看作者想要實(shí)現(xiàn)什么,作者想要實(shí)現(xiàn)的封裝,也就是構(gòu)造函數(shù)和構(gòu)造函數(shù)的原型分開寫的問(wèn)題,作者想把他們寫到一起,作者認(rèn)為這才是封裝,那么我們先來(lái)看下封裝是什么,作者對(duì)封裝的理解是否有誤:

封裝,1、在程序上,隱藏對(duì)象的屬性和實(shí)現(xiàn)細(xì)節(jié),僅對(duì)外公開接口,控制在程序中屬性的讀和修改的訪問(wèn)級(jí)別;將抽象得到的數(shù)據(jù)和行為(或功能)相結(jié)合,形成一個(gè)有機(jī)的整體,也就是將數(shù)據(jù)與操作數(shù)據(jù)的源代碼進(jìn)行有機(jī)的結(jié)合,形成“類”,其中數(shù)據(jù)和函數(shù)都是類的成員。

上面是對(duì)封裝的解釋,可以看出這跟作者描述的封裝并不是一個(gè)意思,作者此處所想表達(dá)的實(shí)際上是更好的代碼結(jié)構(gòu)。

建議

既然清楚了作者的意思,來(lái)看下解決辦法,如何將構(gòu)造函數(shù)的定義和原型的定義寫到一起呢,看下我給出的解決辦法:

var Dialog = (function(){
    function Dialog(){
           
    }
    
    Dialog.prototype = {
        init:function(){
            console.log("ok");
        }
    }
    
    return Dialog;
}());


var a = new Dialog();
a instanceof Dialog;

好了問(wèn)題解決了,結(jié)果正確了,而且我們也得到了比較清晰的代碼結(jié)構(gòu)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)