1. 程式人生 > >JavaScript 5.1

JavaScript 5.1

拜讀JavaScript 教程(看我寫的不知所云的話,可以直接去這裡看))自己做的一個小總結,以便今後有不懂的地方來檢視,有錯誤的地方歡迎留言提出。

不多比比,開始吧。。

目錄

1. JavaScript 的基本語法

1.1 變數

1.2 變數提升

1.3 識別符號

1.4 區塊

1.5 條件語句

1.6 迴圈語句

2. 資料型別

2.1簡介

2.2 typeof 

2.3 null 和 undefined

2.4 布林值

2.5 字串

2.6 物件

2.7 函式

2.8 陣列


1. JavaScript 的基本語法

1.1 變數

//宣告變數和賦值同一行
var a = 1;
//宣告變數和賦值也可以分開寫,效果一樣
var a;
a = 1;

如果只宣告而沒有賦值,則該變數的值是undefinedundefined是一個 JavaScript 關鍵字,表示“無定義”。

var a;
a // undefined

可以在同一條var命令中宣告多個變數。

var a, b;
a=1;
b=2
//也可以宣告變數和賦值一起
var a=1,b=2;

JavaScript 是一種動態型別語言,也就是說,變數的型別沒有限制,變數可以隨時更改型別。

var a = 1;
a = 'hello';//將數值型別的變數a改變成字串

1.2 變數提升

JavaScript 引擎的工作方式是,先解析程式碼,獲取所有被宣告的變數,然後再一行一行地執行。這造成的結果,就是所有的變數的宣告語句,都會被提升到程式碼的頭部,這就叫做變數提升(hoisting)。

console.log(a);//功控制檯輸出undefined
var a = 1;

上面程式碼首先使用console.log方法,在控制檯(console)顯示變數a的值。這時變數a還沒有宣告和賦值,所以這是一種錯誤的做法,但是實際上不會報錯。因為存在變數提升,真正執行的是下面的程式碼。

var a;
console.log(a);//因為變數a已宣告,但還未賦值。所以是undefined
a = 1;

如果我們沒有宣告a則會報錯

console.log(a);

1.3 識別符號

最常見的識別符號就是變數名,以及後面要提到的函式名。JavaScript 語言的識別符號對大小寫敏感,所以aA是兩個不同的識別符號。

簡單說,識別符號命名規則如下。

  • 第一個字元,可以是任意 Unicode 字母(包括英文字母和其他語言的字母),以及美元符號($)和下劃線(_)。
  • 第二個字元及後面的字元,除了 Unicode 字母、美元符號和下劃線,還可以用數字0-9
  • 中文也可以用來做識別符號,但是不推薦使用

JavaScript 有一些保留字,不能用作識別符號:arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。 

1.4 區塊

JavaScript 使用大括號,將多個相關的語句組合在一起,稱為“區塊”(block)。

對於var命令來說,JavaScript 的區塊不構成單獨的作用域(scope)。

console.log(a)//undefined
{
  var a = 1;
}
console.log(a) // 1

因為我們之前說過的var宣告的變數會產生變數提升。其實執行的步驟是:

var a;
console.log(a)//在這裡a只是被宣告,還沒賦值所以是undefined
{
   a = 1;//為變數a賦值
}
console.log(a) //經過上面的變數宣告和賦值,所以a為1

1.5 條件語句

1.5.1 if...else結構(略)

1.5.2 switch 結構

    這也沒啥好說的,注意每個case記得寫上break,如果所有case都不符合,則執行最後的default部分。還有switch語句後面的表示式,與case語句後面的表示式比較執行結果時,採用的是嚴格相等運算子(===),而不是相等運算子(==)。(===)後面會講到。

1.5.3 三元運算子 ?:

(條件) ? 表示式1 : 表示式2

上面程式碼中,如果“條件”為true,則返回“表示式1”的值,否則返回“表示式2”的值。 

1.6 迴圈語句

1.6.1 while 迴圈

1.6.2 for 迴圈

1.6.3 do...while 迴圈

       先執行一次迴圈體,再進行條件判斷。

1.6.4 break 語句和 continue 語句

    break語句用於跳出程式碼塊或迴圈。

 continue語句用於立即終止本輪迴圈,返回迴圈結構的頭部,開始下一輪迴圈

