模式是指一個通用問題的解決方案。
模式分三種
設計模式
編碼模式:javascript特有的javascript
反模式:常見的 引起的問題比解決的問題更多的一種方法。java
只有五種基本類型不是對象:數值類型,字符串類型,布爾類型,空類型和未定義類型。編程
函數實際上也是對象,函數有屬性和方法。windows
對象主要有兩種:
原生的(Native)
原生的對象分爲內置對象(數組,日期等) 和用戶自定義對象 (var o={})
主機的(Host)
包含windows對象和全部的DOM對象。設計模式
JavaScript中沒有類。數組
JavaScript沒有繼承,可使用多種方法實現繼承,一般使用原型。瀏覽器
原型是一個對象,而且建立的每個都會自動獲取一個Prototypes屬性,該屬性指向一個新的空對象。緩存
該對象幾乎等同於採用對象字面量或Object()建立的對象,區別在於它的constructor屬性指向了所建立的函數,而不是指向內置的Object()函數。能夠爲該空對象增長成員變量,之後其餘對象也能夠從該對象繼承並像使用本身的屬性同樣使用該對象的屬性。安全
原型就是一個對象,每個函數都有Prototype屬性。app
核心的JavaScript編程語言(不包含DOM,BOM和額外的主機對象)是基於ECMAScript標準(縮寫是ES).
strict模式
避免使用arguments.callee之類的構造函數
ES5 Object.create() 等同於ES3 Object()
暗示全局變量(implied globals):任何變量,若是未經聲明,就爲全局對象全部
JavaScript代碼質量檢查工具
Console對象不是JavaScript語言的一部分,而是瀏覽器提供的一個運行環境。
P15
另外一種建立隱式全局變量的反模式是帶有var聲明的鏈式賦值。
function foo(){ var a=b=0; // 等於 var a=(b=0); }
隱含全局變量不是真正的變量,而是全局對象的屬性。屬性能夠經過delete操做符刪除,但變量不能夠。
P16
訪問全局對象,能夠按以下方式訪問
var global=(function(){return this;}());
P18 提高:JavaScript容許在函數的任意地方聲明多個變量,不管在哪裏聲明,效果都等於在函數頂部進行申明。
P19 在全部的瀏覽器中,經過將HTML容器上須要遍歷的次數緩存起來都會大大提升速度。
P21 使用正常的for循環來處理數組,並使用for-in循環來處理對象。
P23 不要給內置的原型增長屬性
P24使用===和!====操做符對數值和類型進行比較
P26 避免使用eval(),可以使用new Function()代替eval().
P27 使用paresInt()
P45 不要使用new Object()構造函數
Javascript使用函數管理做用域。變量在函數內生命,只在函數內有效。全局變量在函數外部生命,在函數內部無需聲明便可食用。
每一個Javascript環境都有全局對象,可在函數外部使用this進行訪問。
建立的每個全局變量都爲全局對象全部。
myglobal="hello"; //反模式 console.log(myglobal); console.log(window.myglobal); console.log(window['myglobal']); console.log(this.myglobal);
自執行當即生效函數 the self-executing immediate functions
Javascript特性:
1 Javascript可直接使用變量,甚至無需聲明
2 Javascript有個暗示全局變量(implied globals)的概念,任何變量,若是未經聲明,就爲全局對象全部。
function sum(x,y){ //反模式:暗示全局變量 result result=x+y; return result; }
function sum(x,y){ //正確的寫法 var result=x+y; return result; }
另外一種建立隱式全局變量的反模式是帶有var聲明的鏈式賦值
function foo(){ var a=b=0; //反模式 a是局部變量 b是全局變量 }
首先,優先級較高的是表達式b=0,此時b未經聲明。表達式的返回值爲0,它被賦予給局部變量a 。至關於var a=(b=0);
function foo(){ //正確的賦值方式 對鏈式賦值的全部變量都進行了聲明 var a,b; a=b=0; }
隱含全局變量與明肯定義的全局變量的細微不一樣:可否刪除
這代表隱含全局變量是全局對象的屬性,屬性能夠經過delete操做符刪除,但變量不能夠。
//定義三個全局變量 var global_var=1; global_novar=2; //反模式 (function(){ global_fromfunc=3 //反模式 }()); //企圖刪除 delete global_var; //false delete global_novar; //true; delete global_fromfunc; //true; //測試刪除狀況 typeof global_var; //number類型 typeof global_novar; //undefined 類型 typeof global_fromfunc; //undefined類型
var global=(function(){ return this; }());
從內嵌函數的做用域訪問window對象 (不帶硬編碼的方式)
只使用一個var在函數頂部進行變量聲明的模式。
function func(){ var a=1, b=2, sum=a+b, myobject={}, i, j; //函數體 }
function updateElement(){ var el=document.getElementById('result'), style=el.style; //使用el和style再作其餘事... }
Javascript容許在函數的任意地方聲明多個變量,不管在哪裏聲明,效果都等於在函數頂部進行聲明。因此容忍先使用後聲明的狀況。
//反模式 myname="global";//全局變量 function func(){ alert(myname); //未定義 var myname="local"; alert(myname); //局部變量 } func();
前面的代碼等同於下面的代碼
myname="global"; //全局變量 function func(){ var myname; //等同於->var myname=undefined; alert(myname); //未定義 myname="local"; alert(myname); //局部 } func();
for循環常常用在遍歷數組或類數組對象。
好的for循環模式是將已經遍歷過的數組(或容器)的長度緩存起來。如如下代碼所示。
for(var i=0, max=myarray.length;i<max;i++){ //對myarray[i]進行處理 }
單變量模式,能夠將變量放到循環之外
function looper(){ var i=0, max, myarray=[]; //... for(i=0,max=myarray.length;i<max;i++){ //處理myarray[i] } }
++和--提倡 excessive trickiness 過度棘手
改進版,逐步將至0,這樣更快
var i,myarray=[]; for(i=myarray.length;i--;){ //處理myarray[i] }
var myarray=[], i=myarray.length; while(i--){ //處理 myarray[i] }
for-in循環應該用來遍歷非數組對象。使用for-in循環也被稱爲枚舉enumeration
//對象 var woman={ hands:4 }; var man={ hands:2, legs:2, heads:1 }; //代碼的其餘部分 //將一個方法添加到全部對象上 if(typeof Object.prototype.clone==="undefined"){ Object.prototype.clone=function(){ alert('克隆'); }; } //1 //for-in循環 for(var i in man){ if(man.hasOwnProperty(i)){ //filter console.log(i,":",man[i]); } } /* 控制檯中的結果 hands:2 legs:2 heads:1 */ //2 //反模式 //不適用hasOwnProperty()進行檢查後使用for-in循環的結果 for(var i in man){ console.log(i,":",man[i]); } /* 控制檯中的結果 hands:2 legs:2 heads:1 clone:function Object.clone() */
另一種使用hasOwnProperty()的模式是在Object.prototype中調用該函數
for(var i in man){ if(Object.prototype.hasOwnProperty.call(man,i)){ //過濾 console.log(i,":",man[i]); } }
使用hasOwnProperty對man對象進行精煉後,能夠避免命名衝突,也可使用一個本地變量來緩存比較長的屬性名。
var i, hasOwn=Object.prototype.hasOwnProperty; for(var i in man){ if(hasOwn.call(man,i)){ //過濾 console.log(i,":",man[i]); } }
變種 略過花括號
//警告 :不能經過JSLint檢查 var i, hasOwn=Object.prototype.hasOwnProperty; for(i in man) if(hasOwn.call(man,i)){ //過濾 console.log(i,":",man[i]); }
增長構造函數的原型屬性是一個加強功能性的強大的方法,但有時候該方法過於強大。
增長內置構造函數(例如Object(),Array(),Function()等)的原型是頗有誘惑的,但這可能會嚴重影響可維護性。
實在須要增長自定義方法能夠用以下代碼:
if(typeof Object.prototype.myMethod!=="function"){ Object.prototype.myMethod=function(){ //implementation.... } }
var inspect_me=0, result=''; switch(inspect_me){ case 0: result="zero"; break; case 1: result="one"; break default: result="unknown"; }
使用=== 和!==操做符
var zero=0; if(zero===false){ //由於zero是0,而不是false,因此代碼未執行 } //反模式 if(zero==false){ //該代碼會被執行。。。 }
避免使用eval()
var obj={ name:"lilu", } //反模式 var property="name"; alert(eval("obj."+property)); //推薦的方法 var property="name"; alert(obj[property]);
eval()包含安全隱患,這樣作有可能執行被篡改過的代碼。
經過setInterval(), setTimeout()和function()等構造函數傳遞參數,也會致使相似eval的隱患。
//反模式 setTimeout("myFunc()",1000); setTimeout("myFunc(1,2,3)",1000); //推薦模式 setTimeout(myFunc,1000); setTimeout(function(){ myFunc(1,2,3); },1000);
eval()中任何採用var定義的變量會自動變成全局變量。所以能夠經過使用Function()或者將eval()調用封裝到一個即時函數中。
console.log(typeof un); //未定義 console.log(typeof deux); //未定義 console.log(typeof trois); //未定義 var jsstring="var un=1;console.log(un);"; eval(jsstring); //logs "1" jsstring="var deux=2; console.log(deux);"; new Function(jsstring)(); //logs "2" jsstring="var trois=3; console.log(trois);"; (function(){ eval(jsstring); }()); //logs "3"; console.log(typeof un); //數值類型 console.log(typeof deux); //未定義 console.log(typeof trois); //未定義
new Function()和eval()的區別在於eval()會影響到做用於鏈,而Function更多地相似於一個沙盒。不管在哪裏執行Function,它都僅僅能看到全局做用域。 Function的使用和new Function是同樣的。
(function(){ var local=1; eval("local=3; console.log(local)"); //logs 3 console.log(local); //logs 3 }()); (function(){ var local=1; Function("console.log(typeof local);")(); //logs 未定義 }());
每次具體制定進制參數
var month="06", year="09"; month=parseInt(month,10);
year=parseInt(year,10);
//另一個將字符串轉換爲數值的方法是 +"08" //結果是8 Number("08") //8
一致遵循約定比這個具體約定是什麼更爲重要。
JSLint默認值 4個空格縮進
function outer(a, b){ var c = 1, d = 2, inner; if (a > b){ inner = function () { return { r: c - d }; }; } else { inner = function () { return { r: c + d }; }; } return inner; }
應該常用大括號
分號插入機制 semicolon insertion mechanism
構造函數能夠用使用大駝峯命名法
函數和方法名能夠用小駝峯命名法
函數的變量能夠用小寫單詞和下劃線鏈接
變量名所有大寫表明該變量在生命週期中不可變
/** * @tag value */ /** * 反轉一個字符串 * * @param {String} 輸入血藥反轉的字符串 * @return {String} 反轉後的字符串 */ var reverse=function(input){ //... return output; };
http://www.jspatterns.com/book/2/
/** * 個人JavaScript應用程序 * * @module myapp */ var MYAPP = {}; /** * 一個數字工具 * @namespace MYAPP * @class math_stuff */ MYAPP.math_stuff={ /** * Sums two numbers * * @method sum * @param {Number} 是第一個數 * @param {Number} 是第二個數 * @return {Number} 兩個輸入的總和 */ sum: function (a, b) { return a + b; }, /** * Multiplies two numbers * * @method multi * @param {Number} 是第一個數 * @param {Number} 是第二個數 * @return {Number} 兩個輸入相乘後結果 */ multi: function (a, b) { return a * b; } }; /** * Constructs Person objects * @class Person * @constructor * @namespace MYAPP * @param {String} first 是名字 * @param {String} last 是姓氏 */ MYAPP.Person =function (first, last){ /** * 人的姓名 * @property first_name * @type String */ this.first_name=first; /** * Last (family) name of the person * @property last_name * @type String */ this.last_name=last; }; /** * Returns the name of the person object * * @method getName * @return {String} 人的姓名 */ MYAPP.Person.prototype.getName = function () { return this.first_name + ' ' + this.last_name; };