欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于JavaScript如何實(shí)現(xiàn)私有成員的語法特征及私有成員的實(shí)現(xiàn)方式

 更新時(shí)間:2015年10月28日 10:43:07   投稿:mrr  
本文給大家介紹基于JavaScript如何實(shí)現(xiàn)私有成員的語法特征及私有成員的實(shí)現(xiàn)方式,涉及到j(luò)avascript語法特征相關(guān)知識(shí),對(duì)本文感興趣的朋友快來一起學(xué)習(xí)吧

前言

在面向?qū)ο蟮木幊谭妒街?,封裝都是必不可少的一個(gè)概念,而在諸如 Java,C++等傳統(tǒng)的面向?qū)ο蟮恼Z言中, 私有成員是實(shí)現(xiàn)封裝的一個(gè)重要途徑。但在 JavaScript 中,確沒有在語法特性上對(duì)私有成員提供支持, 這也使得開發(fā)人員使出了各種奇技淫巧去實(shí)現(xiàn) JS 中的私有成員,以下將介紹下目前實(shí)現(xiàn) JS 私有成員特性的幾個(gè)方案以及它們之間的優(yōu)缺點(diǎn)對(duì)比。

現(xiàn)有的一些實(shí)現(xiàn)方案

約定命名方案

約定以下劃線'_'開頭的成員名作為私有成員,僅允許類成員方法訪問調(diào)用,外部不得訪問私有成員。簡(jiǎn)單的代碼如下:

JavaScript

var MyClass = function () {
  this._privateProp = ‘privateProp';
};
MyClass.prototype.getPrivateProp = function () {
  return this._privateProp;
};
var my = new MyClass();
alert(my.getPrivateProp()); // ‘privateProp';
alert(my._privateProp); // 并未真正隱藏,依然彈出 ‘privateProp'

優(yōu)點(diǎn)

毫無疑問,約定命名是最簡(jiǎn)單的私有成員實(shí)現(xiàn)方案,沒有代碼層面上的工作。
調(diào)試方便,能夠在控制臺(tái)上直接看到對(duì)象上的私有成員,方便排查問題。
兼容性好,ie6+都支持

不足

無法阻止外部對(duì)私有成員的訪問和變更,如果真有不知道或者不遵守約定的開發(fā)人員變更私有屬性,也是無能為力。
必須強(qiáng)制或說服大家遵守這個(gè)約定,當(dāng)然這個(gè)在有代碼規(guī)范的團(tuán)隊(duì)中不是什么太大的問題。

es6 symbol 方案

在 es6中,引入了一個(gè) Symbol 的特性,該特性正是為了實(shí)現(xiàn)私有成員而引入的。
主要的思路是,為每一個(gè)私有成員的名稱產(chǎn)生一個(gè)隨機(jī)且唯一的字符串key,這個(gè) key 對(duì)外不可見,對(duì)內(nèi)的可見性則是通過 js 的閉包變量實(shí)現(xiàn),示例代碼如下:

JavaScript

(function() {
   var privateProp = Symbol(); // 每次調(diào)用會(huì)產(chǎn)生一個(gè)唯一的key
   function MyClass() {
     this[privateProp] = ‘privateProp'; // 閉包內(nèi)引用到這個(gè) key
   }
   MyClass.prototype.getPrivateProp = function () {
     return this[privateProp];
   };
})();
var my = new MyClass();
alert(my.getPrivateProp()); // ‘privateProp';
alert(my.privateProp); // 彈出 undefined,因?yàn)槌蓡T的key其實(shí)是隨機(jī)字符串

優(yōu)點(diǎn)

彌補(bǔ)了命名約定方案的缺陷,外部無法通過正常途徑獲得私有成員的訪問權(quán)。
調(diào)試便捷程度上可以接受,一般是通過給 symbol 的構(gòu)造函數(shù)傳入一個(gè)字符串參數(shù),則控制臺(tái)上對(duì)應(yīng)的私有屬性名稱會(huì)展示為:Symbol(key)

兼容性不錯(cuò),不支持 Symbol的瀏覽器可以很容易的 shim 出來。

不足

寫法上稍顯別扭,必須為每一個(gè)私有成員都創(chuàng)建一個(gè)閉包變量讓內(nèi)部方法可以訪問。
外部還是可以通過 Object.getOwnPropertySymbols的方式獲取實(shí)例的 symbol 屬性名稱,通過該名稱獲得私有成員的訪問權(quán)。這種場(chǎng)景出現(xiàn)得比較少,且知道這種途徑的開發(fā)人員水平相信都是有足夠的能力知道自己的行為會(huì)有什么影響,因此這個(gè)不足點(diǎn)也算不上真正意義的不足。

es6 WeakMap 方案

