讀《JavaScript權威指南》筆記(四)--數組、函數
一、數組
1.數組是值的有序集合。每個值叫做一個元素,而每個元素在數組中有一個位置,以數字表示,稱為索引。JavaScript數組是無類型的:數組元素可以是任意類型,並且同一個數組中的不同元素也可能有不同的類型。數組的元素甚至也可能是對象或其他數組,這允許創建復雜的數據結構,如對象的數組和數組的數組。JavaScript數組的索引是基於零的32位數值:第一個元素的索引為0,最大可能的索引為4294967294(232-2),數組最大能容納4294967295個元素。
所有的索引都是屬性名,但只有在0~232-2之間的整數屬性名才是索引。所有的數組都是對象,可以為其創建任意名字的屬性。但如果使用的屬性是數組的索引,數組的特殊行為就是將根據需要更新它們的length屬性值。
a[-1.23] = true; // 這將創建一個名為"-1.23"的屬性 a["1000"] = 0; // 這是數組的第1001個元素 a[1.000] // 和a[1]相等
a = new Array(5); // 數組沒有元素,但是a.length是5 a = []; //創建一個空數組,length = 0 a[1000] = 0; // 賦值添加一個元素,但是設置length為1001
2.數組的實現是經過優化的,用數字索引來訪問數組元素一般來說比訪問常規的對象屬性要快很多。
var isArray = Function.isArray || function(o){ return typeof o === "object" && Object.prototype.toString.call(o)=== "[object Array]"; };
3.indexOf()和lastIndexOf()方法不接收一個函數作為其參數。第一個參數是需要搜索的值,第二個參數是可選的:它指定數組中的一個索引,從那裏開始搜索。如果省略該參數,indexOf()從頭開始搜索,而lastIndexOf()從末尾開始搜索。第二個參數也可以是負數,它代表相對數組末尾的偏移量,對於splice()方法:例如,-1指定數組的最後一個元素。
4.reduce()時只有一個參數:沒有指定初始值。當不指定初始值調用reduce()時,它將使用數組的第一個元素作為其初始值。這意味著第一次調用化簡函數就使用了第一個和第二個數組元素作為其第一個和第二個參數
5.一旦every()和some()確認該返回什麽值它們就會停止遍歷數組元素。some()在判定函數第一次返回true後就返回true,但如果判定函數一直返回false,它將會遍歷整個數組。every()恰好相反:它在判定函數第一次返回false後就返回false,但如果判定函數一直返回true,它將會遍歷整個數組。註意,根據數學上的慣例,在空數組上調用時,every()返回true,some()返回false。
6.Array.sort()方法將數組中的元素排序並返回排序後的數組。當不帶參數調用sort()時,數組元素以字母表順序排序。如果數組包含undefined元素,它們會被排到數組的尾部。為了按照其他方式而非字母表順序進行數組排序,必須給sort()方法傳遞一個比較函數。
7.字符串是不可變值,故當把它們作為數組看待時,它們是只讀的。如push()、sort()、reverse()和splice()等數組方法會修改數組,它們在字符串上是無效的。
二、函數
1.函數聲明語句的語法如下:
function funcname([arg1[, arg2[..., argn]]]){
statements
}
funcname是要聲明的函數的名稱的標識符。函數名之後的圓括號中是參數列表,參數之間使用逗號分隔。當調用函數時,這些標識符則指代傳入函數的實參。
2.函數聲明語句並非真正的語句,ECMAScript規範只是允許它們作為頂級語句。它們可以出現在全局代碼裏,或者內嵌在其他函數中,但它們不能出現在循環、條件判斷,或者try/cache/finally以及with語句中。註意,此限制僅適用於以語句聲明形式定義的函數。函數定義表達式可以出現在JavaScript代碼的任何地方。
3.一個典型的函數定義表達式包含關鍵字function,跟隨其後的是一對圓括號,括號內是一個以逗號分割的列表,列表含有0個或多個標識符(參數名),然後再跟隨一個由花括號包裹的JavaScript代碼段(函數體),例如:
// 這個函數返回傳入參數值的平方 var square = function(x){ return x * x; }
4.用於初始化一個新創建的對象的函數稱為構造函數。構造函數的命名規則(首字母大寫)和普通函數是如此不同還有另外一個原因,構造函數調用和普通函數調用是不盡相同的。構造函數就是用來“構造新對象”的,它必須通過關鍵字new調用,如果將構造函數用做普通函數的話,往往不會正常工作。如果構造函數調用在圓括號內包含一組實參列表,先計算這些實參表達式,然後傳入函數內,這和函數調用和方法調用是一致的。但如果構造函數沒有形參,JavaScript構造函數調用的語法是允許省略實參列表和圓括號的。凡是沒有形參的構造函數調用都可以省略圓括號。
5.如果函數掛載在一個對象上,作為對象的一個屬性,就稱它為對象的方法。當通過這個對象來調用函數時,該對象就是此次調用的上下文(context),也就是該函數的this的值。一個方法無非是個保存在一個對象的屬性裏的JavaScript函數。
6.this是一個關鍵字,不是變量,也不是屬性名。JavaScript的語法不允許給this賦值。和變量不同,關鍵字this沒有作用域的限制,嵌套的函數不會從調用它的函數中繼承this。如果嵌套函數作為方法調用,其this的值指向調用它的對象。如果嵌套函數作為函數調用,其this值不是全局對象(非嚴格模式下)就是undefined(嚴格模式下)。很多人誤以為調用嵌套函數時this會指向調用外層函數的上下文。如果你想訪問這個外部函數的this值,需要將this的值保存在一個變量裏,這個變量和內部函數都同在一個作用域內。通常使用變量self來保存this。
7.arguments.call ee.length(期望傳入的實參個數)
8.大多數(非全部)的toString()方法的實現都返回函數的完整源碼。內置函數往往返回一個類似“[native code]”的字符串作為函數體。
9.不完全函數編程是一種函數編程技巧,即把一次完整的函數調用拆成多次函數調用,每次傳入的參數都是完成參數的一部分,每次才分開的函數就叫做不完全函數。每次函數調用就叫做不完全調用。
10.
(function(){ // mymodule()函數重寫為匿名的函數表達式 // 模塊代碼 }()); // 結束函數定義並立即調用它
這種定義匿名函數並立即在單個表達式中調用它的寫法非常常見,已經成為一種慣用法了。註意上面代碼的圓括號的用法,function之前的左圓括號是必需的,因為如果不寫這個左圓括號,JavaScript解釋器會試圖將關鍵字function解析為函數聲明語句。使用圓括號JavaScript解釋器才會正確地將其解析為函數定義表達式。使用圓括號是習慣用法,盡管有些時候沒有必要也不應當省略。這裏定義的函數會立即調用。
11.每個JavaScript函數(ECMAScript 5中的Function.bind()方法返回的函數除外)都自動擁有一個prototype屬性。這個屬性的值是一個對象,這個對象包含唯一一個不可枚舉屬性constructor。constructor屬性的值是一個函數對象:
var F = function(){}; // 這是一個函數對象 var p = F.prototype; // 這是F相關聯的原型對象 var c = p.constructor; // 這是與原型相關聯的函數 c === F // => true: 對於任意函數F.prototype.constructor==F
可以看到構造函數的原型中存在預先定義好的constructor屬性,這意味著對象通常繼承的constructor均指代它們的構造函數。由於構造函數是類的“公共標識”,因此這個constructor屬性為對象提供了類。
var o = new F(); //創建類F的一個對象 o.constructor === F // => true,constructor屬性指代這個類
12.構造函數是類的公有標識。在JavaScript中也可以定義對象的類,讓每個對象都共享某些屬性,這種“共享”的特性是非常有用的。類的成員或實例都包含一些屬性,用以存放或定義它們的狀態,其中有些屬性定義了它們的行為(通常稱為方法)。這些行為通常是由類定義的,而且為所有實例所共享。例如,假設有一個名為Complex的類用來表示復數,同時還定義了一些復數運算。一個Complex實例應當包含復數的實部和虛部(狀態),同樣Complex類還會定義復數的加法和乘法操作(行為)。在JavaScript中,類的實現是基於其原型繼承機制的。如果兩個實例都從同一個原型對象上繼承了屬性,我們說它們是同一個類的實例。
13.判斷是否是函數
function isFunction(x){ return Object.prototype.toString.call(x)=== "[object Function]"; }
14.bind
var sum = function(x,y){ return x + y }; //返回兩個實參的和值 // 創建一個類似sum的新函數,但this的值綁定到null // 並且第一個參數綁定到1,這個新的函數期望只傳入一個實參 var succ = sum.bind(null, 1); succ(2)// => 3: x綁定到1,並傳入2作為實參y function f(y,z){ return this.x + y + z }; //另外一個做累加計算的函數 var g = f.bind({x:1}, 2); //綁定this和y g(3) // => 6: this.x綁定到1,y綁定到2,z綁定到3
if(!Function.prototype.bind){ Function.prototype.bind = function(o /*, args */){ // 將this和arguments的值保存至變量中 // 以便在後面嵌套的函數中可以使用它們 var self = this, boundArgs = arguments; // bind()方法的返回值是一個函數 return function(){ // 創建一個實參列表,將傳入bind()的第二個及後續的實參都傳入這個函數 var args = [], i; for(i = 1; i < boundArgs.length; i++)args.push(boundArgs[i]); for(i = 0; i < arguments.length; i++)args.push(arguments[i]); // 現在將self作為o的方法來調用,傳入這些實參 return self.apply(o, args); }; }; }
15.try/catch語句已經可以使用多catch從句了,在catch從句的參數中加入關鍵字if以及一個條件判斷表達式:
try { // 這裏可能會拋出多種類型的異常 throw 1; } catch(e if e instanceof ReferenceError){ // 這裏處理引用錯誤 } catch(e if e === "quit"){ // 這裏處理拋出的字符串是"quit"的情況 } catch(e if typeof e === "string"){ // 處理其他字符串的情況 } catch(e){ // 處理余下的異常情況 } finally { // finally從句正常執行 }
16.如果函數只計算一個表達式並返回它的值,關鍵字return和花括號都可以省略
let succ=function(x)x+1, yes=function()true, no=function()false;
讀《JavaScript權威指南》筆記(四)--數組、函數