JavaScript Gerden概述

1.對象

1.1 對象使用和屬性

JavaScript中全部變量都是對象,除了null和undefinedjavascript

1.2 對象做爲數據類型

JavaScript對象能夠做爲哈希表使用,主要用來保存命名的鍵和值的對應關係html

1.3 訪問屬性

點操做符和中括號操做符java

中括號操做符在下面2種狀況下依然有效 1.動態設置屬性 2.屬性名不是一個有效的變量名數組

1.4 刪除屬性

刪除屬性的惟一方法是使用delete操做符;設置屬性爲undefined或者null並不能真正的刪除屬性,只是移除了屬性和值的關聯瀏覽器

1.5 屬性名的語法

對象的屬性名可使用字符串或者普通字符(非關鍵詞)聲明。安全

2.原型

2.1 原型繼承

注:簡單的使用Bar.prototype = Foo.prototype將會致使兩個對象共享相同的原型。閉包

function Foo(){app

this.value = 42;函數

}性能

Foo.ptototype = {

method : function() {}

};

function Bar() {}

//設置Bar的prototype屬性爲Foo的實例對象

Bar.prototype = new Foo();

Bar.prototype.foo = "Hello World";

//修正Bar.prototype.constructor爲Bar自己

Bar.prototype.constructor = Bar;

注:不要使用Bar.prototype = Foo, 由於這不會執行Foo的原型,而是指向函數Foo。所以原型鏈將會回溯到Function.prototype而不是Foo.prototype。

2.2 屬性查找

查找對象屬性時,JavaScript會向上遍歷原型鏈,直到找到給定名稱的屬性爲止。

到查找到達原型鏈的頂部(Object.prototype),可是仍然沒有找到指定的屬性,就會返回undefined。

2.3 原型屬性

當原型屬性用來建立原型鏈時,能夠把任何類型的值賦給它。然而將原子類型賦給prototype的操做將會被忽略。

2.4 性能

若是一個屬性在原型鏈的頂端,則對於查找時間將帶來不利影響。

當使用 for - in 循環遍歷對象的屬性時,原型鏈上的全部屬性都將被訪問。

2.5 擴展內置類型的原型

一個錯誤特性被常用,那就是擴展Object.prototype或者其餘內置類型的原型對象。

這種技術會破壞封裝。

擴展內置類型的惟一理由是爲了和新的JavaScript保持一致。好比Arrary.forEach。

3.函數

3.1 函數聲明

函數聲明會在執行前被解析(hoisted),所以它存在於當前上下文的任意一個地方,即便在函數定義體的上面被調用也是對的。

foo();

function foo(){}

3.2 函數賦值表達式

foo;

foo();

var foo = function(){};

因爲var定義了一個聲明語句,對變量foo的解析是在代碼運行以前,所以foo變量在代碼運行時已經被定義過了。

可是因爲賦值語句只在運行時執行,所以在相應代碼執行以前,foo的值缺省爲undefined。

3.3 hasOwnProperty函數

hasOwnProperty是JavaScript中惟一一個處理屬性可是不須要查找原型鏈的方法。

JavaScript不會保護hasOwnProperty被非法佔用,所以若是一個對象碰巧存在這個屬性,就須要使用外部的hasOwnProperty函數來獲取正確的結果。

{}.hasOwnProperty.call(foo, 'bar');

4.this的工做原理

4.1 全局範圍

當在所有範圍內使用this,它將會指向全局對象。(瀏覽器中運行的JavaScript腳本,這個全局對象是window)

4.2 函數調用

foo();

此時this指向全局對象。

ES5 注意: 在嚴格模式下(strict mode),不存在全局變量。這種狀況下this將會是undefined。

        4.3 方法調用

this指向調用該方法的對象

4.4 調用構造函數

this指向新建立的對象。

4.5 call和apply

call 或者 apply 方法時,函數內的 this將會被顯式設置爲函數調用的第一個參數,若爲null則指向全局對象

4.6 方法的賦值表達式

將一個方法賦值給一個變量。

var test = someObject.methodTest;

test();

此時,test就像一個普通的函數被調用;所以,函數內的this將再也不被指向到someObject對象。(晚綁定)

5.閉包和引用

5.1 模擬私有變量

function Counter(start) {

var count = start;

return {

increment: function() {

count++;

},

get: function() {

return count;

}

}

}

var foo = Counter(4);

foo.increment();

foo.get(); // 5

這裏,Counter函數返回兩個閉包,函數increment和函數get。這兩個函數都維持着對外部做用域Counter的引用,所以總能夠訪問此做用域內定義的變量count。

5.2 不能夠在外部訪問私有變量