在 es6 中引入了 Map, WeakMap 容器,最大的特點(diǎn)是容器的鍵名可以是任意的數(shù)據(jù)類型,雖說初衷不是為了實(shí)現(xiàn)私有成員引入,但意外的可以被用來實(shí)現(xiàn)私有成員特性。

主要的思路是,在類的級(jí)別上創(chuàng)建一個(gè) WeakMap 容器,用于存儲(chǔ)各個(gè)實(shí)例的私有成員,這個(gè)容器對(duì)外不可見,對(duì)內(nèi)通過閉包方式可見;內(nèi)部方法通過將實(shí)例作為鍵名獲取容器上對(duì)應(yīng)實(shí)例的私有成員,示例代碼如下:

JavaScript

(function() {
   var privateStore = new WeakMap(); // 私有成員存儲(chǔ)容器
   function MyClass() {
     privateStore.set(this, {privateProp: ‘privateProp'}); // 閉包內(nèi)引用到privateStore, 用當(dāng)前實(shí)例做 key,設(shè)置私有成員
   }
   MyClass.prototype.getPrivateProp = function () {
     return privateStore.get(this).privateProp; 
   };
})();
var my = new MyClass();
alert(my.getPrivateProp()); // ‘privateProp';
alert(my.privateProp); // 彈出 undefined,實(shí)例上并沒有 privateProp 屬性

優(yōu)點(diǎn)

彌補(bǔ)了命名約定方案的缺陷,外部無法通過正常途徑獲得私有成員的訪問權(quán)。
對(duì) WeakMap 做一些封裝,抽出一個(gè)私有特性的實(shí)現(xiàn)模塊,可以在寫法上相對(duì) Symbol 方案更加簡(jiǎn)潔干凈,其中一種封裝的實(shí)現(xiàn)可以查看參考文章3。
最后一個(gè)是個(gè)人認(rèn)為最大的優(yōu)勢(shì):基于 WeakMap 方案,可以方便的實(shí)現(xiàn)保護(hù)成員特性(這個(gè)話題會(huì)在其他文章說到:))

不足

不好調(diào)試,因?yàn)槭撬接谐蓡T都在閉包容器內(nèi),無法在控制臺(tái)打印實(shí)例查看對(duì)應(yīng)的私有成員
待確認(rèn)的性能問題,根據(jù) es6的相關(guān)郵件列表,weakmap 內(nèi)部似乎是通過順序一一對(duì)比的方式去定位 key 的,時(shí)間復(fù)雜度為 O(n),和 hash 算法的 O(1)相比會(huì)慢不少

最大的缺陷則是兼容性帶來的內(nèi)存膨脹問題,在不支持 WeakMap 的瀏覽器中是無法實(shí)現(xiàn) WeakMap 的弱引用特性,因此實(shí)例無法被垃圾回收。 比如示例代碼中 privateProp 是一個(gè)很大的數(shù)據(jù)項(xiàng),無弱引用的情況下,實(shí)例無法回收,從而造成內(nèi)存泄露。

現(xiàn)有實(shí)現(xiàn)方案小結(jié)

從上面的對(duì)比來看,Symbol方案最大優(yōu)勢(shì)在于很容易模擬實(shí)現(xiàn);而WeakMap的優(yōu)勢(shì)則是能夠?qū)崿F(xiàn)保護(hù)成員, 現(xiàn)階段無法忍受的不足是無法模擬實(shí)現(xiàn)弱引用特性而導(dǎo)致的內(nèi)存問題。于是我的思路又轉(zhuǎn)向了將兩者優(yōu)勢(shì)結(jié)合起來的方向。

Symbol + 類WeakMap 的整合方案

在 WeakMap 的方案中最大的問題是無法 shim 弱引用,較次要的問題是不大方便調(diào)試。

shim 出來的 WeakMap 主要是無法追溯實(shí)例的生命周期,而實(shí)例上的私有成員的生命周期又是依賴實(shí)例, 因此將實(shí)例級(jí)別的私有成員部分放在實(shí)例上不就好了? 實(shí)例沒了,自然其屬性也隨之摧毀。而私有存儲(chǔ)區(qū)域的隱藏則可以使用 Symol 來做。

該方案的提供一個(gè) createPrivate 函數(shù),該函數(shù)會(huì)返回一個(gè)私有的 token 函數(shù),對(duì)外不可見,對(duì)內(nèi)通過閉包函數(shù)獲得, 傳入當(dāng)前實(shí)例會(huì)返回當(dāng)前實(shí)例的私有存儲(chǔ)區(qū)域。使用方式如下:

JavaScript

