javascript模式 讀書筆記一

 

第一章 簡介

模式

模式是指一個通用問題的解決方案。
模式分三種
設計模式
編碼模式:javascript特有的javascript

反模式:常見的 引起的問題比解決的問題更多的一種方法。java

JavaScript:基本概念

面向對象

只有五種基本類型不是對象:數值類型,字符串類型,布爾類型,空類型和未定義類型。編程

函數實際上也是對象,函數有屬性和方法。windows


對象主要有兩種:
原生的(Native) 
    原生的對象分爲內置對象(數組,日期等) 和用戶自定義對象 (var o={})
主機的(Host)
     包含windows對象和全部的DOM對象。設計模式

沒有類

JavaScript中沒有類。數組

原型(Prototypes)

JavaScript沒有繼承,可使用多種方法實現繼承,一般使用原型。瀏覽器

原型是一個對象,而且建立的每個都會自動獲取一個Prototypes屬性,該屬性指向一個新的空對象。緩存

該對象幾乎等同於採用對象字面量或Object()建立的對象,區別在於它的constructor屬性指向了所建立的函數,而不是指向內置的Object()函數。能夠爲該空對象增長成員變量,之後其餘對象也能夠從該對象繼承並像使用本身的屬性同樣使用該對象的屬性。安全

原型就是一個對象,每個函數都有Prototype屬性。app

ECMAScript 5

核心的JavaScript編程語言(不包含DOM,BOM和額外的主機對象)是基於ECMAScript標準(縮寫是ES).

strict模式

避免使用arguments.callee之類的構造函數

ES5 Object.create() 等同於ES3 Object()

暗示全局變量(implied globals):任何變量,若是未經聲明,就爲全局對象全部

JSLint

JavaScript代碼質量檢查工具

Console

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; 
}

 

變量釋放時的反作用

隱含全局變量與明肯定義的全局變量的細微不一樣:可否刪除

  • 使用var建立的全局變量(這類變量在函數外部建立)不能刪除
  • 不使用var建立的隱性全局變量(儘管是在函數內部建立)能夠刪除

這代表隱含全局變量是全局對象的屬性,屬性能夠經過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模式(Single var Pattern)

只使用一個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循環模式是將已經遍歷過的數組(或容器)的長度緩存起來。如如下代碼所示。

 

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循環應該用來遍歷非數組對象。使用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....
	}
}

 

switch模式

 

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 未定義
}());

 

使用parseInt()的數值約定

每次具體制定進制參數

 

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

空格

命名約定

構造函數的首字母大寫

分割單詞

構造函數能夠用使用大駝峯命名法

函數和方法名能夠用小駝峯命名法

函數的變量能夠用小寫單詞和下劃線鏈接

其餘命名默模式

變量名所有大寫表明該變量在生命週期中不可變

編寫註釋

編寫API文檔

 

/**
* @tag value
*/

/**
 * 反轉一個字符串
 *
 * @param {String} 輸入血藥反轉的字符串
 * @return {String} 反轉後的字符串
 */
var reverse=function(input){
	//...
	return output;
};

 

YUIDoc範例

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;
};
相關文章
相關標籤/搜索