跳至內容

JavaScript語法

維基百科,自由的百科全書

JavaScript語法英語Syntax (programming languages)是編寫該語言程序的一套規則。

JavaScript標準庫缺少官方標準串流函數(除了document.write)。由於JavaScript主要用於現代網頁瀏覽器上執行客戶端腳本,幾乎所有瀏覽器都支援alert函數。下列範例中使用標準文本輸出的console對象的log函數。

起源

布蘭登·艾克在JavaScript 1.1 規範的第一段中[1][2]總結如下:

JavaScript從Java借來了大部分語法,但也從AwkPerl繼承,在其對象原型系統中還受到 Self 的一些間接影響。

基礎

大小寫敏感

JavaScript是大小寫敏感的。經常以一個大寫字母開始構造函數的名稱 ,一個小寫字母開始函數或變量的名字。

例子:

var a = 5;
console.log(a); // 5
console.log(A); // throws a ReferenceError: A is not defined

空白和分號

不像在C語言中,JavaScript原始碼中的空白可以直接影響形式語義學。JavaScript里的語句用分號結束。因為自動分號插入(ASI),也就是說,一些符合語法規則的的語句在分析到換行符時會被認為是完整的,如同在換行符前面插入了一個分號。一些當局建議顯式地寫結束語句的分號,因為它也許會減少自動分號插入未曾預料的效果。[3]

有兩個問題:有五個符記既能開始一條語句,也能擴展一條完整的語句;以及五個受限的語句(production),其中換行在一些位置不被允許,潛在可能會產生不正確的解析。[4]

