脚本

一般来说,只有最简单的脚本才嵌入到HTML中。更复杂的脚本存放在单独的文件中。使用独立文件的好处是浏览器会下载它,然后将它保存到浏览器的缓存中。之后,其他页面想要相同的脚本就会从缓存中获取,而不是下载它。所以文件实际上只会下载一次。这可以节省流量,并使得页面(加载)更快。
一个单独的script标签不能同时有src特性和内部包裹的代码。如果设置了src特性,script标签内容将会被忽略。

分号

在大多数情况下,换行意味着一个分号。但是“大多数情况”并不意味着“总是”!有很多换行并不是分号的例子,例如:

alert(3 +
1
+ 2);

代码输出 6,因为 JavaScript 并没有在这里插入分号。显而易见的是,如果一行以加号 "+" 结尾,那么这是一个“不完整的表达式”,不需要分号。所以,这个例子得到了预期的结果。
但存在 JavaScript 无法确定是否真的需要自动插入分号的情况,这种情况下发生的错误是很难被找到和解决的,以下就是一个错误的例子:

alert("Hello");

[1, 2].forEach(alert);

这是能正常运行的代码,但如果删除 alert 语句后的分号:

alert("Hello")

[1, 2].forEach(alert);

如果我们运行这段代码,只有第一个 Hello 会被显示出来(并且有一个报错,你可能需要打开控制台才能看到它)。并且不会再有数字被显示出来。
这是因为,JavaScript 引擎并没有假设在方括号 [...] 前有一个分号。因此,最后一个示例中的代码被视为了单个语句。对于引擎来说,它是这样的:

alert("Hello")[1, 2].forEach(alert);

在这种情况下,这样将两行代码合并到一起是不对的。我们需要在 alert 后面加一个分号,代码才能正常运行。

注释

js不支持注释嵌套!
例如:

/*
  /* 嵌套注释 ?!? */
*/
alert( 'World' );

上面这一段段代码是会报错且无法执行的。

“严格模式”

  1. 确保 “use strict” 出现在最顶部,否则严格模式可能无法启用,只有注释可以出现在 "use strict" 的上面。
  2. 并没有类似于 "no use strict" 这样的指令可以使程序返回默认模式。一旦进入了严格模式,就没有回头路了。
  3. 使用 开发者控制台 运行代码时,请注意它默认是不启动 use strict 的。

如果想在控制台中使用“use strict”,可以尝试搭配使用 Shift+Enter 按键去输入多行代码,然后将 use strict 放在代码最顶部,就像这样:

'use strict'; <Shift+Enter 换行>
//  ...你的代码
<按下 Enter 以运行>

除此之外,现代 JavaScript 支持 “class” 和 “module” —— 高级语言结构,它们会自动启用 use strict。因此,如果我们使用它们,则无需添加 "use strict" 指令。

命名变量

  1. 区分大小写
  2. 允许非英文字母,但不推荐

数据类型 Number

除了常规的数字,还包括所谓的“特殊数值(“special numeric values”)”也属于这种类型:Infinity、-Infinity 和 NaN。
Infinity 代表数学概念中的 无穷大 ∞。是一个比任何数字都大的特殊值。
NaN 代表一个计算错误。它是一个不正确的或者一个未定义的数学操作所得到的结果,比如:

alert( "not a number" / 2 ); // NaN,这样的除法是错误的

NaN 是粘性的。任何对 NaN 的进一步数学运算都会返回 NaN:
al

ert( NaN + 1 ); // NaN
alert( 3 * NaN ); // NaN
alert( "not a number" / 2 - 1 ); // NaN

所以,如果在数学表达式中有一个 NaN,会被传播到最终结果(只有一个例外:NaN ** 0 结果为 1)。
除此之外,在 JavaScript 中,“number” 类型无法安全地表示大于$ (2^{53}-1)$(即 9007199254740991),或小于$ -(2^{53}-1)$ 的整数,这时候就得用BigInt 类型。

prompt与confirm

prompt 函数接收两个参数:

result = prompt(title, [default]);

title显示给用户的文本,default可选的第二个参数,指定 input 框的初始值。
confirm与prompt类似,函数显示一个带有 question 以及确定和取消两个按钮的模态窗口。点击确定返回 true,点击取消返回 false。

类型比较

当对不同类型的值进行比较时,JavaScript 会首先将其转化为数字(number)再判定大小。
例如:

alert( '2' > 1 ); // true,字符串 '2' 会被转化为数字 2
alert( '01' == 1 ); // true,字符串 '01' 会被转化为数字 1

对于布尔类型值,true 会被转化为 1、false 转化为 0。
有时候,以下两种情况会同时发生:
若直接比较两个值,其结果是相等的。
若把两个值转为布尔值,它们可能得出完全相反的结果,即一个是 true,一个是 false。
例如:

let a = 0;
alert( Boolean(a) ); // false

let b = "0";
alert( Boolean(b) ); // true

alert(a == b); // true!

对于 JavaScript 而言,这种现象其实挺正常的。因为 JavaScript 会把待比较的值转化为数字后再做比较(因此 "0" 变成了 0)。若只是将一个变量转化为 Boolean 值,则会使用其他的类型转换规则。

null vs 0

当使用数学式或其他比较方法 < > <= >= 时:
null/undefined 会被转化为数字:null 被转化为 0,undefined 被转化为 NaN。
通过比较 null 和 0 可得:

alert( null > 0 );  // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) true

上面的结果完全打破了你对数学的认识。在最后一行代码显示“null 大于等于 0”的情况下,前两行代码中一定会有一个是正确的,然而事实表明它们的结果都是 false。
为什么会出现这种反常结果,这是因为相等性检查 == 和普通比较符 > < >= <= 的代码逻辑是相互独立的。进行值的比较时,null 会被转化为数字,因此它被转化为了 0。这就是为什么(3)中 null >= 0 返回值是 true,(1)中 null > 0 返回值是 false。
另一方面,undefined 和 null 在相等性检查 == 中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外,不会等于任何其他的值。这就解释了为什么(2)中 null == 0 会返回 false。

?? 与 && 或 ||

|| 返回第一个真值。
?? 返回第一个已定义的值。?? 运算符的优先级与 || 相同,
出于安全原因,JavaScript 禁止将 ?? 运算符与 && 和 || 运算符一起使用,除非使用括号明确指定了优先级。
下面的代码会触发一个语法错误:

let x = 1 && 2 ?? 3; // Syntax error

这个限制无疑是值得商榷的,它被添加到语言规范中是为了避免人们从 || 切换到 ?? 时的编程错误。
可以明确地使用括号来解决这个问题:

let x = (1 && 2) ?? 3; // 正常工作了

alert(x); // 2

函数表达式 vs 函数声明

  • 函数表达式是在代码执行到达时被创建,并且仅从那一刻起可用。一旦代码执行到赋值表达式 `let sum =
    function…`的右侧,此时就会开始创建该函数,并且可以从现在开始使用(分配,调用等)。
  • 在函数声明被定义之前,它就可以被调用。这是内部算法的缘故。当 JavaScript 准备
    运行脚本时,首先会在脚本中寻找全局函数声明,并创建这些函数。我们可以将其视为“初始化阶段”。

严格模式下,当一个函数声明在一个代码块内时,它在该代码块内的任何位置都是可见的。但在代码块外不可见。
如果我们使用函数声明,则以下代码无法像预期那样工作:

let age = prompt("What is your age?", 18);

// 有条件地声明一个函数
if (age < 18) {

  function welcome() {
    alert("Hello!");
  }

} else {

  function welcome() {
    alert("Greetings!");
  }

}

// ……稍后使用
welcome(); // Error: welcome is not defined

这是因为函数声明只在它所在的代码块中可见。

下面是另一个例子:

let age = 16; // 拿 16 作为例子

if (age < 18) {
  welcome();               // \   (运行)
                           //  |
  function welcome() {     //  |
    alert("Hello!");       //  |  函数声明在声明它的代码块内任意位置都可用
  }                        //  |
                           //  |
  welcome();               // /   (运行)

} else {

  function welcome() {
    alert("Greetings!");
  }
}

// 在这里,我们在花括号外部调用函数,我们看不到它们内部的函数声明。


welcome(); // Error: welcome is not defined

如果想让 welcome 在 if 外可见,正确的做法是使用函数表达式,并将 welcome 赋值给在 if 外声明的变量,并具有正确的可见性。

下面的代码可以如愿运行:

let age = prompt("What is your age?", 18);

let welcome;

if (age < 18) {

  welcome = function() {
    alert("Hello!");
  };

} else {

  welcome = function() {
    alert("Greetings!");
  };

}

welcome(); // 现在可以了

或者我们可以使用问号运算符 ? 来进一步对代码进行简化:

let age = prompt("What is your age?", 18);

let welcome = (age < 18) ?
  function() { alert("Hello!"); } :
  function() { alert("Greetings!"); };

welcome(); // 现在可以了

箭头函数

  • 不带花括号:(...args) => expression ——
    右侧是一个表达式:函数计算表达式并返回其结果。如果只有一个参数,则可以省略括号,例如 n => n*2
  • 带花括号:(...args) => { body } —— 花括号允许我们在函数中编写多个语句,但是我们需要显式地
    return 来返回一些内容。