由於JavaScript中不能夠對做用域進行引用或賦值,所以沒有辦法在外部訪問count變量。惟一的途徑就是經過那兩個閉包。

5.3 循環中的閉包

for(var i = 0; i < 10; i++ ){

setTimeout(function(){

console.log(i);

}, 1000);

}

當console.log被調用的時候,匿名函數保持對外部變量i的引用,此時for循環已經結束,i的值被修改爲10。

爲了獲得想要的結果,須要在每次循環中建立變量i的拷貝。

5.3 避免引用錯誤

使用自執行匿名函數

for(var i = 0; i < 10; i++){

(function(e){

setTimeout(function(){

console.log(e);

}, 1000);

})(i);

}

6.arguments對象

6.1 基本概念

JavaScript中每一個函數內都能訪問arguments。它維護着全部傳遞到這個函數中的參數列表。

經過var關鍵字定義arguments或者將arguments聲明爲一個形式參數,都將致使原生的arguments不會被建立。

arguments變量不是一個數組,儘管在語法上有個數組相關的屬性length。

6.2 轉化爲數組

Array.prototype.slice.call(arguments)

6.3 傳遞參數

function foo(){

bar.apply(null, arguments);

}

function bar(a, b, c){

//some work

}

6.4 性能真相

arguments對象總會被建立,除了兩個特殊狀況 - 做爲局部變量聲明和做爲形式參數。

使用arguments.callee會顯著的影響現代JavaScript引擎的性能。

ES5提示:在嚴格模式下,arguments.callee會報錯TypeError,由於它已經被廢除了。

7.構造函數

7.1基本概念

在構造函數內部,this指向新建立的對象Object。這個新建立的對象的prototype被指向到構造函數的prototype。

被調用的函數沒有顯示的return表達式,則隱式的會返回this對象。

顯示的return表達式將會影響返回結果,但僅限於返回的是一個對象。

function Bar(){

return 2;

}

new Bar(); //返回新建立的對象 new Bar().constructor === Bar

function Foo(){

this.value = 2;

return {

foo : 1

};

}

new Foo(); //返回的對象 {foo:1}

7.2 經過工廠模式建立新對象

function Foo(){

var obj = {};

obj.value = "blue";

var privateVal = 2;

obj.someMethod = function(value){

this.value = value;

}

obj.getPrivate = function(){

return privateVal;

}

return obj;

}

優勢:充分利用私有變量,比起使用構造函數方式不容易出錯。

缺點:佔用更多的內存,新建立的對象不能共享原型上的方法。

 爲了實現繼承,工廠方法須要從另一個對象拷貝全部屬性,或者把一個對象做爲新建立對象的原型。

 放棄原型鏈僅僅是由於防止遺漏new帶來的問題,這彷佛和語言自己的思想一想違背。

 

 

8.做用域與命名空間

8.1 函數做用域

JavaScript不支持塊級做用域,而僅僅支持函數做用域。

注意: 若是不是在賦值語句中,而是在return表達式或者函數參數中,{...}

將會做爲代碼段解析, 而不是做爲對象的字面語法解析。若是考慮到自動分

號插入,這可能會致使一些不易察覺的錯誤。若是return對象的左括號和return不在一行上就會出錯。

8.2 局部變量

兩種方式聲明,一個事做爲函數參數,另外一個是經過var關鍵字聲明。

8.3 變量聲明提高

JavaScript會提高變量聲明。這意味着var表達式和function聲明都將會被提高到當前做用域的頂部。

8.4 名稱解析順序

當訪問函數內的foo變量時,JavaScript會按照下面順序查找:

1.當前做用域內是否有var foo的定義。

2.函數形式參數是否有使用foo名稱的。

3.函數自身是否叫作foo。

4.回溯到上一級做用域,而後從 1 從新開始

8.5 命名空間

經過當即執行的匿名函數解決命名衝突的問題。

(function(){

//do some work

})();

9.數組

9.1 遍歷

使用for - in循環,會查詢對象原型鏈上的全部屬性,所以須要使用hasOwnProperty函數來過濾,比普通for循環慢上好幾倍。

9.2 length屬性

length屬性的getter方式會簡單的返回數組的長度,而setter方式會截斷數組。

var foo = [1, 2, 3, 4, 5, 6];

foo.length = 3;

foo; // [1, 2, 3]

foo.length = 6;

foo; // [1, 2, 3]

譯者注: 在 Firebug 中查看此時 foo 的值是: [1, 2, 3, undefined, 

undefined, undefined]  可是這個結果並不許確,若是你在 Chrome 的控制檯

查看 foo 的結果,你會發現是這樣的: [1, 2, 3] 由於在 JavaScript 中

undefined 是一個變量,注意是變量不是關鍵字,所以上面兩個結果的意義是

徹底不相同的。