五個有問題的符記是左圓括號「(」,左方括號「[」,斜線「/」,加號「+」,減號「-」。當然,左圓括號在立即調用函數表達式模式中很常見,左方括號有時出現,其他幾個就很少了。規範中給出的示例是:[4]

a = b + c
(d + e).foo()

// Treated as:
//  a = b + c(d + e).foo();

有建議前面的語句以分號結束。 有些人建議在以「(」或「[」開始的行,使用前導分號,讓這行不會意外地與上一行連接。這被稱為防禦性分號,特別推薦,因為否則在重新排列代碼時可能變得模稜兩可。[4][5]例如:

a = b + c
(d + e).foo()

// Treated as:
//  a = b + c(d + e).foo();

最初的分號有時也在JavaScript庫中使用,在它們被追加到另一個省略尾部分號的庫的情況下,因為這可能會導致初始語句的模糊不清。 五個受限的語句是returnthrowbreakcontinue,和後導遞增或遞減。在所有情況里,插入分號不會解決問題,但使得解析的語法更加清晰,錯誤更加容易檢測。returnthrow 取一個可選的值,breakcontinue 取一個可選的標籤。所有情況下,都建議使值或標籤和語句保持在一行上。最多的情況是返回語句,一個人可能想返回一個大的對象文字,它可能意外地被放在了新行上。對於後導遞增或遞減,有可能與前綴遞增或遞減模稜兩可,所以再一次推薦把這些放在同一行。

return
a + b;

// Returns undefined. Treated as:
//   return;
//   a + b;
// Should be written as:
//   return a + b;

註釋

註釋語法和C++,Swift,和許多其他程式語言相同。

// a short, one-line comment

/* this is a long, multi-line comment
  about my script. May it one day
  be great. */

/* Comments /* may not be nested */ Syntax error */

變量

標準JavaScript中的變量沒有附加類型,因此任何值(每個值都有一個類型)可以存儲在任何變量中。從ES6(該語言的第6版)開始,變量可以用var聲明函數範圍變量,而letconst用於塊級變量。在ES6之前,變量只能用var語句聲明。分配給用const聲明的變量的值不能更改,但其屬性可以。變量的標識符必須以字母、下劃線 (_) 或美元符號 ($) 開頭,而後續字符也可以是數字 (0-9)。JavaScript區分大小寫,因此大寫字符「A」到「Z」與小寫字符「a」到「z」不同。

從JavaScript1.5 開始,可以在標識符中使用ISO 8859-1Unicode字符(或 \uXXXXUnicode轉義序列)。[6]在某些JavaScript實現中,可以在標識符中使用at符號 (@),但這與規範相反,並且在較新的實現中不受支持。

作用域和提升

var聲明的變量是在函數級別詞法作用域,而用letconst聲明的變量具有塊級作用域。由於聲明是在任何代碼執行之前處理的,因此可以在代碼中聲明之前分配和使用變量。[7]這被稱為提升(hoisting),它相當於在函數或塊的頂部前向聲明的變量。[8]

對於varletconst語句,只有聲明被提升;賦值未被提升。因此,函數中間的var x = 1語句等效於函數頂部的var x聲明語句,以及函數中間該點的{{{1}}}賦值語句。這意味着值在聲明之前不能被訪問;前向引用(forward reference)是不可能的。使用var變量的值在初始化之前是未定義的(undefined)。使用letconst聲明的變量在初始化之前無法訪問,因此之前引用此類變量會導致錯誤。[9][10]

函數聲明,它聲明一個變量並為其分配一個函數,類似於變量語句,但除了可以提升聲明之外,它們還提升了賦值——就好像整個語句出現在包含的函數的頂部一樣——因此前向引用(forward reference)也是允許的:函數語句在包含它的函數中的位置是不相關的。這不同於在varletconst語句中將函數表達式賦值給變量。

因而,如:

var func = function() { .. } // 仅提升声明
function func() { .. } // 声明和赋值被提升

塊作用域可以通過將整個塊包裝在一個函數中然後執行它來產生——這被稱為立即調用函數表達式模式——或者通過使用let關鍵字聲明變量。

聲明和賦值

在作用域外聲明的變量是全局變量。如果一個變量在更高的作用域中聲明,它可以被子作用域(child scope)訪問。

當JavaScript嘗試解析標識符時,它會在本地作用域內查找。如果沒有找到這個標識符,它會在下一個外部作用域中查找,依此類推,直到抵達全局變量所在的全局作用域。如果仍未找到,JavaScript將引發ReferenceError異常。

給標識符賦值時,JavaScript會通過完全相同的過程來獲取此標識符;但如果在全局範圍內找不到它,將在創建的作用域內創建「變量」。[11]因此,如果給未聲明的變量賦值,會被創建為一個全局變量。在全局範圍內(即在任何函數體之外或在let/const的情況下為塊)聲明一個變量(使用關鍵字var),或者給一個未聲明的標識符賦值,或向全局對象(通常是窗口)添加一個屬性,這將創建一個新的全局變量。

請注意,JavaScript的嚴格模式(strict mode)禁止給未聲明的變量賦值,從而避免了全局命名空間污染。

例子

變量聲明和作用域的一些例子:

var x1 = 0; // 一个全局变量,因为未在任何函数内
let x2 = 0; // 也是全局变量,因为未在任何块内

function f() {
  var z = 'foxes', r = 'birds'; // 2个局部变量
  m = 'fish'; //全局变量,因为给未声明的变量赋值

  function child() {
    var r = 'monkeys'; //为局部变量,不影响父函数中变量r的值"birds"
    z = 'penguins'; //闭包:子函数也能访问父函数中的变量
  }

  twenty = 20; //该变量在下一行声明,但在该函数内任何位置都可用,这里是在声明之前使用
  var twenty;

  child();
  return x1 + x2; //这里使用的x1和x2是全局变量
}

f();

console.log(z); // 这将抛出ReferenceError异常,因为z不可用了
for (let i = 0; i < 10; i++) console.log(i);
console.log(i); // 抛出 ReferenceError: i is not defined
for (const i = 0; i < 10; i++) console.log(i); // 抛出TypeError: Assignment to constant variable

const pi; // 抛出SyntaxError: Missing initializer in const declaration

基本數據類型

JavaScript語言提供6種基本數據類型:

  • Undefined
  • Number
  • BigInt
  • String
  • Boolean
  • Symbol

一些基本數據類型還提供一組命名值來表示類型邊界的範圍。這些命名值在下面的相應子節中描述。

Undefined

「undefined」值英語undefined value被賦值給所有未初始化變量英語uninitialized variable,在查看不存在的對象屬性時也返回這個值。在布爾上下文中,未定義值被認為是假值。

注意: undefined 被認為是真正的基本數據類型。 除非顯式轉換,否則與在邏輯上下文中評估為false的其他類型相比,未定義的值可能會出現不可預料的行為。

var test;                         // 变量被声明但未定义,其值被赋为undefined值

var testObj = {};
console.log(test);                // test的值存在,显示为undefined
console.log(testObj.myProp);      // testObj存在,但属性不存在,显示为undefined
console.log(undefined == null);   // 未强制类型检查,显示为true
console.log(undefined === null);  // 强制类型检查,显示为false

注意:沒有內建的語言字面量用於undefined。因此(x === undefined)並不是一個萬無一失的放式檢查一個變量是否為undefined,因為在ECMAScript 5之前的版本,如果寫代碼var undefined = "I'm defined now";是合法的。更魯棒的比較方法是(typeof x === 'undefined').

下屬函數不會如期望那樣工作:

function isUndefined(x) { var u; return x === u; }             // 如这个...
function isUndefined(x) { return x === void 0; }               // ... 或第二个
function isUndefined(x) { return (typeof x) === "undefined"; } // ... 或第三个

調用isUndefined(my_var)會拋出ReferenceError,如果my_var是未知標識符,但typeof my_var === 'undefined'不會拋出異常。

Number

Number數據類型表示IEEE-754浮點雙精度。不能精確表示一些實數或分數。如:

console.log(0.2 + 0.1 === 0.3); // displays false
console.log(0.94 - 0.01);       // displays 0.9299999999999999

因此,無論何時格式化輸出,都應使用諸如toFixed()方法之類的例程對數字進行捨入。[12]

數字可以用這些符號中的任何一種來指定:

345;    // 一个整数
34.5;   // 一个浮点数
3.45e2; // 另一个浮点数
0b1011; // 二进制整数
0o377;   // 八进制整数
0xFF;   // 十六进制整数,A-F可大写可小写

ES2021引入了數字分隔符_ (the underscore):

1_000_000_000;    // 用于大数
1_000_000.5;      // 支持十进制
1_000e1_000;      // 支持科学计数法

//支持二进制、八进制、十六进制
0b0000_0000_0101_1011;
0o0001_3520_0237_1327;
0xFFFF_FFFF_FFFF_FFFE;

// 但是您能在数的非数字部分旁边,或开头或结尾处使用分隔符
_12; // 变量未定义(下划线使其成为变量标识符)
12_; // 语法错误(不能在数字的末尾)
12_.0; // 语法错误(在小数点旁边放置分隔符没有意义)
12._0; // 语法错误
12e_6; // 语法错误(“e”旁边,非数字。在开头放置分隔符没有意义)
1000____0000; // 语法错误(“_”旁边,非数字。一次只允许 1 个分隔符

Number類型的範圍+∞, −∞NaN(Not a Number)可以通過兩個程序表達式獲得:

Infinity; // 正无穷大(用-Infinity 获得负无穷大)
NaN;      // Not-A-Number值,字符串到数字转换时也作为失败返回值

Infinity 和 NaN 是數字:

typeof Infinity;   // 返回"number"
typeof NaN;        // 返回"number"

這三個特殊值對應並按照IEEE-754描述的方式運行。

Number構造函數(用作函數),或一元+或-可用於執行顯式數值轉換:

var myString = "123.456";
var myNumber1 = Number(myString);
var myNumber2 = +myString;

當用作構造函數時,會創建一個數值包裝對象(儘管它用處不大):

myNumericWrapper = new Number(123.456);

但是,NaN 不等於自身:

const nan = NaN;
console.log(NaN == NaN);        // false
console.log(NaN === NaN);       // false
console.log(NaN !== NaN);       // true
console.log(nan !== nan);       // true

//可用isNaN方法检查NaN
console.log(isNaN("converted to NaN"));     // true
console.log(isNaN(NaN));                    // true
console.log(Number.isNaN("not converted")); // false
console.log(Number.isNaN(NaN));             // true

BigInt

BigInts 可用於任意大的整數。特別是大於253 - 1的整數,這是JavaScript可以用 Number 原語可靠表示的最大數字,並由Number.MAX_SAFE_INTEGER常量表示。

let t = 100n;
let t1 = BigInt('100');
let t2 = BigInt('0x64');
let t3 = BigInt(100);
//这几种方法,都可以定义BigInt

Math的方法不支持BigInt。

JSON.stringify不支持BigInt,需要BigInt.prototype.toJSON = function() { return this.toString(); }後就可以支持。

toString方法支持BigInt,radix有效值是[2,36],默認是10。

BigInt除法,結果會被截斷為整數,不是浮點數。如:100n/7n;結果為14n,不是浮點數。

String

JavaScript中的字符串是一個字符序列。在JavaScript中,可以通過將一系列字符放在雙引號(") 或單引號(')之間直接創建字符串(作為字面量)。此種字符串必須寫在單行上,但可包含轉義換行符(如\n). JavaScript標準允許反引號字符(`,即重音符或反撇號)引用多行字符串字面量,但這僅在2016年開始的某些瀏覽器上支持:Firefox和Chrome,但Internet Explorer 11不支持。[13]

var greeting = "Hello, World!";
var anotherGreeting = 'Greetings, people of Earth.';

可以使用charAt方法(由String.prototype提供)訪問字符串中的單個字符。這是訪問字符串中的單個字符時的首選方式,因為它也適用於非現代瀏覽器:

var h = greeting.charAt(0);

在現代瀏覽器中,可以通過與數組相同的表示法訪問字符串中的單個字符:

var h = greeting[0];

但是,JavaScript字符串是不可變的

greeting[0] = "H"; // Fails.

如果字符串具有相同的內容,則將相等運算符 ("==") 應用於兩個字符串將返回 true,這意味着:具有相同的長度並包含相同的字符序列(大小寫對字母很重要)。因此:

var x = "World";
var compare1 = ("Hello, " +x == "Hello, World"); // Here compare1 contains true.
var compare2 = ("Hello, " +x == "hello, World"); // Here compare2 contains false since the first characters of the second operands are not of the same case.

除非轉義,否則不能嵌套相同類型的引號:

var x = '"Hello, World!" he said.'; // Just fine.
var x = ""Hello, World!" he said."; //  Not good.
var x = "\"Hello, World!\" he said."; // Works by escaping " with \"

String構造函數創建一個字符串對象(一個包裝字符串的對象):

var greeting = new String("Hello, World!");

這些對象有一個valueOf方法,返回包裝在其中的原始字符串:

var s = new String("Hello !");
typeof s; // Is 'object'.
typeof s.valueOf(); // Is 'string'.

兩個String對象之間的相等與字符串原語不同:

var s1 = new String("Hello !");
var s2 = new String("Hello !");
s1 == s2; // Is false, because they are two distinct objects.
s1.valueOf() == s2.valueOf(); // Is true.

Boolean

JavaScript提供了一個帶有真假字面量的布爾數據類型typeof運算符為這些基本類型返回字符串「"boolean"」。 在邏輯上下文中使用時,由於自動類型轉換0-0nullNaNundefined和空字符串("")求值為false。 所有其他值(前一個列表的補集)求值為true,包括字符串"0""false"和任何對象。

類型轉換

使用類型檢查的比較運算符(===!==)可以避免相等比較運算符(==!=)的自動類型強制(type coercion)。

當需要類型轉換時,JavaScript會按如下方式轉換BooleanNumberStringObject操作數:[14]

Number和String
字符串被轉換為數字值。 JavaScript嘗試將字符串數字字面量轉換為數字類型的值。首先,從字符串數字字面量推導出數學值。接下來將此值四捨五入到最接近的數字類型值。
Boolean
如果其中一個操作數是布爾值,則該布爾操作數如果為true則轉換為 1,如果為false則轉換為 0。
Object
如果將對象與數字或字符串進行比較,JavaScript會嘗試返回該對象的默認值。使用對象的.valueOf().toString()方法將對象轉換為基本String或Number值。如果失敗,則會生成運行時錯誤。

道格拉斯·克羅克福特提倡用「truthy」和「falsy」來描述各種類型的值在邏輯上下文中的行為方式,特別是在邊緣情況下。[15]在JavaScript的早期版本中,二元邏輯運算符返回一個布爾值,但現在它們返回一個操作數。在合取(a && b)時,如果左操作數為false,則返回左操作數;或在析取(a || b)時,如果左操作數為true,則返回左操作數;否則返回右操作數。對於混合了布爾操作數和number兼容操作數(包括可以計算為number的字符串,或可以計算為此類字符串的對象),比較運算符的自動類型強制可能會有所不同,因為布爾操作數將作為一個數值被比較。這可能出乎意料。表達式可以通過雙重邏輯否定運算符:(!!)、使用Boolean()函數或使用條件運算符:(c ? t : f)顯式轉換為布爾基本類型。

// Automatic type coercion
console.log(true  ==   2 ); // false... true  → 1 !== 2 ←  2
console.log(false ==   2 ); // false... false → 0 !== 2 ←  2
console.log(true  ==   1 ); // true.... true  → 1 === 1 ←  1
console.log(false ==   0 ); // true.... false → 0 === 0 ←  0
console.log(true  ==  "2"); // false... true  → 1 !== 2 ← "2"
console.log(false ==  "2"); // false... false → 0 !== 2 ← "2"
console.log(true  ==  "1"); // true.... true  → 1 === 1 ← "1"
console.log(false ==  "0"); // true.... false → 0 === 0 ← "0"
console.log(false ==  "" ); // true.... false → 0 === 0 ← ""
console.log(false ==  NaN); // false... false → 0 !== NaN

console.log(NaN == NaN); // false...... NaN is not equivalent to anything, including NaN.

// Type checked comparison (no conversion of types and values)
console.log(true === 1); // false...... data types do not match

// Explicit type coercion
console.log(true === !!2);   // true.... data types and values match
console.log(true === !!0);   // false... data types match, but values differ
console.log( 1  ? true : false); // true.... only ±0 and NaN are "falsy" numbers
console.log("0" ? true : false); // true.... only the empty string is "falsy"
console.log(Boolean({}));    // true.... all objects are "truthy"

new運算符可用於為布爾基本類型創建對象包裝器。但是,typeof運算符不返回對象包裝器的boolean,它返回object。因為所有對象都求值為true,所以必須使用諸如.valueOf().toString()之類的方法來檢索包裝的值。對於Boolean類型的顯式強制,Mozilla建議優先使用Boolean()函數(沒有new)而不是 Boolean 對象。

var b = new Boolean(false);   // Object  false {}
var t = Boolean(b);           // Boolean true
var f = Boolean(b.valueOf()); // Boolean false
var n = new Boolean(b);       // Not recommended
n = new Boolean(b.valueOf()); // Preferred

if (0 || -0 || "" || null || undefined || b.valueOf() || !new Boolean() || !t) {
  console.log("Never this");
} else if ([] && {} && b && typeof b === "object" && b.toString() === "false") {
  console.log("Always this");
}

Symbol

ECMAScript6引入的新類型。一個Symbol是獨一無二且不可變的標識符。

例如:

var x = Symbol(1);
var y = Symbol(1);
console.log(x === y); // => false

var symbolObject = {};
var normalObject = {};

// since x and y are unique,
// they can be used as unique keys in an object
symbolObject[x] = 1;
symbolObject[y] = 2;

console.log(symbolObject[x]); // => 1
console.log(symbolObject[y]); // => 2

// as compared to normal numeric keys
normalObject[1] = 1;
normalObject[1] = 2; // overrides the value of 1

console.log(normalObject[1]); // => 2

// changing the value of x does not change the key stored in the object
x = Symbol(3);
console.log(symbolObject[x]); // => undefined

// changing x back just creates another unique Symbol
x = Symbol(1);
console.log(symbolObject[x]); // => undefined

存在well known symbols。其中一個是Symbol.iterator; 如果程序實現了Symbol.iterator,則它是可迭代的:

let x = [1, 2, 3, 4]; // x is an Array
x[Symbol.iterator] === Array.prototype[Symbol.iterator]; // and Arrays are iterable

const xIterator = x[Symbol.iterator](); // The [Symbol.iterator] function should provide an iterator for x
xIterator.next(); // { value: 1, done: false }
xIterator.next(); // { value: 2, done: false }
xIterator.next(); // { value: 3, done: false }
xIterator.next(); // { value: 4, done: false }
xIterator.next(); // { value: undefined, done: true }
xIterator.next(); // { value: undefined, done: true }

// for..of loops automatically iterate values
for (const value of x) {
   console.log(value); // 1 2 3 4
}

// Sets are also iterable:
[Symbol.iterator] in Set.prototype; // true

for (const value of new Set(['apple', 'orange'])) {
   console.log(value); // "apple" "orange"
}

原生對象

JavaScript語言提供了一些原生對象(native object)。JavaScript原生對象被認為是JavaScript規範的一部分。 儘管有JavaScript環境,但這組對象應該始終可用。

Array

數組(array)是從Array構造函數原型化的JavaScript對象,專門設計用於存儲由整數鍵索引的數據值。數組與基本的Object類型不同,它是具有方法和屬性的原型,以幫助程式設計師完成日常任務(例如,join, slicepush)。

C語言家族的程式語言一樣,數組使用從零開始的索引方案:通過push方法插入空數組的值佔據數組的第0個索引。

var myArray = [];            // Point the variable myArray to a newly ...
                             // ... created, empty Array
myArray.push("hello World"); // Fill the next empty index, in this case 0
console.log(myArray[0]);           // Equivalent to console.log("hello World");

數組有一個length屬性,保證總是大於數組中使用的最大整數索引。如果創建具有更大索引的屬性,它會自動更新。將較小的數字寫入length屬性將刪除較大的索引。

可以使用普通對象屬性訪問表示法訪問Array的元素:

myArray[1];  // the 2nd item in myArray
myArray["1"];

以上兩個是等價的。 不能使用「點」表示法或帶有替代數字表示的字符串:

myArray.1;     // syntax error
myArray["01"]; // not the same as myArray[1]

聲明一個數組可用Array字面量或者Array構造函數:

let myArray;

// Array literals
myArray = [1, 2]; // length of 2
myArray = [1, 2,]; // same array - You can also have an extra comma at the end

// It's also possible to not fill in parts of the array
myArray = [0, 1, /* hole */, /* hole */, 4, 5]; // length of 6
myArray = [0, 1, /* hole */, /* hole */, 4, 5,]; // same array
myArray = [0, 1, /* hole */, /* hole */, 4, 5, /* hole */,]; // length of 7

// With the constructor
myArray = new Array(0, 1, 2, 3, 4, 5); // length of 6
myArray = new Array(365);              // an empty array with length 365

數組實現為「稀疏數組」,只有定義的元素佔用了內存。設置myArray[10] = 'someThing'myArray[57] = 'somethingOther'只為這兩個元素分配了內存空間,這與任何其他對象一樣。數組的length仍報告為58。數組的最大長度為4,294,967,295,對應於 32 位二進制無符號數(11111111111111111111111111111111)2

可以使用對象聲明字面量來創建其行為類似於其他語言中的關聯數組對象:

dog = {color: "brown", size: "large"};
dog["color"]; // results in "brown"
dog.color;    // also results in "brown"

可以使用對象和數組聲明字面量來快速創建關聯數組、多維數組或兩者兼有的數組。從技術上講,JavaScript不支持多維數組,但可以使用數組數組來模仿它們:

cats = [{color: "brown", size: "large"},
    {color: "black", size: "small"}];
cats[0]["size"];      // results in "large"

dogs = {rover: {color: "brown", size: "large"},
    spot: {color: "black", size: "small"}};
dogs["spot"]["size"]; // results in "small"
dogs.rover.color;     // results in "brown"

Date

Date對象存儲有符號的毫秒計數,零表示1970-01-01 00:00:00 UT,值域為±108 天。有幾種方法可以為Date構造函數提供參數。注意,月份是從零開始的。

new Date();                       // create a new Date instance representing the current time/date.
new Date(2010, 2, 1);             // create a new Date instance representing 2010-Mar-01 00:00:00
new Date(2010, 2, 1, 14, 25, 30); // create a new Date instance representing 2010-Mar-01 14:25:30
new Date("2010-3-1 14:25:30");    // create a new Date instance from a String.

提供了抽取字段的方法,以及有用的toString:

var d = new Date(2010, 2, 1, 14, 25, 30); // 2010-Mar-01 14:25:30;

// Displays '2010-3-1 14:25:30':
console.log(d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + ' '
    + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds());

// Built-in toString returns something like 'Mon Mar 01 2010 14:25:30 GMT-0500 (EST)':
console.log(d);

Error

Error類可以創建定製的錯誤消息:

throw new Error("Something went wrong.");

這可被try...catch...finally塊捕獲。將在異常處理節中詳述。

Math

The Math object contains various math-related constants (for example, ) and functions (for example, cosine). (Note that the object has no constructor, unlike or . All its methods are "static", that is "class" methods.) All the trigonometric functions use angles expressed in radians, not degrees or grads. Math對象包含各種與數學相關的常數(例如,π)和函數(例如,餘弦)。 請注意,與ArrayDate不同,Math對象沒有構造函數。它的所有方法都是「靜態的」,即「類」方法。所有三角函數都使用以弧度表示角度,而不是度數百分度

Math對象中包含的一些常量
屬性 返回值
四捨五入到5位數字
說明
Math.E 2.7183 e: 自然對數基
Math.LN2 0.69315 2的自然對數
Math.LN10 2.3026 10的自然對數
Math.LOG2E 1.4427 e的以2為基的對數
Math.LOG10E 0.43429 e的以10為基的對數
Math.PI 3.14159 π: 圓周率
Math.SQRT1_2 0.70711 ½的平方根
Math.SQRT2 1.4142 2的平方根
Math對象的方法
例子 返回值
四捨五入到5位數字
說明
Math.abs(-2.3) 2.3 絕對值
Math.acos(Math.SQRT1_2) 0.78540 rad = 45° 反餘弦
Math.asin(Math.SQRT1_2) 0.78540 rad = 45° 反正弦
Math.atan(1) 0.78540 rad = 45° 半圓反正切 ()
Math.atan2(-3.7, -3.7) −2.3562 rad = −135° 全圓反正切 ()
Math.ceil(1.1) 2 Ceiling: 近似到最小整數≥參數
Math.cos(Math.PI/4) 0.70711 餘弦函數
Math.exp(1) 2.7183 指數函數: e的指數值
Math.floor(1.9) 1 Floor:近似到最大整數≤參數
Math.log(Math.E) 1 基於e的自然對數
Math.max(1, -2) 1 最大值: (x > y) ? x : y
Math.min(1, -2) −2 最小值: (x < y) ? x : y
Math.pow(-3, 2) 9 指數運算: Math.pow(x, y)返回xy
Math.random() e.g. 0.17068 偽隨機數,值域[0,1)
Math.round(1.5) 2 四捨五入,1.5近似到2
Math.sin(Math.PI/4) 0.70711 正弦
Math.sqrt(49) 7 平方根
Math.tan(Math.PI/4) 1 正切

正則表達式

/expression/.test(string);     // returns Boolean
"string".search(/expression/); // returns position Number
"string".replace(/expression/, replacement);

// Here are some examples
if (/Tom/.test("My name is Tom")) console.log("Hello Tom!");
console.log("My name is Tom".search(/Tom/));          // == 11 (letters before Tom)
console.log("My name is Tom".replace(/Tom/, "John")); // == "My name is John"

字符類

// \d  - digit
// \D  - non digit
// \s  - space
// \S  - non space
// \w  - word char
// \W  - non word
// [ ] - one of
// [^] - one not of
//  -  - range

if (/\d/.test('0'))                   console.log('Digit');
if (/[0-9]/.test('6'))                console.log('Digit');
if (/[13579]/.test('1'))              console.log('Odd number');
if (/\S\S\s\S\S\S\S/.test('My name')) console.log('Format OK');
if (/\w\w\w/.test('Tom'))             console.log('Hello Tom');
if (/[a-zA-Z]/.test('B'))             console.log('Letter');

字符匹配

// A...Z a...z 0...9  - alphanumeric
// \u0000...\uFFFF    - Unicode hexadecimal
// \x00...\xFF        - ASCII hexadecimal
// \t                 - tab
// \n                 - new line
// \r                 - CR
// .                  - any character
// |                  - OR

if (/T.m/.test('Tom')) console.log ('Hi Tom, Tam or Tim');
if (/A|B/.test("A"))  console.log ('A or B');

重複

// ?   - 0 or 1 match
// *   - 0 or more
// +   - 1 or more
// {n}   - exactly n
// {n,}  - n or more
// {0,n} - n or less
// {n,m} - range n to m

if (/ab?c/.test("ac"))       console.log("OK"); // match: "ac", "abc"
if (/ab*c/.test("ac"))       console.log("OK"); // match: "ac", "abc", "abbc", "abbbc" etc.
if (/ab+c/.test("abc"))      console.log("OK"); // match: "abc", "abbc", "abbbc" etc.
if (/ab{3}c/.test("abbbc"))  console.log("OK"); // match: "abbbc"
if (/ab{3,}c/.test("abbbc")) console.log("OK"); // match: "abbbc", "abbbbc", "abbbbbc" etc.
if (/ab{1,3}c/.test("abc"))  console.log("OK"); // match: "abc", "abbc", "abbbc"

Anchors

// ^  - string starts with
// $  - string ends with

if (/^My/.test("My name is Tom"))   console.log ("Hi!");
if (/Tom$/.test("My name is Tom"))  console.log ("Hi Tom!");

子表達式

// ( )  - groups characters

if (/water(mark)?/.test("watermark")) console.log("Here is water!"); // match: "water", "watermark",
if (/(Tom)|(John)/.test("John"))      console.log("Hi Tom or John!");

Flag

// /g  - global
// /i  - ignore upper/lower case
// /m  - allow matches to span multiple lines

console.log("hi tom!".replace(/Tom/i, "John"));  // == "hi John!"
console.log("ratatam".replace(/ta/, "tu"));      // == "ratutam"
console.log("ratatam".replace(/ta/g, "tu"));     // == "ratutum"

高級方法

my_array = my_string.split(my_delimiter);
// example
my_array = "dog,cat,cow".split(",");      // my_array==["dog","cat","cow"];

my_array = my_string.match(my_expression);
// example
my_array = "We start at 11:30, 12:15 and 16:45".match(/\d\d:\d\d/g); // my_array==["11:30","12:15","16:45"];

Capturing group

var myRe = /(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})/;
var results = myRe.exec("The date and time are 2009-09-08 09:37:08.");
if (results) {
  console.log("Matched: " + results[0]); // Entire match
  var my_date = results[1]; // First group == "2009-09-08"
  var my_time = results[2]; // Second group == "09:37:08"
  console.log("It is " + my_time + " on " + my_date);
} else console.log("Did not find a valid date!");

函數

JavaScript的每個函數是Function構造符的一個實例。當然還有async / generator函數構造符:(async()=>{})構造符得到AsyncFunction, (async function*(){})構造符得到AsyncGeneratorFunction, (function*(){})構造符得到GeneratorFunction。

// x, y is the argument. 'return x + y' is the function body, which is the last in the argument list.
var add = new Function('x', 'y', 'return x + y');
add(1, 2); // => 3

上述加法函數也可定義為函數表達式:

var add = function(x, y) {
  return x + y;
};
add(1, 2); // => 3

ES6中,增加了箭頭函數語法(arrow function syntax)。與function() {}表達式不同,它們還保留全局對象的this而不是從調用它的位置/調用時的值來繼承它。

var add = (x, y) => {return x + y;};
// values can also be implicitly returned (i.e. no return statement is needed)
var addImplicit = (x, y) => x + y;

add(1, 2); // => 3
addImplicit(1, 2) // => 3

對於需要提升的函數,專門的表達式:

function add(x, y) {
  return x + y;
}
add(1, 2); // => 3

提升允許你在聲明之前使用它:

add(1, 2); // => 3, not a ReferenceError
function add(x, y) {
  return x + y;
}

函數實例可具有屬性和方法:

function subtract(x, y) {
  return x - y;
}

console.log(subtract.length); // => 2, arity of the function (number of arguments)
console.log(subtract.toString());

/*
"function subtract(x, y) {
  return x - y;
}"
*/

運算符

'+'運算符重載:它用於字符串連接和算術加法。 當無意中混合字符串和數字時,這可能會導致問題。 作為一元運算符,它可以將數字字符串轉換為數字。

// Concatenate 2 strings
console.log('He' + 'llo'); // displays Hello

// Add two numbers
console.log(2 + 6);  // displays 8

// Adding a number and a string results in concatenation (from left to right)
console.log(2 + '2');    // displays 22
console.log('$' + 3 + 4);  // displays $34, but $7 may have been expected
console.log('$' + (3 + 4)); // displays $7
console.log(3 + 4 + '7'); // displays 77, numbers stay numbers until a string is added

// Convert a string to a number using the unary plus
console.log(+'2' === 2); // displays true
console.log(+'Hello'); // displays NaN

類似地, '*'運算符也是重載的:可以把字符串轉為數。

console.log(2 + '6'*1);  // displays 8
console.log(3*'7'); // 21
console.log('3'*'7'); // 21
console.log('hello'*'world'); // displays NaN

算術運算符

JavaScript支持下述二元算術運算符

+ 加法
- 加法
* 乘法
/ 除法(返回浮點值)
% 求余(返回餘數)
** 指數

JavaScript支持下述酉算術運算符:

+ 字符串轉數
- 符號取反
++ 前增或後增
-- 前減或後減

例如:

var x = 1;
console.log(++x); // x becomes 2; displays 2
console.log(x++); // displays 2; x becomes 3
console.log(x);  // x is 3; displays 3
console.log(x--); //  displays 3; x becomes 2
console.log(x);  //  displays 2; x is 2
console.log(--x); //  x becomes 1; displays 1

模運算符顯示除以模數後的餘數。 如果涉及負數,則返回值取決於操作數。

var x = 17;
console.log(x%5); // displays 2
console.log(x%6); // displays 5
console.log(-x%5); // displays -2
console.log(-x%-5); // displays -2
console.log(x%-5); // displays 2

要始終返回非負數,請重新添加模數並再次應用模運算符:

var x = 17;
console.log((-x%5+5)%5); // displays 3

賦值複合運算符

= 賦值
+= 加法賦值
-= 減法賦值
*= 乘法賦值
/= 除法賦值
%= 求余賦值
**= 指數賦值

基本類型的賦值:

var x = 9;
x += 1; 
console.log(x); // displays: 10
x *= 30;
console.log(x); // displays: 300
x /= 6;
console.log(x); // displays: 50
x -= 3;
console.log(x); // displays: 47
x %= 7;
console.log(x); // displays: 5

對象類型的賦值:

/**
 * To learn JavaScript objects...
 */
var object_1 = {a: 1};		// assign reference of newly created object to object_1
var object_2 = {a: 0};
var object_3 = object_2;	// object_3 references the same object as object_2 does
	 
object_3.a = 2;
message();	        	// displays 1 2 2
	 
object_2 = object_1;		// object_2 now references the same object as object_1
	        	        // object_3 still references what object_2 referenced before
message();		        // displays 1 1 2
	 
object_2.a = 7;  	        // modifies object_1
message();		        // displays 7 7 2

object_3.a = 5;                 // object_3 doesn't change object_2
message();	                // displays 7 7 5

object_3 = object_2;	
object_3.a=4;                  // object_3 changes object_1 and object_2
message();                     // displays 4 4 4

/**
 * Prints the console.log message
 */
function message() {
	console.log(object_1.a + " " + object_2.a + " " + object_3.a);
}

解構賦值

at its leaves that are to receive the substructures of the assigned value.

在Mozilla的JavaScript中,從1.7版開始,解構賦值(destructuring assignment)允許一次將部分數據結構賦值給多個變量。 賦值的左側是一個類似於任意嵌套對象/數組字面量的模式,在其葉子處包含左值將接收賦值的子結構。

var a, b, c, d, e;
[a, b, c] = [3, 4, 5];
console.log(a + ',' + b + ',' + c); // displays: 3,4,5

e = {foo: 5, bar: 6, baz: ['Baz', 'Content']};
var arr = [];
({baz: [arr[0], arr[3]], foo: a, bar: b}) = e;
console.log(a + ',' + b + ',' + arr);	// displays: 5,6,Baz,,,Content
[a, b] = [b, a];		// swap contents of a and b
console.log(a + ',' + b);		// displays: 6,5

[a, b, c] = [3, 4, 5]; // permutations
[a, b, c] = [b, c, a];
console.log(a + ',' + b + ',' + c); // displays: 4,5,3

擴展/休息運算符

ECMAScript 2015標準引入了「...」運算符,用於「擴展語法」(spread syntax)[16]和「剩餘參數」(rest parameters)的相關概念。[17]

擴展語法提供了另一種解構數組的方法。 它表示指定數組中的元素應用作函數調用中的參數或數組字面量中的項。

換句話說, "..."把"[...foo]"變換為"[foo[0], foo[1], foo[2]]",而"this.bar(...foo);"變換為"this.bar(foo[0], foo[1], foo[2]);":

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

// It can be used multiple times in the same expression
var b = [...a, ...a]; // b = [1, 2, 3, 4, 1, 2, 3, 4];

// It can be combined with non-spread items.
var c = [5, 6, ...a, 7, 9]; // c = [5, 6, 1, 2, 3, 4, 7, 9];

// For comparison, doing this without the spread operator 
// creates a nested array.
var d = [a, a]; // d = [[1, 2, 3, 4], [1, 2, 3, 4]]

// It works the same with function calls
function foo(arg1, arg2, arg3) {
    console.log(arg1 + ':' + arg2 + ':' + arg3);
}

// You can use it even if it passes more parameters than the function will use
foo(...a); // "1:2:3" → foo(a[0], a[1], a[2], a[3]);

// You can mix it with non-spread parameters
foo(5, ...a, 6); // "5:1:2" → foo(5, a[0], a[1], a[2], a[3], 6);

// For comparison, doing this without the spread operator
// assigns the array to arg1, and nothing to the other parameters.
foo(a); // "1,2,3,4:undefined:undefined"

當在函數聲明中使用...時,它表示一個剩餘參數(rest parameter)。 剩餘參數必須是函數參數列表中的最右側的命名參數。 它將被賦值一個Array,其中包含傳遞給函數超過其他命名參數的任何多餘的參數。 換句話說,它獲取傳遞給函數的「多餘的」參數(因此得名)。

function foo(a, b, ...c) {
    console.log(c.length);
}

foo(1, 2, 3, 4, 5); // "3" → c = [3, 4, 5]
foo('a', 'b'); // "0" → c = []

剩餘參數類似於Javascript的 arguments對象,它是一個類似數組的對象,包含當前函數調用中的所有參數(命名和未命名)。 然而,與 arguments參數不同的是,剩餘參數是真正的Array對象,因此可以直接對它們使用.slice().sort()等方法。

...運算符只能與Array對象一起使用。然而,在未來的ECMAScript標準中,有人提議將其擴展到Object[18])

比較運算符

== 相等
!= 不等
> 大於
>= 大於等於
< 小於
<= 小於等於
=== 帶類型檢查的相等比較
!== 不相同

引用對象的變量只有在引用同一個對象時才相等或者相同:

var obj1 = {a: 1};
var obj2 = {a: 1};
var obj3 = obj1;
console.log(obj1 == obj2);  //false
console.log(obj3 == obj1);  //true
console.log(obj3 === obj1); //true

參見String.

邏輯運算符

JavaScript提供4個邏輯運算符:

在邏輯運算上下文中,除下述表達式外,任何表達式的計算結果都為true:

  • 字符串: "", '',
  • 數: 0, -0, NaN,
  • 特殊: null, undefined,
  • 布爾: false.

布爾函數可用於顯式轉換為基礎類型Boolean:

// Only empty strings return false
console.log(Boolean("")      === false);
console.log(Boolean("false") === true);
console.log(Boolean("0")     === true);

// Only zero and NaN return false
console.log(Boolean(NaN) === false);
console.log(Boolean(0)   === false);
console.log(Boolean(-0)  === false); // equivalent to -1*0
console.log(Boolean(-2)  === true);

// All objects return true
console.log(Boolean(this) === true);
console.log(Boolean({})   === true);
console.log(Boolean([])   === true);

// These types return false
console.log(Boolean(null)      === false);
console.log(Boolean(undefined) === false); // equivalent to Boolean()

The NOT operator evaluates its operand as a Boolean and returns the negation. Using the operator twice in a row, as a double negative, explicitly converts an expression to a primitive of type Boolean:

console.log( !0 === Boolean(!0));
console.log(Boolean(!0) === !!1);
console.log(!!1 === Boolean(1));
console.log(!!0 === Boolean(0));
console.log(Boolean(0) === !1);
console.log(!1 === Boolean(!1));
console.log(!"" === Boolean(!""));
console.log(Boolean(!"") === !!"s");
console.log(!!"s" === Boolean("s"));
console.log(!!"" === Boolean(""));
console.log(Boolean("") === !"s");	
console.log(!"s" === Boolean(!"s"));

三元運算符可用於顯式轉換:

console.log([] == false); console.log([] ? true : false); // “truthy”, but the comparison uses [].toString()
console.log([0] == false); console.log([0]? true : false); // [0].toString() == "0"
console.log("0" == false); console.log("0"? true : false); // "0" → 0 ... (0 == 0) ... 0 ← false
console.log([1] == true); console.log([1]? true : false); // [1].toString() == "1"
console.log("1" == true); console.log("1"? true : false); // "1" → 1 ... (1 == 1) ... 1 ← true
console.log([2] != true); console.log([2]? true : false); // [2].toString() == "2"
console.log("2" != true); console.log("2"? true : false); // "2" → 2 ... (2 != 1) ... 1 ← true

使用後增量(i++)等特性的表達式具有預期的副作用。 JavaScript提供了表達式的短路求值; 僅當左操作數不足以確定表達式的值時,右操作數才被執行。

console.log(a || b);  // When a is true, there is no reason to evaluate b.
console.log(a && b);  // When a is false, there is no reason to evaluate b.
console.log(c ? t : f); // When c is true, there is no reason to evaluate f.

In early versions of JavaScript and JScript, the binary logical operators returned a Boolean value (like most C-derived programming languages). However, all contemporary implementations return one of their operands instead: 在JavaScript和JScript的早期版本中,二元邏輯運算符返回一個布爾值(就像大多數從C派生的程式語言一樣)。但是,所有Javascript的當代實現都返回其操作數之一:

console.log(a || b); // if a is true, return a, otherwise return b
console.log(a && b); // if a is false, return a, otherwise return b

更熟悉C中的行為的程式設計師可能會發現這個特性令人驚訝,但它允許更簡潔地表達模式,如空值結合運算符

var s = t || "(default)"; // assigns t, or the default value, if t is null, empty, etc.

邏輯賦值

??= 空值結合賦值
||= 邏輯或賦值
&&= 邏輯與賦值

按位運算符

& AND
| OR
^ XOR
! NOT
<< 左移(右端零填充)
>> 右移 (左端為符號位複製)
>>> 右移(左端零填充)

例如:

x=11 & 6;
console.log(x); // 2

JavaScript支持酉按位運算符:

~ NOT (按位取反)

位操作賦值

&= and
|= or
^= xor
<<= 左移(右端零填充)
>>= 右移 (左端為符號位複製)
>>>= 右移(左端零填充)

例如:

x=7;
console.log(x); // 7
x<<=3;
console.log(x); // 7->14->28->56

字符串

= 賦值
+ 連接
+= 連接賦值

例如:

str = "ab" + "cd";  // "abcd"
str += "e";      // "abcde"

str2 = "2" + 2;     // "22", not "4" or 4.

??

JavaScript最接近的運算符是??, 即"nullish coalescing operator",從ECMAScript第11版引入。[19]當左端操作數不為"nullish" (nullundefined),取其值作為結果,否則取右端操作數作為結果。 例如:

const a = b ?? 3;
邏輯或運算符(||)對任何布爾假值:null, undefined, "", 0, NaN, false,都會取右端操作數的值。

控制結構

複合語句

一對大括號 { } 和一個封閉的語句序列構成一個複合語句,可以在任何可以使用語句的地方使用。

If ... else

if (expr) {
  //statements;
} else if (expr2) {
  //statements;
} else {
  //statements;
}

三元調解運算符

條件運算符創建一個表達式,該表達式根據條件計算為兩個表達式之一。 這類似於根據條件選擇兩個語句之一執行的「if」語句。 即,條件運算符之於表達式就像 if 之於語句。

 result = condition ? expression : alternative;

等價於:

 if (condition) {
  result = expression;
 } else {
  result = alternative;
 }

if 語句不同,條件運算符不能省略其「else-branch」。

Switch語句

JavaScript的switch語句如下:

 switch (expr) {
  case SOMEVALUE:
   // statements;
   break;
  case ANOTHERVALUE:
   // statements;
   break;
  default:
   // statements;
   break;
 }
  • break; 是可選的; 但是,通常需要它,因為否則代碼執行將繼續到下一個案例塊的主體。
  • 作為預防措施,在最後一個案例的末尾添加一個 break 語句,以防以後添加其他案例。
  • 可用字符串字面量作為case值。
  • 可以使用表達式代替值。
  • 當表達式不匹配任何其他指定的情況時,執行默認情況(可選)。
  • 大括號是必需的。

For循環

 for (initial; condition; loop statement) {
  /*
   statements will be executed every time
   the for{} loop cycles, while the
   condition is satisfied
  */
 }

或:

 for (initial; condition; loop statement(iteration)) // one statement

For ... in loop

for (var property_name in some_object) {
  // statements using some_object[property_name];
}
  • 遍歷對象的所有可枚舉屬性。
  • 遍歷數組的所有使用索引,包括數組對象的所有用戶定義屬性(如果有)。 因此,在遍歷數組時,最好使用傳統的for循環對索引的所有值循環。
  • 不同的Web瀏覽器在 for...in 循環語句反映哪些屬性方面存在差異。理論上,這是由ECMAscript標準定義的稱為「DontEnum」的內部狀態屬性控制的,但實際上,每個瀏覽器在自省期間返回的屬性集略有不同。使用 if (some_object.hasOwnProperty(property_name)) { ...} 測試給定屬性很有用。 因此,使用 Array.prototype.newMethod = function() {...} 向數組原型添加方法可能會導致 for ... in 循環遍歷方法的名稱。

While循環

while (condition) {
  statement1;
  statement2;
  statement3;
  ...
}

Do ... while循環

do {
  statement1;
  statement2;
  statement3;
  ...
} while (condition);

With

with語句將所有給定對象的屬性和方法添加到以下塊的範圍內,讓它們被引用,就好像它們是局部變量一樣。

with (document) {
  var a = getElementById('a');
  var b = getElementById('b');
  var c = getElementById('c');
};
  • 請注意在每次getElementById()調用之前缺少document.

因為with語句的可用性會阻礙程序性能並且被認為會降低代碼的清晰度(因為任何給定的變量實際上可能是封閉with的屬性),因此在「嚴格模式」中不允許使用此語句 .

Label

JavaScript在大多數實現中都支持嵌套標籤。可以為break語句標記循環或塊,為continue標記循環。雖然 goto 是保留字,[20] goto在JavaScript中未實現。

loop1: for (var a = 0; a < 10; a++) {
  if (a == 4) {
    break loop1; // Stops after the 4th attempt
  }
  console.log('a = ' + a);
  loop2: for (var b = 0; b < 10; ++b) {
    if (b == 3) {
     continue loop2; // Number 3 is skipped
    }
    if (b == 6) {
     continue loop1; // Continues the first loop, 'finished' is not shown
    }
    console.log('b = ' + b);
  }
  console.log('finished');
}
block1: {
  console.log('Hello'); // Displays 'Hello'
  break block1;
  console.log('World'); // Will never get here
}
goto block1; // Parse error.

函數

函數沒有返回語句的情況下,則返回值undefined

function gcd(segmentA, segmentB) {
  var diff = segmentA - segmentB;
  if (diff == 0) 
    return segmentA;
  return diff > 0 ? gcd(segmentB, diff) : gcd(segmentA, -diff);
}
console.log(gcd(60, 40)); // 20

var mygcd = gcd; // mygcd is a reference to the same function as gcd. Note no argument ()s.
console.log(mygcd(60, 40)); // 20

函數時頭等對象,可以賦值給其他變量。

調用函數時給出的參數數量不一定與函數定義中的參數數量相對應;在調用時沒有匹配實參的命名形參具有值undefined(可以隱式轉換為 false)。在函數中,參數也可以通過arguments對象訪問; 這提供了使用索引訪問所有參數(例如 arguments[0], arguments[1], ... arguments[n]),包括那些超出命名參數數量的參數。(雖然參數列表具有 .length 屬性,但它不是Array的實例;它沒有諸如.slice( ).sort()等)

function add7(x, y) {
  if (!y) {
    y = 7;
  }
  console.log(x + y + arguments.length);
};
add7(3); // 11
add7(3, 4); // 9

原始值(數字、布爾值、字符串)按值傳遞。 對於對象,它是對傳遞的對象的引用。

var obj1 = {a : 1};
var obj2 = {b : 2};
function foo(p) {
  p = obj2; // Ignores actual parameter
  p.b = arguments[1];
}
foo(obj1, 3); // Does not affect obj1 at all. 3 is additional parameter
console.log(obj1.a + " " + obj2.b); // writes 1 3

函數可以在其他函數內部聲明,並訪問外部函數的局部變量。 此外,即使在外部函數退出後,它們也會通過記住外部函數的局部變量來實現完整的閉包

var v = "Top";
var bar, baz;
function foo() {
  var v = "fud";
  bar = function() { console.log(v) };
  baz = function(x) { v = x; };
}
foo();
baz("Fugly");
bar(); // Fugly (not fud) even though foo() has exited.
console.log(v); // Top

Async/await

JavaScript中的await運算符只能用於async標註的函數中,或者用於模塊的最頂層。

如果await運算符後跟參數為Promise對象,那麼函數邏輯會在該Promise對象被resolve之後繼續執行,或者在它被reject以後拋出異常(可以進行異常處理);如果await運算符後跟參數不是Promise對象,那麼該值會被直接返回(不會等待)。[21]

許多JavaScript庫提供了可返回Promise對象的函數,它們都可以被await——只要符合JavaScript中的Promise規範。JQuery中函數返回的Promise在3.0版本以後才達到了Promises/A+兼容度。[22]

下面是一個使用例[23]

async function createNewDoc() {
  let response = await db.post({}); // post a new doc
  return db.get(response.id); // find by id
}

async function main() {
  try {
    let doc = await createNewDoc();
    console.log(doc);
  } catch (err) {
    console.log(err);
  }
}
main();
Node.js 8 中包含的一樣工具可將標準庫中利用回調模式編寫的函數當作Promise來使用。[24]

對象Object

為方便起見,類型通常被細分為「基本」(primitives)和「對象」。 對象是具有標識符的實體(它們僅與自身相同)並將屬性名稱映射到值(基於原型編程 術語中的「槽」slot)。 對象可以被認為是 關聯數組或散列(hash),並且通常使用這些數據結構來實現。但是,對象具有其他功能,例如原型鏈(prototype chain),這是普通關聯數組所沒有的。

JavaScript有幾種內置對象,分別是ArrayBooleanDateFunctionMathNumberObjectRegExpString。 其他對象是「宿主對象」(host object),不是由語言定義,而是由運行時環境定義。 例如,在瀏覽器中,典型的宿主對象屬於DOM(窗口、表單、連結等)。

創建對象

可以使用構造函數或對象字面量創建對象。構造函數可以使用內置的Object函數或自定義函數。按照慣例,構造函數的名稱以大寫字母開頭:

// Constructor
var anObject = new Object();

// Object literal
var objectA = {};
var objectA2 = {};  // A != A2, {}s create new objects as copies.
var objectB = {index1: 'value 1', index2: 'value 2'};

// Custom constructor (see below)

對象字面量和數組字面量允許人們輕鬆創建靈活的數據結構:

var myStructure = {
  name: {
    first: "Mel",
    last: "Smith"
  },
  age: 33,
  hobbies: ["chess", "jogging"]
};

這是 JSON 的基礎,它是一種使用類似JavaScript的語法進行數據交換的簡單表示法。

方法

method是一個已分配給對象的屬性名稱的函數。與許多面向對象的語言不同,在與對象相關的JavaScript中,函數定義和方法定義沒有區別。相反,區別發生在函數調用期間。函數可以作為方法調用。

當作為方法調用時,標準局部變量 this 會自動設置為 "." 左側的對象實例。(還有 callapply 方法可以顯式設置 this - 一些包,例如 jQuerythis 做不尋常的事情。)

在下面的示例中,Foo被用作構造函數。構造函數沒有什麼特別之處——它只是一個初始化對象的普通函數。當與 new 關鍵字一起使用時,通常情況下,this 設置為新創建的空白對象。

請注意,在下面的示例中,Foo只是為槽分配值,其中一些值是函數。因此它可以為不同的實例分配不同的功能。此示例中沒有原型設計。

function px() { return this.prefix + "X"; }

function Foo(yz) {
  this.prefix = "a-";
  if (yz > 0) {
    this.pyz = function() { return this.prefix + "Y"; };
  } else {
    this.pyz = function() { return this.prefix + "Z"; };
  }
  this.m1 = px;
  return this;
}

var foo1 = new Foo(1);
var foo2 = new Foo(0);
foo2.prefix = "b-";

console.log("foo1/2 " + foo1.pyz() + foo2.pyz());
// foo1/2 a-Y b-Z

foo1.m3 = px; // Assigns the function itself, not its evaluated result, i.e. not px()
var baz = {"prefix": "c-"};
baz.m4 = px; // No need for a constructor to make an object.

console.log("m1/m3/m4 " + foo1.m1() + foo1.m3() + baz.m4());
// m1/m3/m4 a-X a-X c-X

foo1.m2(); // Throws an exception, because foo1.m2 doesn't exist.

構造函數

構造函數只需將值分配給新創建對象的槽。 這些值可以是數據或其他函數。

示例:操作對象:

function MyObject(attributeA, attributeB) {
  this.attributeA = attributeA;
  this.attributeB = attributeB;
}

MyObject.staticC = "blue"; // On MyObject Function, not object
console.log(MyObject.staticC); // blue

object = new MyObject('red', 1000);

console.log(object.attributeA); // red
console.log(object["attributeB"]); // 1000

console.log(object.staticC); // undefined
object.attributeC = new Date(); // add a new property

delete object.attributeB; // remove a property of object
console.log(object.attributeB); // undefined

delete object; // remove the whole Object (rarely used)
console.log(object.attributeA); // throws an exception

構造函數本身在對象原型的「構造函數」槽中被引用。 所以,

function Foo() {}
// Use of 'new' sets prototype slots (for example, 
// x = new Foo() would set x's prototype to Foo.prototype,
// and Foo.prototype has a constructor slot pointing back to Foo).
x = new Foo();
// The above is almost equivalent to
y = {};
y.constructor = Foo;
y.constructor();
// Except
x.constructor == y.constructor // true
x instanceof Foo // true
y instanceof Foo // false
// y's prototype is Object.prototype, not
// Foo.prototype, since it was initialised with
// {} instead of new Foo.
// Even though Foo is set to y's constructor slot,
// this is ignored by instanceof - only y's prototype's
// constructor slot is considered.

函數本身就是對象,可以用來產生類似於「靜態屬性」(使用 C++/Java 術語)的效果,如下所示。(函數對象還有一個特殊的 prototype 屬性,如下面的「繼承」部分所述。)

對象刪除很少使用,因為腳本引擎將垃圾收集不再被引用的對象。

繼承

JavaScript以Self的方式通過原型設計(prototyping)支持繼承層次結構。

在以下示例中,Derived類繼承自 Base類。當d創建為Derived時,對Base基礎實例的引用被複製到 d.base

Derive不包含aBaseFunction的值,因此在訪問aBaseFunction時從aBaseFunction檢索它。 這可以通過改變base.aBaseFunction的值來明確,這反映在d.aBaseFunction的值中。

一些實現允許使用__proto__槽顯式訪問或設置原型,如下所示。

function Base() {
  this.anOverride = function() { console.log("Base::anOverride()"); };

  this.aBaseFunction = function() { console.log("Base::aBaseFunction()"); };
}

function Derived() {
  this.anOverride = function() { console.log("Derived::anOverride()"); };
}

base = new Base();
Derived.prototype = base; // Must be before new Derived()
Derived.prototype.constructor = Derived; // Required to make `instanceof` work

d = new Derived();    // Copies Derived.prototype to d instance's hidden prototype slot.
d instanceof Derived; // true
d instanceof Base;    // true

base.aBaseFunction = function() { console.log("Base::aNEWBaseFunction()"); }

d.anOverride();    // Derived::anOverride()
d.aBaseFunction(); // Base::aNEWBaseFunction()
console.log(d.aBaseFunction == Derived.prototype.aBaseFunction); // true

console.log(d.__proto__ == base); // true in Mozilla-based implementations and false in many others.

下面清楚地顯示了在實例創建時對原型的引用是如何「複製」的,但是對原型的更改會影響引用它的所有實例。

function m1() { return "One"; }
function m2() { return "Two"; }
function m3() { return "Three"; }

function Base() {}

Base.prototype.m = m2;
bar = new Base();
console.log("bar.m " + bar.m()); // bar.m Two

function Top() { this.m = m3; }
t = new Top();

foo = new Base();
Base.prototype = t;
// No effect on foo, the *reference* to t is copied.
console.log("foo.m " + foo.m()); // foo.m Two

baz = new Base();
console.log("baz.m " + baz.m()); // baz.m Three

t.m = m1; // Does affect baz, and any other derived classes.
console.log("baz.m1 " + baz.m()); // baz.m1 One

在實踐中,使用了這些主題的許多變體,它可能既強大又令人困惑。

異常處理

JavaScript 包含一個try ... catch ... finally異常處理 語句來處理運行時錯誤。

try ... catch ... finally語句捕獲由錯誤或 throw 語句導致的異常。 它的語法如下:

try {
  // Statements in which exceptions might be thrown
} catch(errorValue) {
  // Statements that execute in the event of an exception
} finally {
  // Statements that execute afterward either way
}

最初,try塊中的語句執行。如果拋出異常,腳本的控制流會立即轉移到catch塊中的語句,異常可用作錯誤參數。否則將跳過catch塊。 如果catch塊不想處理特定錯誤,它可以 throw(errorValue)

在任何情況下,finally塊中的語句都會被執行。這可用於釋放資源,儘管內存會自動進行垃圾回收。

可以省略catch或finally子句。 catch參數是必需的。

Mozilla實現允許多個catch語句,作為對ECMAScript標準的擴展。它們遵循類似於Java中使用的語法:

try { statement; }
catch (e if e == "InvalidNameException")  { statement; }
catch (e if e == "InvalidIdException")    { statement; }
catch (e if e == "InvalidEmailException") { statement; }
catch (e)                                 { statement; }

在瀏覽器中,onerror比捕獲異常更常用。

onerror = function (errorValue, url, lineNr) {...; return true;};

本地幔數與方法

與網頁瀏覽器不相關。

eval (表達式)

將第一個參數計算為表達式,其中可以包括賦值語句。 表達式可以引用函數的局部變量。 但是,eval代表了主要的安全風險,因為它允許不良行為者執行任意代碼,因此不鼓勵使用它。[25]

var x = 7;
console.log("val " + eval("x + 2"));

參考文獻

  1. ^ JavaScript 1.1 specification. [2022-02-28]. (原始內容存檔於2017-02-26). 
  2. ^ Chapter 1. Basic JavaScript. speakingjs.com. [2020-09-22]. (原始內容存檔於2022-02-10). 
  3. ^ Flanagan, David. JavaScript: The definitive Guide需要免費註冊. 2006: 16. ISBN 978-0-596-10199-2. Omitting semicolons is not a good programming practice; you should get into the habit of inserting them. 
  4. ^ 4.0 4.1 4.2 "JavaScript Semicolon Insertion: Everything you need to know頁面存檔備份,存於互聯網檔案館)", ~inimino/blog/頁面存檔備份,存於互聯網檔案館), Friday, 28 May 2010
  5. ^ "Semicolons in JavaScript are optional頁面存檔備份,存於互聯網檔案館)", by Mislav Marohnić, 7 May 2010
  6. ^ Values, Variables, and Literals - MDC. Mozilla Developer Network. 16 September 2010 [1 February 2020]. (原始內容存檔於29 June 2011). 
  7. ^ JavaScript Hoisting. W3Schools. [2022-02-28]. (原始內容存檔於2022-03-31). In JavaScript, a variable can be declared after it has been used. In other words; a variable can be used before it has been declared. 
  8. ^ "JavaScript Scoping and Hoisting頁面存檔備份,存於互聯網檔案館)", Ben Cherry頁面存檔備份,存於互聯網檔案館), Adequately Good頁面存檔備份,存於互聯網檔案館), 2010-02-08
  9. ^ let语句在Mozilla.org. [2022-02-28]. (原始內容存檔於2019-05-28). 
  10. ^ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting頁面存檔備份,存於互聯網檔案館) var_hoisting in mozilla.org]
  11. ^ ECMA-262 5e edition clarified this behavior with the Declarative Environment Record and Object Environment Record. With this formalism, the global object is the Object Environment Record of the global Lexical Environment (the global scope).
  12. ^ comp.lang.javascript FAQ Version 32.2, Updated 2010-10-08, by Garrett Smith. [2022-02-28]. (原始內容存檔於2021-12-07). 
  13. ^ Template literals. MDN Web Docs. [2018-05-02]. (原始內容存檔於2022-03-31) (美國英語). 
  14. ^ Comparison Operators - MDC Doc Center. Mozilla. 5 August 2010 [5 March 2011]. (原始內容存檔於2012-05-04). 
  15. ^ The Elements of JavaScript Style. Douglas Crockford. [5 March 2011]. (原始內容存檔於2011-03-17). 
  16. ^ Spread syntax. [2022-02-28]. (原始內容存檔於2017-06-15). 
  17. ^ rest parameters. [2022-02-28]. (原始內容存檔於2018-05-30). 
  18. ^ Ecmascript. (原始內容存檔於2016-08-09). 
  19. ^ ECMAScript 2020 Language Specification. Ecma International. June 2020 [2021-08-30]. (原始內容存檔於2020-12-23). 
  20. ^ ECMA-262, Edition 3, 7.5.3 Future Reserved Words
  21. ^ await - JavaScript (MDN). [2 May 2017]. (原始內容存檔於2017-06-02). 
  22. ^ jQuery Core 3.0 Upgrade Guide. [2 May 2017]. (原始內容存檔於2021-01-21). 
  23. ^ Taming the asynchronous beast with ES7. [12 November 2015]. (原始內容存檔於2015-11-15). 
  24. ^ Foundation, Node.js. Node v8.0.0 (Current) - Node.js. Node.js. [2023-06-27]. (原始內容存檔於2023-10-03). 
  25. ^ eval(). MDN Web Docs. [29 January 2020]. (原始內容存檔於2022-04-01). 

進一步閱讀

  • Danny Goodman: JavaScript Bible, Wiley, John & Sons, ISBN 0-7645-3342-8.
  • David Flanagan, Paula Ferguson: JavaScript: The Definitive Guide, O'Reilly & Associates, ISBN 0-596-10199-6.
  • Thomas A. Powell, Fritz Schneider: JavaScript: The Complete Reference, McGraw-Hill Companies, ISBN 0-07-219127-9.
  • Axel Rauschmayer: Speaking JavaScript: An In-Depth Guide for Programmers, 460 pages, O'Reilly Media, 25 February 2014, ISBN 978-1449365035. (free online edition頁面存檔備份,存於互聯網檔案館))
  • Emily Vander Veer: JavaScript For Dummies, 4th Edition, Wiley, ISBN 0-7645-7659-3.

外部連結