1.6.5

JavaScript 語言允許,語句的前面有標籤(label),相當於定位符,用於跳轉到程式的任意位置,標籤的格式如下。

label:
  語句

標籤可以是任意的識別符號,但不能是保留字,語句部分可以是任意語句。

標籤通常與break語句和continue語句配合使用,跳出特定的迴圈。

top:
  for (var i = 0; i <= 2; i++){
    for (var j = 0; j <= 2; j++){
      if (i === 1 && j === 1) break top;
      console.log('i=' + i + ', j=' + j);
    }
  }
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0

如果break語句後面不使用標籤,則只能跳出內層迴圈,進入下一次的外層迴圈,對比一下

 

for (var i = 0; i <= 2; i++){
   for (var j = 0; j <= 2; j++){
     if (i === 1 && j === 1) break;
     console.log('i=' + i + ', j=' + j);
   }
 }
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
// i=2, j=0
// i=2, j=1
// i=2, j=2

continue語句也可以與標籤配合使用。

top:
  for (var i = 0; i < 3; i++){
    for (var j = 0; j < 3; j++){
      if (i === 1 && j === 1) continue top;
      console.log('i=' + i + ', j=' + j);
    }
  }
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
// i=2, j=0
// i=2, j=1
// i=2, j=2

2. 資料型別

2.1簡介