(function() {
   var $private = createPrivate(); // 私有成員 token 函數(shù),可以傳入對(duì)象參數(shù),會(huì)作為原型鏈上的私有成員
   function MyClass() {
     $private(this).privateProp = ‘privateProp' ; // 閉包內(nèi)引用到privateStore, 用當(dāng)前實(shí)例做 key,設(shè)置私有成員
   }
   MyClass.prototype.getPrivateProp = function () {
     return $private(this).privateProp; 
   };
})();
var my = new MyClass();
alert(my.getPrivateProp()); // ‘privateProp';
alert(my.privateProp); // 彈出 undefined,實(shí)例上并沒有 privateProp 屬性

代碼中主要就是實(shí)現(xiàn) createPrivate 函數(shù),大概的實(shí)現(xiàn)如下:

JavaScript

// createPrivate.js
function createPrivate(prototype) {
  var privateStore = Symbol('privateStore');
  var classToken = Symbol(‘classToken');
  return function getPrivate(instance) {
     if (!instance.hasOwnProperty(privateStore)) {
       instance[privateStore] = {};
     }
    var store = instance[classToken];
     store[token] = store[token] || Object.create(prototype || {});
     return store[token];
   };
}

上述實(shí)現(xiàn)做了兩層存儲(chǔ),privateStore 這層是實(shí)例上統(tǒng)一的私有成員存儲(chǔ)區(qū)域,而 classToken 對(duì)應(yīng)的則是繼承層次之間不同類的私有成員定義,基類有基類的私有成員區(qū)域,子類和基類的私有成員區(qū)域是不同的。

當(dāng)然,只做一層的存儲(chǔ)也可以實(shí)現(xiàn),兩層存儲(chǔ)僅僅是為了調(diào)試方便,可以直接在控制臺(tái)通過Symbol(‘privateStore')這個(gè)屬性來查看實(shí)例各個(gè)層次的私有部分。

奇葩的 es5 property getter 攔截方案

該方案純粹是閑得無聊玩了玩,主要是利用了 es5 提供的 getter,根據(jù) argument.callee.caller 去判斷調(diào)用場(chǎng)景,如果是外部的則拋異?;蚍祷?undefined, 如果是內(nèi)部調(diào)用則返回真正的私有成員,實(shí)現(xiàn)起來比較復(fù)雜,且不支持 strict 模式,不推薦使用。 有興趣的同學(xué)可以看看實(shí)現(xiàn)。

總結(jié)

以上幾個(gè)方案對(duì)比下來,我個(gè)人是傾向 Symbol+WeakMap 的整合方案,結(jié)合了兩者的優(yōu)點(diǎn),又彌補(bǔ)了 WeakMap 的不足和 Symbol 書寫的冗余。 當(dāng)然了,我相信隨著 JS 的發(fā)展,私有成員和保護(hù)成員也遲早會(huì)在語法層面上進(jìn)行支持,正如 es6 對(duì) class 關(guān)鍵字和 super 語法糖的支持一樣, 只是現(xiàn)階段需要開發(fā)者使用一些技巧去填補(bǔ)語言特性上的空白。

Javascript私有成員的實(shí)現(xiàn)方式

總體來講這本書還是可以的,但看完這本書還留了幾個(gè)問題一直困擾著我,如js中私有變量的實(shí)現(xiàn),prototype等,經(jīng)過自己一系列測(cè)試,現(xiàn)在終于弄明白了。

很多書上都是說,Javascript是不能真正實(shí)現(xiàn)Javascript私有成員的,因此在開發(fā)的時(shí)候,統(tǒng)一約定 __ 兩個(gè)下劃線開頭為私有變量。

后來,發(fā)現(xiàn)Javascript中閉包的特性,從而徹底解決了Javascript私有成員的問題。

 function testFn(){ 
    var _Name;//定義Javascript私有成員 
    this.setName = function(name){ 
     _Name = name; //從當(dāng)前執(zhí)行環(huán)境中獲取_Name 
    } 
    this.getName = function(){ 
     return _Name; 
    } 
}// End testFn 
var test = testFn(); 
alert(typeof test._Name === "undefined")//true 
test.setName("KenChen"); 

test._Name 根本訪問不到,但是用對(duì)象方法能訪問到,因?yàn)殚]包能從當(dāng)前的執(zhí)行環(huán)境中獲取信息。

接下來我們看看,共有成員是怎樣實(shí)現(xiàn)的

function testFn(name){ 
  this.Name = name; 
  this.getName = function(){ 
   return this.Name; 
  } 
} 
var test = new testFn("KenChen"); 
test.getName(); //KenChen 
test.Name = "CC"; 
est.getName();//CC 

接下來在看看類靜態(tài)變量是怎樣實(shí)現(xiàn)的

function testFn(){ 
} 
testFn.Name = "KenChen"; 
alert(testFn.Name);//KenChen 
testFn.Name = "CC"; 
alert(testFn.Name);//CC 

相關(guān)文章

最新評(píng)論