最近閱讀了《JavaScript設計模式與開發實踐》(2015年度最佳推薦),收獲頗多,自己對設計模式有了全新的了解和認識。在項目實踐中也用到了一些,感覺很不錯。
設計模式應遵守的原則:
(1)最少知識原則:一個軟件實體應當盡可能少地與其他實體發生相互作用(把對象劃分成較小的粒度,以便提高復用性)
(2)開放-封閉原則:軟件實體(類、模塊、函數)等應該是可以擴展的,但是不可修改。
### 一、原型模式
JavaScript基于原型的委托機制實現對象與對象之間的繼承。
當對象無法響應某個請求時,會把該請求委托給它自己的原型。
構造器有原型,實例對象沒有原型,有一個名為proto的屬性,其默認指向它的構造器的原型對象,即{constructor}.prototype。
**示例一:**
~~~
var obj = {name: "ligang"};
var A = function() {};
A.prototype = obj;
var a = new A();
console.log(a.name); // ligang
~~~
(1)遍歷對象a中所有屬性,未找到name屬性
(2)查找name屬性的請求被委托給對象a的對象構造器的原型,它被a.**proto**記錄著并且指向A.prototype,而其被設置為對象obj
(3)在對象obj中找到name屬性,并返回。
**示例二:**
~~~
var obj = {name: "ligang"};
var A = function(name) {
this.name = name;
};
A.prototype = obj;
var a = new A();
console.log(a.name); // undefined
~~~
(1)首選遍歷對象a中的所有屬性,存在name屬性,但未賦值
**示例三:**
~~~
var obj = {name: "ligang"};
var A = function() {};
A.prototype = obj;
var B = function() {};
B.prototype = new A();
var b = new B();
console.log(b.name); // ligang
~~~
查找順序:
b對象 –> b.proto(即:B.prototype) –> new A()對象 –> B.prototype –> obj
### 二、this
#### 1. 詞法作用域
首先明確JavaScript只具備詞法作用域(書寫代碼時函數聲明的位置來決定),不具備動態作用域。通過下述示例說明。
備注:詞法作用域詳見:[《JavaScript詞法作用域(你不知道的JavaScript)》](http://blog.csdn.net/ligang2585116/article/details/46367565)
**示例一:**
~~~
function foo() {
console.log(a);
}
function bar() {
var a = 1;
foo();
}
bar(); // ReferenceError
~~~
**示例二:**
~~~
function foo() {
console.log(this.a); // this指向window
}
function bar() {
var a = 1;
foo();
}
bar(); // undefined
~~~
**示例三:**
~~~
function foo() {
console.log(this.a);
}
function bar() {
this.a = 1;
foo();
}
bar(); // 1
~~~
如果JavaScript存在動態作用域,示例一的結果應該為1。
#### 2. this
下面補充幾點this注意事項
備注:關于this詳見:[《JavaScript中的this(你不知道的JavaScript)》](http://blog.csdn.net/ligang2585116/article/details/47059289)
(1)this指向(構造器調用)
**示例一:**
~~~
var MyClass = function() {
this.age = 25;
this.name = "ligang";
};
var obj = new MyClass();
console.log("姓名:" + obj.name + " 年齡:" + obj.age); // 姓名:ligang 年齡:25
~~~
**示例二:**
~~~
var MyClass = function() {
this.age = 25;
this.name = "ligang";
return {
name: "camile"
};
};
var obj = new MyClass();
console.log("姓名:" + obj.name + " 年齡:" + obj.age); // 姓名:camile 年齡:undefined
~~~
**注意:**如果構造器顯式地返回一個object類型的對象,那么此次運算最終返回這個對象,而不是我們之前期待的this。
#### 3. 指定函數內部this指向
call、apply、Function.prototype.bind
其中Function.prototype.bind部分瀏覽器不兼容,兼容性請查看[http://caniuse.com/](http://caniuse.com/)
模擬實現Function.prototype.bind:
~~~
Function.prototype.bind = function() {
var self = this,
context = [].shift.call(arguments),
args = [].slice.call(arguments);
return function() {
return self.apply(context, [].concat.call(args, [].slice.call(arguments)));
};
};
// 測試
var obj = {
name: "ligang"
};
var func = function(a, b, c) {
console.log(this.name);
console.log([a, b, c])
}.bind(obj, 1, 2);
func(3);
~~~
### 三、閉包和高階函數
#### 1. 閉包
對象以方法的形式包含了過程,而閉包則是在過程中一環境的形式包含了數據。
**示例:緩存機制**
~~~
var mult = (function() {
var cache = {};
return function() {
var args = Array.prototype.join.call(arguments, ",");
if(args in cache) {
return cache[args];
}
var a = 1;
for(var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return cache[args] = a;
}
})();
console.log(mult(1, 2, 3));
console.log(mult(1, 2, 3)); // 再次計算,直接從緩存中取
~~~
**注意:**閉包容易導致循環引用,從而導致內存溢出。可以通過把這些變量置為null,回收這些變量。
#### 2. 高階函數
高階函數:函數作為參數傳遞;函數作為返回值輸出。
(1)回調函數
~~~
function fun(callback) {
console.log(1);
if(typeof callback === 'function'){
setTimeout(function() {
callback();
});
}
}
fun(function(){console.log(2);});
~~~
(2)判斷數據類型
~~~
var isType = function(type) {
return function(obj) {
return Object.prototype.toString.call(obj) === '[object' + type + ']';
}
};
var isArray = isType("Array");
var isNumber = isType("Number");
isArray([]);
isNumber("123");
~~~
(3)單例
~~~
var getSingle = function(fn) {
var result;
return function() {
return result || (result = fn.apply(this, arguments));
};
};
function testSingle(){}
getSingle(testSingle)() === getSingle(testSingle)(); // true
~~~
轉載請標明出處:[http://blog.csdn.net/ligang2585116](http://blog.csdn.net/ligang2585116)!