- 相關推薦
常用的JavaScript模式
常用的JavaScript模式有哪些?模式是解決或者避免一些問題的方案。下面YJBYS小編為大家列出幾種常用的JavaScript模式,歡迎大家閱讀學習!
在JavaScript中,會用到一些常用的編碼模式。下面就列出了一些常用的JavaScript編碼模式,有的模式是為了解決特定的問題,有的則是幫助我們避免一些JavaScript中容易出現(xiàn)的錯誤。
單一var模式
所謂“單一var模式”(Single var pattern)就是指在函數(shù)頂部,只使用一個var進行變量聲明的模式。例如:
function func() {
var a = 1,
b = 2,
sum = a + b,
myObject = {}, i,
j;
// other code}
使用這個模式的好處:
在函數(shù)頂部展示了所有函數(shù)中使用的局部變量
防止變量提升引起的問題
變量提升
JavaScript允許在函數(shù)的任意地方聲明變量,但是效果都等同于在函數(shù)頂部進行聲明,這個是所謂的變量提升(Hoisting)。
看一個例子:
var num = 10;
function func() {
alert(num); // undefined
var num = 1;
alert(num); // 1}func();
從這個例子可以看到,第一次alert的值并不是10,而是undefined。所以,應該盡量使用“單一var模式”來避免類似的問題。
關于變量提升的細節(jié),請參考我前面一篇JavaScript的執(zhí)行上下文。
for-in循環(huán)
在JavaScript中,for-in循環(huán)主要用來枚舉對象的屬性。
但是,由于JavaScript中原型鏈的存在,一般都會結(jié)合hasOwnProperty()來使用for-in循環(huán),從而過濾原型鏈上的非該對象的屬性。
var wilber = {
name: "Wilber",
age: 28,
gender: "male"};Object.prototype.printPersonalInfo = function() { console.log(this.name, "is", this.age, "years old");
};for(var prop in wilber) { if(wilber.hasOwnProperty(prop)) { console.log(prop, ":", wilber[prop]);
}
}
開放的大括號位置
根據(jù)開發(fā)人員的習慣,開放大括號的位置會有不同的選擇,可以和語句放在同一行,也可以放在新的一行:
var total = 10;if(tatal > 5) { console.log("bigger than 5");
}if(tatal > 5)
{ console.log("bigger than 5");
}
兩種形式的代碼都能實現(xiàn)同樣的邏輯,但是,JavaScript允許開發(fā)人員省略分號,JavaScript的分號插入機制(semicolon insertion mechanism)會負責加上省略的分號,這時開放大括號的位置不同就可能產(chǎn)生不同的結(jié)果。
看一個例子:
function func() { return
{
name: "Wilber"
};
}
alert(func());// undefined
之所以得到的結(jié)果是undefined就是因為JavaScript的分號插入機制,在return語句之后自動添加了分號。
調(diào)整一下開放的大括號的位置就可以避免這個問題:
function func() { return {
name: "Wilber"
};
}
alert(func());// [object]
所以,關于開放的大括號位置,建議將開放的大括號放置在前面語句的同一行。
強制new模式
JavaScript中,通過new關鍵字,可以用構(gòu)造函數(shù)來創(chuàng)建對象,例如:
function Person(name, city) { this.name = name; this.city = city;
this.getInfo = function() { console.log(this.name, "lives at", this.city);
}
}var will = new Person("Will", "Shanghai");
will.getInfo();// Will lives at Shanghai
但是,如果開發(fā)人員忘記了new關鍵字,那么構(gòu)造函數(shù)中的this將代表全局對象(瀏覽器中就是window對象),所有的屬性將會變成全局對象的屬性。
function Person(name, city) { this.name = name; this.city = city;
this.getInfo = function() { console.log(this.name, "lives at", this.city);
}
}var will = Person("Will", "Shanghai");console.log(will.name);// Uncaught TypeError: Cannot read property 'name' of undefinedconsole.log(window.name);// Willconsole.log(window.city);// Shanghaiwindow.getInfo();// Will lives at Shanghai
所以,為了避免這類問題的方式,首先是從代碼規(guī)范上下手。建議對于所有的JavaScript構(gòu)造函數(shù)的命名方式都遵循,構(gòu)造函數(shù)使用首字母大寫的命名方式。
這樣當我們看到首字母大寫的函數(shù),就要考慮是不是漏掉了new關鍵字。
自調(diào)用構(gòu)造函數(shù)
當然除了規(guī)范之外,還可以通過代碼的方式來避免上面的問題。
具體的做法就是,在構(gòu)造函數(shù)中檢查this是否為構(gòu)造函數(shù)的一個實例,如果不是,構(gòu)造函數(shù)可以通過new關鍵字進行自調(diào)用。
下面就是使用自調(diào)用構(gòu)造函數(shù)對上面的例子進行改進:
function Person(name, city) { if(!(this instanceof Person)) { return new Person(name, city);
}
this.name = name; this.city = city;
this.getInfo = function() { console.log(this.name, "lives at", this.city);
}
}var will = Person("Will", "Shanghai");console.log(will.name);// Willconsole.log(will.city);// Shanghaiwill.getInfo();// Will lives at Shanghaiwindow.getInfo();// Uncaught TypeError: window.getInfo is not a function
結(jié)合構(gòu)造函數(shù)的命名約定和自調(diào)用的構(gòu)造函數(shù),這下就不用擔心漏掉new關鍵字的情況了。
數(shù)組性質(zhì)檢查
當在JavaScript中判斷一個對象是不是數(shù)組的時候,不能直接使用typeof,因為我們會得到object。
在ECMA5中提出了Array.isArray()這個函數(shù),我們可以直接使用來判斷一個對象是不是數(shù)組類型。
對于不支持ECMA5的環(huán)境,我們可以通過下面的方式自己實現(xiàn)Array.isArray()這個函數(shù)。
if(typeof Array.isArray === "undefined") { Array.isArray = function(arg){ return Object.prototype.toString.call(arg) === "[object Array]";
};
}var arr = [];console.log(Array.isArray(arr));// true
立即執(zhí)行函數(shù)
立即執(zhí)行函數(shù)是JavaScript中非常常用的一種模式,形式如下:
(function() {
// other code
}());
通過這個模式可以提供一個局部的作用域,所以函數(shù)代碼都會在局部作用域中執(zhí)行,不會污染其他作用域。
現(xiàn)在的很多JavaScript庫都直接使用了這種模式,例如JQuery、underscore等等。
立即執(zhí)行函數(shù)的參數(shù)
關于立即執(zhí)行函數(shù)另外一點需要注意的地方就是立即執(zhí)行函數(shù)的參數(shù)。
我們可以像正常的函數(shù)調(diào)用一樣進行參數(shù)傳遞:
(function(name, city) {
console.log(name, "lives at", city);}("Wilber", "Shanghai"));// Wilber lives at Shanghai
在立即執(zhí)行函數(shù)中,是可以訪問外部作用域的(當然包括全局對象),例如:
var name = "Wilber";var city = "Shanghai";
(function() { console.log(name, "lives at", city);
}());// Wilber lives at Shanghai
但是,如果立即執(zhí)行函數(shù)需要訪問全局對象,常用的模式就是將全局對象以參數(shù)的方式傳遞給立即執(zhí)行函數(shù)。
var name = "Wilber";var city = "Shanghai";
(function(global) { console.log(global.name, "lives at", global.city);
}(this));// Wilber lives at Shanghai
這樣做的好處就是,在立即執(zhí)行函數(shù)中訪問全局變量的屬性的時候就不用進行作用域鏈查找了,關于更多JavaScript作用域鏈的內(nèi)容,可以參考理解JavaScript的作用域鏈。
初始化時分支
初始化時分支(Init-time Branching)是一種常用的優(yōu)化模式,就是說當某個條件在整個程序聲明周期內(nèi)都不會發(fā)生改變的時候,不用每次都對條件進行判斷,僅僅一次判斷就足夠了。
這里最常見的例子就是對瀏覽器的檢測,在下面的例子中,每次使用utils.addListener1屬性的時候都要進行瀏覽器判斷,效率比較低下:
var utils = {
addListener: function(el, type, fn) {
if (typeof window.addEventListener === 'function') {
el.addEventListener(type, fn, false);
} else if (typeof document.attachEvent === 'function') { // IE el.attachEvent('on' + type, fn);
} else { // older browsers el['on' + type] = fn;
}
},
removeListener: function(el, type, fn) {
// pretty much the same... }
};
所以,根據(jù)初始化時分支模式,可以在腳本初始化的時候進行一次瀏覽器檢測,這樣在以后使用utils的時候就不必進行瀏覽器檢測了:
// the interfacevar utils = {
addListener: null,
removeListener: null};// the implementationif (typeof window.addEventListener === 'function') {
utils.addListener = function(el, type, fn) {
el.addEventListener(type, fn, false);
};
utils.removeListener = function(el, type, fn) {
el.removeEventListener(type, fn, false);
};
} else if (typeof document.attachEvent === 'function') { // IE utils.addListener = function(el, type, fn) {
el.attachEvent('on' + type, fn);
};
utils.removeListener = function(el, type, fn) {
el.detachEvent('on' + type, fn);
};
} else { // older browsers utils.addListener = function(el, type, fn) {
el['on' + type] = fn;
};
utils.removeListener = function(el, type, fn) {
el['on' + type] = null;
};
}
命名空間模式
JavaScript代碼中,過多的全局變量經(jīng)常會引發(fā)一些問題,比如命名沖突。
結(jié)合命名空間模式就可以一定程度上減少代碼中全局變量的個數(shù)。
下面就看一個通用命名空間函數(shù)的實現(xiàn):
var MYAPP = MYAPP || {};
MYAPP.namespace = function (ns_string) { var parts = ns_string.split('.'), parent = MYAPP,
i; // strip redundant leading global
if (parts[0] === "MYAPP") {
parts = parts.slice(1);
} for (i = 0; i < parts.length; i += 1) { // create a property if it doesn't exist
if (typeof parent[parts[i]] === "undefined") { parent[parts[i]] = {};
} parent = parent[parts[i]];
} return parent;
};
結(jié)合這個通用命名空間函數(shù)的,就可以實現(xiàn)代碼的模塊化:
// assign returned value to a local varvar module2 = MYAPP.namespace('MYAPP.modules.module2');
module2 === MYAPP.modules.module2; // true// skip initial `MYAPP`MYAPP.namespace('modules.module51');// long namespaceMYAPP.namespace('once.upon.a.time.there.was.this.long.nested.property');
聲明依賴關系
JavaScirpt庫通常是通過命名空間來進行模塊化,當我們在代碼中使用第三方的庫的時候,可以只引入我們代碼依賴的模塊。
所謂聲明依賴關系,就是指在函數(shù)或者模塊的頂部是聲明代碼需要依賴哪些模塊,這個聲明包括創(chuàng)建一個局部變量,并將它們指向你需要的模塊:
var myFunction = function () { // dependencies
var event = YAHOO.util.Event,
dom = YAHOO.util.Dom; // use event and dom variables
// for the rest of the function...};
通過聲明依賴關系這種模式,會給我們帶來很多好處:
明確的依賴聲明可以向你的代碼的使用者表明這些特殊的腳本文件需要被確保包含進頁面
數(shù)頭部的聲明解,讓發(fā)現(xiàn)和處理依賴關系更加簡單
局部變量(比如:dom)通常比使用全局變量(比如:YAHOO)快,比訪問全局對象的屬性(比如:YAHOO.util.Do)更快,可以得到更好的性能,全局符號只會在函數(shù)中出現(xiàn)一次,然后就可以使用局部變量,后者速度更快。
壓縮工具比如YUICompressor 和 Google Closure compiler會重命名局部變量,產(chǎn)生更小的體積的代碼,但從來不會重命名全局變量,因為那樣是不安全的
代碼復用模式
下面就看看JavaScript中的代碼復用模式。一般來說,通常使用下面的方式來實現(xiàn)代碼的復用:
繼承
借用方法
繼承
在JavaScript中可以很方便的通過原型來實現(xiàn)繼承。
關于原型式繼承,ECMA5通過新增Object.create()方法規(guī)范化了原型式繼承。這個方法接收兩個參數(shù):
一個用作新對象原型的對象
一個為新對象定義額外屬性的對象(可選的)
看一個使用Object.create()的例子:
utilsLibC = Object.create(utilsLibA, { sub: {
value: function(){
console.log("sub method from utilsLibC");
}
},
mult: {
value: function(){
console.log("mult method from utilsLibC");
}
},
})
utilsLibC.add();// add method from utilsLibA
utilsLibC.sub();// sub method from utilsLibCutilsLibC.mult();
// mult method from utilsLibC
console.log(utilsLibC.__proto__);// Object {add: (), sub: (), __proto__: Object}console.log(utilsLibC.__proto__.constructor);
// function Object() { [native code] }
關于JavaScript繼承的更多信息,可以參考關于JavaScript繼承的那些事。
借用方法
有時候可能只需要一個已經(jīng)存在的對象的一個或兩個方法,但是又不想通過繼承,來建立額外的父子(parent-child)關系。
這時就可以考慮使用借用方法模式完成一些函數(shù)的復用。借用方法模式得益于function的方法call()和apply()。
這種模式一個常見用法就是借用數(shù)組方法。
數(shù)組擁有有用的方法,那些類數(shù)組對象(array-like objects)比如arguments類數(shù)組對象(array-like objects)比如arguments沒有的方法。所以arguments可以借用數(shù)組的方法,比如slice()方法,看一個例子:
function f() { var args = [].slice.call(arguments, 1, 3); return args;
}// examplef(1, 2, 3, 4, 5, 6); // returns [2,3]
【常用的JavaScript模式】相關文章:
JavaScript常用方法匯總03-08
對javascript的理解03-29
最常用的20個javascript方法函數(shù)03-10
JavaScript的課堂講解03-31
JavaScript 基礎教學04-01
JavaScript學習筆記03-30
Javascript的this用法簡述03-25