9.3 Array構造函數

[1, 2, 3]; // 結果: [1, 2, 3]

new Array(1, 2, 3); // 結果: [1, 2, 3]

[3]; // 結果: [3]

new Array(3); // 結果: [] 

new Array('3') // 結果: ['3']

因爲只有一個參數傳遞到構造函數中(譯者注:指的是 new Array(3); 這種調

用方式),而且這個參數是數字,構造函數會返回一個 length 屬性被設置爲

此參數的空數組。 須要特別注意的是,此時只有 length 屬性被設置,真正的

數組並無生成。

10.相等於比較

10.1 等於操做符

等於操做符由兩個等號組成:==

JavaScript 是弱類型語言,這就意味着,等於操做符會爲了比較兩個值而進行強制類型轉換。

""           ==   "0"           // false

0            ==   ""            // true

0            ==   "0"           // true

false        ==   "false"       // false

false        ==   "0"           // true

false        ==   undefined     // false

false        ==   null          // false

null         ==   undefined     // true

" \t\r\n"    ==   0             // true

此外,強制類型轉換也會帶來性能消耗,好比一個字符串爲了和一個數組進行比較,必須事先被強制轉換爲數字。

10.2 嚴格的等於操做符

嚴格的等於操做符由三個等號組成:===

不想普通的等於操做符,嚴格的等於操做符不會進行強制類型轉換。

""           ===   "0"           // false

0            ===   ""            // false

0            ===   "0"           // false

false        ===   "false"       // false

false        ===   "0"           // false

false        ===   undefined     // false

false        ===   null          // false

null         ===   undefined     // false

" \t\r\n"    ===   0             // false 

10.3 比較對象

雖然 == 和 === 操做符都是等於操做符,可是當其中有一個操做數爲對象時,行爲就不一樣了。

{} === {};                   // false

new String('foo') === 'foo'; // false

new Number(10) === 10;       // false

var foo = {};

foo === foo;                 // true

這裏等於操做符比較的不是值是否相等,而是是否屬於同一個身份;也就是說,

只有對象的同一個實例才被認爲是相等的。 這有點像 Python 中的 is 和 C 

中的指針比較。

11.typeof操做符

11.1 JavaScript類型表格

Value               Class      Type

-------------------------------------

"foo"               String     string

new String("foo")   String     object

1.2                 Number     number

new Number(1.2)     Number     object 

true                Boolean    boolean

new Boolean(true)   Boolean    object

new Date()          Date       object

new Error()         Error      object

[1,2,3]             Array      object

new Array(1, 2, 3)  Array      object

new Function("")    Function   function

/abc/g              RegExp     object (function in Nitro/V8)

new RegExp("meow")  RegExp     object (function in Nitro/V8)

{}                  Object     object

new Object()        Object     object

Class一列表示對象的內部屬性[[Class]]的值。

爲了獲取對象的[[Class]],咱們須要使用定義在 Object.prototype上的方法toString。

11.2 對象的類定義

JavaScript 標準文檔中定義: [[Class]] 的值只多是下面字符串中的一個:

Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String。

JavaScript標準文檔只給出了一種獲取[[Class]]值的方法,那就是使用Object.prototype.toString。

function is(type, obj) {

var clas = Object.prototype.toString.call(obj).slice(8, -1);

return obj !== undefined && obj !== null && clas === type;

}

is('String', 'test'); // true

is('String', new String('test')); // true

ES5 提示: 在 ECMAScript 5 中,爲了方便,對 null 和 undefined 調用

Object.prototype.toString方法,其返回值由Object變成了Null和Undefined。

12.instanceof操做符

12.1 基本概念

instanceof操做符用來比較兩個操做數的構造函數。只有在比較自定義的對象時纔有意義。若是用來比較內置類型,將會和typeof操做符同樣用處不大。

12.2 比較自定義對象

function Foo() {}

function Bar() {}

Bar.prototype = new Foo();

new Bar() instanceof Bar; // true

new Bar() instanceof Foo; // true

// 若是僅僅設置Bar.prototype爲函數Foo自己,而不是Foo構造函數的一個實例

Bar.prototype = Foo;

new Bar() instanceof Foo; // false

12.3 instanceof比較內置類型

new String('foo') instanceof String; // true

new String('foo') instanceof Object; // true

'foo' instanceof String; // false

'foo' instanceof Object; // false

13.類型轉換

13.1 內置類型的構造函數

new Number(10) === 10;     // False, 對象與數字的比較

Number(10) === 10;         // True, 數字與數字的比較

new Number(10) + 0 === 10; // True, 因爲隱式的類型轉換

使用內置類型Number做爲構造函數將會建立一個新的Number對象,而在不使用new關鍵字的Number函數更像是一個數字轉換器.

