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.

外部链接