計劃按以下順序完成這篇筆記:
安全
這是筆記的第4篇,聊聊閉包/getter/setter,看看JavaScript中的變量做用域和實現封裝的方法。
閉包
做者博客:http://blog.csdn.net/stationxpapp
做者微博:http://weibo.com/liuhailong2008函數
轉載請取得做者贊成工具
閉包是JavaScript一項簡單實用的語言特性。經過閉包:
彌補了函數沒有public/private等訪問訪問權限控制符的缺陷,保護了函數內部變量的安全。
使得函數對象在做爲參數傳遞時,不只僅傳遞運算邏輯,同時也傳遞了相關變量。
使得函數「類」的不一樣實例,獨享本身的屬性。
下面逐個來看。
ui
閉包最主要的特性是:當函數做爲返回值時,連同函數定義時的環境(包括函數外部,函數能夠訪問到的變量)一塊兒返回,確保這些變量不因其依附的對象銷燬而被銷燬。
有點兒繞,具體什麼意思呢?看下面的代碼:
// 代碼段1
function UiObject() {
var childCount = 0; // 函數內部變量,
return 0;
}
var funcReturnValue = UiObject(); // 調用函數,返回0
console.log(UiObject.childCount); // 輸出:undefined,因爲函數調用完成,內部變量已經被銷燬
childCount隨着UiObject函數的調用結束而銷燬,從另外一個角度看,保護了函數內部的變量。
在這個基礎上,若是咱們有一種辦法能夠確保函數的內部變量不被銷燬,並且提供方法對其訪問操做,也就實現了經過public方法訪問 private 變量。
代碼以下:
// 代碼段2
function UiObject() {
var childCount = 0; // 函數內部變量
function getChildCount(){
childCount = 6;
return childCount;
}
return getChildCount;
}
var funcReturnFObject = UiObject(); // 調用函數,返回 getChildCount 函數,返回值是一個閉包
console.log(funcReturnFObject()); // 輸出:6
下面咱們就看看,若是利用閉包的這一特性實現對私有屬性的保護。
this
在代碼段2的基礎上改進,代碼段3展現的代碼,能夠同時提供多個方法,對私有屬性訪問。
//代碼段3
function UiObject() {
var childCount = 0; // 函數內部變量
return {
getChildCount : function (){
return childCount;
},
setChildCount : function (cnt){
childCount = cnt;
}
};
}
var o = UiObject(); // 調用函數,返回 getChildCount 函數,返回值是一個閉包
o.setChildCount(6);
console.log(o.getChildCount()); // 輸出:6
思考: 若是 var childCount = 0 ; 改成 this.childCount = 0; 呢?
咱們以前研究過this,「 this.childCount = 0; 」語句是將childCount附加爲調用者的屬性,this.childCount的將生命週期與調用者相同。這與閉包不衝突。
代碼段3還能夠寫成代碼段4的形式。
// 代碼段4
function UiObject() {
var childCount = 0; // 函數內部變量
this.setChildCount = function(cnt){
childCount = cnt;
};
this.getChildCount = function(){
return childCount;
};
}
var ui = new UiObject(); // 調用函數,返回retObj
console.log(ui.childCount); // 輸出:undefined,因爲函數調用完成,局部變量已經被銷燬
ui.setChildCount(3); // 因爲閉包的做用,ui仍然保存着變量childCount,並對其操做
console.log(ui.getChildCount()); // 輸出:3
「this.setChildCount = function(cnt){ childCount = cnt; };」 這個語句至關於在UiObject內部定義了函數並「傳遞」了給ui對象。一樣產生了閉包。
spa
經過上面的例子,咱們已經看到了這點特性。
在具體的應用場景中可對其大加利用。
.net
咱們已經知道了,函數在傳遞過程當中,會產生一個閉包。對於同一方法產生的閉包,是相同的,仍是爲每次傳遞建立了不一樣的拷貝呢?
看下面的代碼:
//代碼段5
function UiObject() {
var childCount = 0; // 函數內部變量
return {
getChildCount : function (){
return childCount;
},
setChildCount : function (cnt){
childCount = cnt;
}
};
}
var ui1 = UiObject();
var ui2 = UiObject();
ui1.setChildCount(1);
ui2.setChildCount(2);
console.log(ui1.getChildCount()); // output : 1
console.log(ui2.getChildCount()); // output : 2
每次生成閉包是不一樣的拷貝。
思考:對比Java,加深理解。
prototype
JavaScript
//代碼段6
var uiPanel ={
_type : 'Panel',
_width : -1,
_height: -1,
get type(){ return this._type;},
get width(){ return this._width; },
set width(v){this._width = v;},
get height(){ return this._height; },
set height(v){this._height = v;}
};
uiPanel.type = 'TextField'; // does not work
console.log('type:'+uiPanel.type); // ouput : type:Panel
uiPanel.width = 800; //
console.log('width:'+uiPanel.width); // ouput : width:800
語法上愈加有面向對象的範兒了。
除了在定義時經過set/set關鍵字控制屬性的讀寫權限。還能夠在運行期經過 Object.defineProperty()函數動態添加屬性,並提供更精細的控制。
代碼以下(如下代碼均未經實驗):
var o = {};
Object.defineProperty(o,'propName',{
value:1, //屬性的值,也能夠經過 get:function(){retun x;}的方法設定
writeable:true,//是否能夠經過o.propName = newValue ; 的方法設置屬性的值
enumerable:false,//是否能夠經過被枚舉
configurable:true//是否能夠經過defineProperty配置
});
另外,還有一系列API能夠完成對屬性的配置、檢測。以下:
Object.getOwnPropertyDescriptor{{x:1},"x"}
Object.keys(obj); // 得到對象上全部可枚舉的「實例屬性」
Object.getOwnPropertyNames(obj) ;//得到對象上全部的「實例屬性」
obj.hasOwnProperty(‘id’); //只要該對象obj擁有屬性id, 不管id是否可枚舉,都返回true
當大多數人已經習慣了面向對象的思惟和方法,語言就要從特性方面予以知足,這不合理,也不美。但世界就是如此不完美。
JavsScript天生麗質,非要把她改形成機甲戰士,那之後誰來負責傾國傾城呢?
ECMAScript 5不是革命性創新,也不是救命稻草,這個世界原本能夠更好的。