JavaScript 語言的每一個值,都屬於某一種資料型別。JavaScript 的資料型別,共有六種。(ES6 又新增了第七種 Symbol 型別的值,本教程不涉及。)

  • 數值(number):整數和小數(比如13.14
  • 字串(string):文字(比如Hello World)。
  • 布林值(boolean):表示真偽的兩個特殊值,即true(真)和false(假)
  • undefined:表示“未定義”或不存在,即由於目前沒有定義,所以此處暫時沒有任何值
  • null:表示空值,即此處的值為空。
  • 物件(object):各種值組成的集合。

JavaScript 有三種方法,可以確定一個值到底是什麼型別。

  • typeof運算子
  • instanceof運算子
  • Object.prototype.toString方法

2.2 typeof 

instanceof運算子和Object.prototype.toString方法,將在後文介紹。這裡介紹typeof運算子。

typeof運算子可以返回一個值的資料型別。

數值、字串、布林值分別返回numberstringboolean

typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"

function f() {}
typeof f // "function"

typeof undefined // "undefined"

//物件返回object。
typeof window // "object"
typeof {} // "object"
typeof [] // "object"

typeof null // "object"

利用這一點,typeof可以用來檢查一個沒有宣告的變數,而不報錯。

v // ReferenceError: v is not defined
typeof v // "undefined"

 上面程式碼中,變數v沒有用var命令宣告,直接使用就會報錯。但是,放在typeof後面,就不報錯了,而是返回undefined

實際程式設計中,這個特點通常用在判斷語句。

// 錯誤的寫法
if (v) {
  // ...
}
// ReferenceError: v is not defined

// 正確的寫法
if (typeof v === "undefined") {
  // ...
}

2.3 null 和 undefined

nullundefined都可以表示“沒有”,含義非常相似。將一個變數賦值為undefinednull,老實說,語法效果幾乎沒區別。

if語句中,它們都會被自動轉為false,相等運算子(==)甚至直接報告兩者相等。

if (!undefined) {
  console.log('undefined is false');
}
// undefined is false

if (!null) {
  console.log('null is false');
}
// null is false

undefined == null
// true

2.4 布林值

布林值代表“真”和“假”兩個狀態。“真”用關鍵字true表示,“假”用關鍵字false表示。布林值只有這兩個值。

如果 JavaScript 預期某個位置應該是布林值,會將該位置上現有的值自動轉為布林值。轉換規則是除了下面六個值被轉為false,其他值都視為true

  • undefined
  • null
  • false
  • 0
  • NaN
  • ""''(空字串)
if ('') {
  console.log('true');
}
// 沒有任何輸出

 

2.5 字串

字串可以被視為字元陣列,因此可以使用陣列的方括號運算子,用來返回某個位置的字元(位置編號從0開始)。

var s = 'hello';
s[0] // "h"
s[1] // "e"
s[4] // "o"

// 直接對字串使用方括號運算子
'hello'[1] // "e"
//如果方括號中的數字超過字串的長度,或者方括號中根本不是數字,則返回undefined。
'hello'[7] // undefined

但是,字串與陣列的相似性僅此而已。實際上,無法改變字串之中的單個字元。

var s = 'hello';

delete s[0];
s // "hello"

s[1] = 'a';
s // "hello"

s[5] = '!';
s // "hello"

上面程式碼表示,字串內部的單個字元無法改變和增刪,這些操作會默默地失敗。

length 屬性返回字串的長度,該屬性也是無法改變的。

2.6 物件

物件的所有鍵名都是字串(ES6 又引入了 Symbol 值也可以作為鍵名),所以加不加引號都可以

如果鍵名是數值,會被自動轉為字串。

var obj = {
  1: 'a',
  3.2: 'b',
  1e2: true,
  1e-2: true,
  .234: true,
  0xFF: true
};

obj
// Object {
//   1: "a",
//   3.2: "b",
//   100: true,
//   0.01: true,
//   0.234: true,
//   255: true
// }

obj['100'] // true

上面程式碼中,物件obj的所有鍵名雖然看上去像數值,實際上都被自動轉成了字串。

如果鍵名不符合標識名的條件(比如第一個字元為數字,或者含有空格或運算子),且也不是數字,則必須加上引號,否則會報錯。

// 報錯
var obj = {
  1p: 'Hello World'
};

// 不報錯
var obj = {
  '1p': 'Hello World',
  'h w': 'Hello World',
  'p+q': 'Hello World'
};
//上面物件的三個鍵名,都不符合標識名的條件,所以必須加上引號。

物件的每一個鍵名又稱為“屬性”(property),它的“鍵值”可以是任何資料型別。如果一個屬性的值為函式,通常把這個屬性稱為“方法”,它可以像函式那樣呼叫

var obj = {
  p: function (x) {
    return 2 * x;
  }
};
obj.p(1) // 2

如果屬性的值還是一個物件,就形成了鏈式引用。

var o1 = {};
var o2 = { bar: 'hello' };

o1.foo = o2;
o1.foo.bar // "hello"

屬性可以動態建立,不必在物件宣告時就指定。

var obj = {};
obj.foo = 123;
obj.foo // 123

如果不同的變數名指向同一個物件,那麼它們都是這個物件的引用,也就是說指向同一個記憶體地址。修改其中一個變數,會影響到其他所有變數。

var o1 = {};
var o2 = o1;

o1.a = 1;
o2.a // 1

o2.b = 2;
o1.b // 2

2.6.1 屬性的操作

(1)屬性的讀取

讀取物件的屬性,有兩種方法,一種是使用點運算子,還有一種是使用方括號運算子。

var obj = {
  p: 'Hello World'
};

obj.p // "Hello World"
obj['p'] // "Hello World"

請注意,如果使用方括號運算子,鍵名必須放在引號裡面,否則會被當作變數處理

var foo = 'bar';
var obj = {
  foo: 1,
  bar: 2
};

obj.foo  // 1
obj[foo]  // 2

方括號運算子內部還可以使用表示式。

obj['hello' + ' world']
obj[3 + 3]

數字鍵可以不加引號,因為會自動轉成字串。

var obj = {
  0.7: 'Hello World'
};

obj['0.7'] // "Hello World"
obj[0.7] // "Hello World"

注意,數值鍵名不能使用點運算子(因為會被當成小數點),只能使用方括號運算子。

var obj = {
  123: 'hello world'
};

obj.123 // 報錯
obj[123] // "hello world

(2)屬性的賦值

點運算子和方括號運算子,不僅可以用來讀取值,還可以用來賦值。

var obj = {};

obj.foo = 'Hello';
obj['bar'] = 'World';

(3)屬性的檢視

檢視一個物件本身的所有屬性,可以使用Object.keys方法。

var obj = {
  key1: 1,
  key2: 2
};

Object.keys(obj);
// ['key1', 'key2']

(4)屬性的刪除

delete命令用於刪除物件的屬性,刪除成功後返回true

var obj = { p: 1 };
Object.keys(obj) // ["p"]

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []

需要注意的是,delete命令只能刪除物件本身的屬性,無法刪除繼承的屬性

var obj = {};
delete obj.toString // true
obj.toString // function toString() { [native code] }

上面程式碼中,toString是物件obj繼承的屬性,雖然delete命令返回true,但該屬性並沒有被刪除,依然存在。這個例子還說明,即使delete返回true,該屬性依然可能讀取到值。

(5)屬性是否存在

in運算子用於檢查物件是否包含某個屬性(注意,檢查的是鍵名,不是鍵值),如果包含就返回true,否則返回false。它的左邊是一個字串,表示屬性名,右邊是一個物件。

var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true

in運算子的一個問題是,它不能識別哪些屬性是物件自身的,哪些屬性是繼承的。就像上面程式碼中,物件obj本身並沒有toString屬性,但是in運算子會返回true,因為這個屬性是繼承的。

這時,可以使用物件的hasOwnProperty方法判斷一下,是否為物件自身的屬性。

var obj = {};
if ('toString' in obj) {
  console.log(obj.hasOwnProperty('toString')) // false
}

(6)屬性的遍歷

for...in迴圈用來遍歷一個物件的全部屬性。

var obj = {a: 1, b: 2, c: 3};

for (var i in obj) {
  console.log('鍵名:', i);
  console.log('鍵值:', obj[i]);
}
// 鍵名: a
// 鍵值: 1
// 鍵名: b
// 鍵值: 2
// 鍵名: c
// 鍵值: 3

如果繼承的屬性是可遍歷的,那麼就會被for...in迴圈遍歷到。但是,一般情況下,都是隻想遍歷物件自身的屬性,所以使用for...in的時候,應該結合使用hasOwnProperty方法,在迴圈內部判斷一下,某個屬性是否為物件自身的屬性。

var person = { name: '老張' };

for (var key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}
// name

(7)with 語句(不建議使用)

// 例一
var obj = {
  p1: 1,
  p2: 2,
};
with (obj) {
  p1 = 4;
  p2 = 5;
}
// 等同於
obj.p1 = 4;
obj.p2 = 5;

// 例二
with (document.links[0]){
  console.log(href);
  console.log(title);
  console.log(style);
}
// 等同於
console.log(document.links[0].href);
console.log(document.links[0].title);
console.log(document.links[0].style);

注意,如果with區塊內部有變數的賦值操作,必須是當前物件已經存在的屬性,否則會創造一個當前作用域的全域性變數。

var obj = {};
with (obj) {
  p1 = 4;
  p2 = 5;
}

obj.p1 // undefined
p1 // 4

2.7 函式

2.7.1 概述

(1)函式的宣告

JavaScript 有三種宣告函式的方法。

①function 命令

function print(s) {
  console.log(s);
}

②函式表示式

var print = function(s) {
  console.log(s);
};
print('abc')

③Function 建構函式(幾乎無人使用)

var add = new Function(
  'x',
  'y',
  'return x + y'
);

// 等同於
function add(x, y) {
  return x + y;
}

你可以傳遞任意數量的引數給Function建構函式,只有最後一個引數會被當做函式體,如果只有一個引數,該引數就是函式體

(2) 函式名的提升

JavaScript 引擎將函式名視同變數名,所以採用function命令宣告函式時,整個函式會像變數宣告一樣,被提升到程式碼頭部。

2.7.2 函式的屬性和方法

(1)name屬性

function f1() {}
f1.name // "f1"

如果是通過變數賦值定義的函式,那麼name屬性返回變數名。

var f2 = function () {};
f2.name // "f2"

但是,上面這種情況,只有在變數的值是一個匿名函式時才是如此。如果變數的值是一個具名函式,那麼name屬性返回function關鍵字之後的那個函式名。

var f3 = function myName() {};
f3.name // 'myName'

(2)length 屬性

函式的length屬性返回函式預期傳入的引數個數,即函式定義之中的引數個數。

function f(a, b) {}
f.length // 2

(3)toString()方法

function f() {
  a();
  b();
  c();
}

f.toString()
// function f() {
//  a();
//  b();
//  c();
// }

2.7.3 函式作用域

(1)定義

作用域(scope)指的是變數存在的範圍。在 ES5 的規範中,Javascript 只有兩種作用域:一種是全域性作用域,變數在整個程式中一直存在,所有地方都可以讀取;另一種是函式作用域,變數只在函式內部存在。ES6 又新增了塊級作用域,本教程不涉及

函式外部宣告的變數就是全域性變數(global variable),它可以在函式內部讀取。

var v = 1;

function f() {
  console.log(v);
}

f()
// 1

在函式內部定義的變數,外部無法讀取,稱為“區域性變數”(local variable)。

function f(){
  var v = 1;
}

v // ReferenceError: v is not defined

注意,對於var命令來說,區域性變數只能在函式內部宣告,在其他區塊中宣告,一律都是全域性變數。

if (true) {
  var x = 5;
}
console.log(x);  // 5

上面程式碼中(if是一個條件語句,不是函式),變數x在條件判斷區塊之中宣告,結果就是一個全域性變數,可以在區塊之外讀取。

(2)函式內部的變數提升

與全域性作用域一樣,函式作用域內部也會產生“變數提升”現象。var命令宣告的變數,不管在什麼位置,變數宣告都會被提升到函式體的頭部。

function foo(x) {
  if (x > 100) {
    var tmp = x - 100;
  }
}

// 等同於
function foo(x) {
  var tmp;
  if (x > 100) {
    tmp = x - 100;
  };
}

2.7.4 引數

(1)引數的省略

函式引數不是必需的,Javascript 允許省略引數。

function f(a, b) {
  return a;
}

f(1, 2, 3) // 1
f(1) // 1
f() // undefined

f.length // 2

上面程式碼的函式f定義了兩個引數,但是執行時無論提供多少個引數(或者不提供引數),JavaScript 都不會報錯。省略的引數的值就變為undefined。需要注意的是,函式的length屬性與實際傳入的引數個數無關,只反映函式預期傳入的引數個數。

 

但是,沒有辦法只省略靠前的引數,而保留靠後的引數。如果一定要省略靠前的引數,只有顯式傳入undefined

function f(a, b) {
  return a;
}

f( , 1) // SyntaxError: Unexpected token ,(…)
f(undefined, 1) // undefined

(2)傳遞方式

函式引數如果是原始型別的值(數值、字串、布林值),傳遞方式是傳值傳遞(passes by value)。這意味著,在函式體內修改引數值,不會影響到函式外部

var p = 2;

function f(p) {
  p = 3;
}
f(p);

p // 2

但是,如果函式引數是複合型別的值(陣列、物件、其他函式),傳遞方式是傳址傳遞(pass by reference)。也就是說,傳入函式的原始值的地址,因此在函式內部修改引數,將會影響到原始值

var obj = { p: 1 };

function f(o) {
  o.p = 2;
}
f(obj);

obj.p // 2

注意,如果函式內部修改的,不是引數物件的某個屬性,而是替換掉整個引數,這時不會影響到原始值。

var obj = [1, 2, 3];

function f(o) {
  o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]

(3)arguments 物件

arguments物件包含了函式執行時的所有引數,arguments[0]就是第一個引數,arguments[1]就是第二個引數,以此類推。這個物件只有在函式體內部,才可以使用

var f = function (one) {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
}

f(1, 2, 3)
// 1
// 2
// 3

正常模式下,arguments物件可以在執行時修改。

var f = function(a, b) {
  arguments[0] = 3;
  arguments[1] = 2;
  return a + b;
}

f(1, 1) // 5

嚴格模式下,arguments物件是一個只讀物件,修改它是無效的,但不會報錯。

var f = function(a, b) {
  'use strict'; // 開啟嚴格模式
  arguments[0] = 3; // 無效
  arguments[1] = 2; // 無效
  return a + b;
}

f(1, 1) // 2

通過arguments物件的length屬性,可以判斷函式呼叫時到底帶幾個引數。

function f() {
  return arguments.length;
}

f(1, 2, 3) // 3
f(1) // 1
f() // 0

2.8 陣列

2.8.1 定義

陣列(array)是按次序排列的一組值。每個值的位置都有編號(從0開始),整個陣列用方括號表示。

var arr = ['a', 'b', 'c'];

除了在定義時賦值,陣列也可以先定義後賦值。

var arr = [];

arr[0] = 'a';
arr[1] = 'b';
arr[2] = 'c';

任何型別的資料,都可以放入陣列。

var arr = [
  {a: 1},
  [1, 2, 3],
  function() {return true;}
];

arr[0] // Object {a: 1}
arr[1] // [1, 2, 3]
arr[2] // function (){return true;}
arr[2]() // true

如果陣列的元素還是陣列,就形成了多維陣列。

var a = [[1, 2], [3, 4]];
a[0][1] // 2
a[1][1] // 4

2.8.2 陣列的本質

本質上,陣列屬於一種特殊的物件。typeof運算子會返回陣列的型別是object

typeof [1, 2, 3] // "object"

上面程式碼表明,typeof運算子認為陣列的型別就是物件。

陣列的特殊性體現在,它的鍵名是按次序排列的一組整數(0,1,2...)。

var arr = ['a', 'b', 'c'];

Object.keys(arr)
// ["0", "1", "2"]

 

2.8.3 length 屬性

陣列的length屬性,返回陣列的成員數量。

['a', 'b', 'c'].length // 3

length屬性是可寫的。如果人為設定一個小於當前成員個數的值,該陣列的成員會自動減少到length設定的值。

var arr = [ 'a', 'b', 'c' ];
arr.length // 3

arr.length = 2;
arr // ["a", "b"]

清空陣列的一個有效方法,就是將length屬性設為0。

如果人為設定length大於當前元素個數,則陣列的成員數量會增加到這個值,新增的位置都是空位。

var a = ['a'];

a.length = 3;
a[1] // undefined

2.8.4 in 運算子

檢查某個鍵名是否存在的運算子in,適用於物件,也適用於陣列。

var arr = [ 'a', 'b', 'c' ];
2 in arr  // true
'2' in arr // true
4 in arr // false

2.8.5 for...in 迴圈和陣列的遍歷

for...in迴圈不僅可以遍歷物件,也可以遍歷陣列,畢竟陣列只是一種特殊物件。

var a = [1, 2, 3];

for (var i in a) {
  console.log(a[i]);
}
// 1
// 2
// 3

但是,for...in不僅會遍歷陣列所有的數字鍵,還會遍歷非數字鍵。

var a = [1, 2, 3];
a.foo = true;

for (var key in a) {
  console.log(key);
}
// 0
// 1
// 2
// foo

上面程式碼在遍歷陣列時,也遍歷到了非整數鍵foo。所以,不推薦使用for...in遍歷陣列。

陣列的遍歷可以考慮使用for迴圈或while迴圈。

var a = [1, 2, 3];

// for迴圈
for(var i = 0; i < a.length; i++) {
  console.log(a[i]);
}

// while迴圈
var i = 0;
while (i < a.length) {
  console.log(a[i]);
  i++;
}

var l = a.length;
while (l--) {
  console.log(a[l]);
}

陣列的forEach方法,也可以用來遍歷陣列,詳見《標準庫》的 Array 物件一章。

var colors = ['red', 'green', 'blue'];
colors.forEach(function (color) {
  console.log(color);
});
// red
// green
// blue

2.8.6 陣列的空位

當陣列的某個位置是空元素,即兩個逗號之間沒有任何值,我們稱該陣列存在空位(hole)。

var a = [1, , 1];
a.length // 3

 上面程式碼表明,陣列的空位不影響length屬性。

需要注意的是,如果最後一個元素後面有逗號,並不會產生空位。也就是說,有沒有這個逗號,結果都是一樣的

陣列的空位是可以讀取的,返回undefined

var a = [, , ,];
a[1] // undefined
console.log(a.length) //3對應上面提到的最後一位是逗號,並不會產生空位

使用delete命令刪除一個數組成員,會形成空位,並且不會影響length屬性。

var a = [1, 2, 3];
delete a[1];

a[1] // undefined
a.length // 3

陣列的某個位置是空位,與某個位置是undefined,是不一樣的

var a = [, , ,];

a.forEach(function (x, i) {
  console.log(i + '. ' + x);
})
// 不產生任何輸出

for (var i in a) {
  console.log(i);
}
// 不產生任何輸出

Object.keys(a)
// []
var a = [undefined, undefined, undefined];

a.forEach(function (x, i) {
  console.log(i + '. ' + x);
});
// 0. undefined
// 1. undefined
// 2. undefined

for (var i in a) {
  console.log(i);
}
// 0
// 1
// 2

Object.keys(a)
// ['0', '1', '2']