最好的選擇是把要比較的值顯式的轉換爲三種可能的類型之一。

13.2 轉換爲字符串

'' + 10 === '10'; // true

將一個值加上空字符串能夠輕鬆轉換爲字符串類型。

13.3 轉換爲數字

+'10' === 10; // true

使用一元的加號操做符,能夠把字符串轉換爲數字。

其餘字符串轉換爲數字的經常使用方法:

+'010' === 10

Number('010') === 10

parseInt('010', 10) === 10  // 用來轉換爲整數

+'010.2' === 10.2

Number('010.2') === 10.2

parseInt('010.2', 10) === 10

13.4 轉換爲布爾型

經過使用否操做符兩次,能夠把一個值轉換爲布爾型。

!!'foo';   // true

!!'';      // false

!!'0';     // true

!!'1';     // true

!!'-1'     // true

!!{};      // true

!!true;    // true

14.eval

14.1 爲何不要使用eval

在任何狀況下咱們都應該避免使用eval函數。99.9%使用eval的場景都有不使用eval的解決方案。

14.2 安全問題

eval 也存在安全問題,由於它會執行任意傳給它的代碼,在代碼字符串未知或者是來自一個不信任的源時,絕對不要使用eval函數。

15.undefined 和 null

15.1 undefined的值

undefined是一個值爲undefined的類型。

下面的狀況會返回 undefined 值:

          訪問未修改的全局變量 undefined。

          因爲沒有定義 return 表達式的函數隱式返回。

          return 表達式沒有顯式的返回任何內容。

          訪問不存在的屬性。

          函數參數沒有被顯式的傳遞值。

          任何被設置爲 undefined 值的變量。

15.2 處理undefined值的改變

因爲全局變量undefined只是保存了undefined類型實際值的副本,所以對它賦新值不會改變類型undefined的值。


爲了不可能對 undefined 值的改變,一個經常使用的技巧是使用一個傳遞到匿名包裝器的額外參數。在調用時,這個參數不會獲取任何值。

var undefined = 123;

(function(something, foo, undefined) {

// 局部做用域裏的 undefined 變量從新得到了 `undefined` 值

})('Hello World', 42);

另一種達到相同目的方法是在函數內使用變量聲明。

var undefined = 123;

(function(something, foo) {

var undefined;

...

})('Hello World', 42);

15.3 使用 null

JavaScript 中的 undefined 的使用場景相似於其它語言中的 null,實際上JavaScript 中的 null 是另一種數據類型。

它在 JavaScript 內部有一些使用場景(好比聲明原型鏈的終結Foo.prototype = null ),可是大多數狀況下均可以使用 undefined 來代替。


16.setTimeout 和 setInterval

16.1 定時器

基於JavaScript引擎的計時策略,以及本質上的單線程運行方式,因此其它代碼的運行可能會阻塞此線程。所以無法確保函數會在setTimeout指定的時刻被調用。

做爲第一個參數的函數將會在全局做用域中執行,所以函數內的 this 將會指向這個全局對象。

function Foo() {

this.value = 42;

this.method = function() {

// this 指向全局對象

console.log(this.value); // 輸出:undefined

};

setTimeout(this.method, 500);

}

new Foo();

注意: setTimeout的第一個參數是函數對象,一個常犯的錯誤是這樣的setTimeout(foo(), 1000) , 這裏回調函數是foo的返回值,而不是foo自己。

大部分狀況下,這是一個潛在的錯誤,由於若是函數返回undefined,setTimeout也不會報錯。

16.2 setInterval的堆調用

當回調函數的執行被阻塞時,setInterval 仍然會發布更多的毀掉指令。在很小的定時間隔狀況下,這會致使回調函數被堆積起來。

function foo(){

// 阻塞執行 1 秒

}

setInterval(foo, 100);

上面代碼中,foo 會執行一次隨後被阻塞了一分鐘。

在 foo 被阻塞的時候,setInterval 仍然在組織未來對回調函數的調用。 所以,當第一次 foo 函數調用結束時,已經有10次函數調用在等待執行。

16.3 處理可能的阻塞調用

最簡單也是最容易控制的方案,是在回調函數內部使用 setTimeout 函數

function foo(){

// 阻塞執行 1 秒

setTimeout(foo, 100);

}

foo();

16.4 手工清空定時器

能夠經過將定時時產生的 ID 標識傳遞給clearTimeout或者clearInterval函數來清除定時。

16.5 隱藏使用 eval

setTimeout 和 setInterval 也接受第一個參數爲字符串的狀況。 這個特性絕對不要使用,由於它在內部使用了 eval。

詳情可見:JavaScript祕密花園

相關文章
相關標籤/搜索