前端入门-JS学习笔记 1| 豆包MarsCode AI刷题

69 阅读22分钟

JavaScript基础知识

hello,world

我们几乎可以使用 <script> 标签将 JavaScript 程序插入到 HTML 文档的任何位置。

<!DOCTYPE HTML>
<html><body>
​
  <p>script 标签之前...</p>
​
  <script>
    alert('Hello, world!');
  </script>
​
  <p>...script 标签之后</p></body></html>

如果你有大量的 JavaScript 代码,我们可以将它放入一个单独的文件。

脚本文件可以通过 src 特性(attribute)添加到 HTML 文件中。

<script src="/path/to/script.js"></script>
这里,/path/to/script.js 是脚本文件从网站根目录开始的绝对路径。当然也可以提供当前页面的相对路径。例如,src ="script.js",就像 src="./script.js",表示当前文件夹中的 "script.js" 文件。
要附加多个脚本,请使用多个标签:
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
…

如果设置了 src 特性, script 标签内容将会被忽略。

一个单独的 <script> 标签不能同时有 src 特性和内部包裹的代码。

这将不会工作:

<script src="file.js">
  alert(1); // 此内容会被忽略,因为设定了 src
</script>
​
为了让上面的例子工作,我们可以将它分成两个 <script> 标签。
<script src="file.js"></script>
<script>
  alert(1);
</script>

代码结构

语句是执行行为(action)的语法结构和命令。

我们已经见过了 alert('Hello, world!') 这样可以用来显示消息的语句。

我们可以在代码中编写任意数量的语句。语句之间可以使用分号进行分割。

例如,我们将 “Hello World” 这条信息一分为二:

alert('Hello');
alert('World');
​
alert(3 +
1
+ 2);
//代码输出 6,因为 JavaScript 并没有在这里插入分号。显而易见的是,如果一行以加号 "+" 结尾,那么这是一个“不完整的表达式”,不需要分号。所以,这个例子得到了预期的结果。alert("Hello");
​
[1, 2].forEach(alert);
​
alert("Hello")
​
[1, 2].forEach(alert);
alert("Hello")[1, 2].forEach(alert);
​
//看起来很奇怪,对吧?在这种情况下,这样将两行代码合并到一起是不对的。我们需要在 alert 后面加一个分号,代码才能正常运行。

注释

单行注释以两个正斜杠字符 // 开始

多行注释以一个正斜杠和星号开始 “/*” 并以一个星号和正斜杠结束 “*/”**

不要在 /*...*/ 内嵌套另一个 /*...*/

变量

变量是数据的“命名存储”。我们可以使用变量来保存商品、访客和其他信息。

在 JavaScript 中创建一个变量,我们需要用到 let 关键字。

下面的语句创建(也可以称为 声明 或者 定义)了一个名称为 “message” 的变量

let message;
message = 'Hello!';
​
alert(message); // 显示变量内容
let message = 'Hello!'; // 定义变量,并且赋值
​
alert(message); // Hello!
​
let message;message = 'Hello!';message = 'World!'; // 值改变了
​
alert(message);

一个变量应该只被声明一次。

对同一个变量进行重复声明会触发 error:

let message = "This";
​
// 重复 'let' 会导致 error
let message = "That"; // SyntaxError: 'message' has already been declared
变量命名

avaScript 的变量命名有两个限制:

  1. 变量名称必须仅包含字母、数字、符号 $_
  2. 首字符必须非数字。

区分大小写;允许非英文字母,但不推荐。

常量

声明一个常数(不变)变量,可以使用 const 而非 let

const myBirthday = '18.04.1982';myBirthday = '01.01.2001'; // 错误,不能对常量重新赋值

数据类型

JavaScript 中的值都具有特定的类型。例如,字符串或数字。

在 JavaScript 中有 8 种基本的数据类型(译注:7 种原始类型和 1 种引用类型)

Number类型

umber 类型代表整数和浮点数。

数字可以有很多操作,比如,乘法 *、除法 /、加法 +、减法 - 等等。

除了常规的数字,还包括所谓的“特殊数值(“special numeric values”)”也属于这种类型:Infinity-InfinityNaN

Infinity 代表数学概念中的 无穷大 ∞。是一个比任何数字都大的特殊值。

NaN 代表一个计算错误。它是一个不正确的或者一个未定义的数学操作所得到的结果。

BigInt类型

BigInt 类型是最近被添加到 JavaScript 语言中的,用于表示任意长度的整数。

String类型

JavaScript 中的字符串必须被括在引号里。

在 JavaScript 中,有三种包含字符串的方式。

  1. 双引号:"Hello".
  2. 单引号:'Hello'.
  3. 反引号:Hello.

双引号和单引号都是“简单”引用,在 JavaScript 中两者几乎没有什么差别。

反引号是 功能扩展 引号。它们允许我们通过将变量和表达式包装在 ${…} 中,来将它们嵌入到字符串中。例如:

let name = "John";
​
// 嵌入一个变量
alert( `Hello, ${name}!` ); // Hello, John!// 嵌入一个表达式
alert( `the result is ${1 + 2}` ); // the result is 3
Boolean类型

boolean 类型仅包含两个值:truefalse

Null类型

特殊的 null 值不属于上述任何一种类型。

它构成了一个独立的类型,只包含 null 值:

相比较于其他编程语言,JavaScript 中的 null 不是一个“对不存在的 object 的引用”或者 “null 指针”。

JavaScript 中的 null 仅仅是一个代表“无”、“空”或“值未知”的特殊值。

underfined值

特殊值 undefinednull 一样自成类型。

undefined 的含义是 未被赋值

如果一个变量已被声明,但未被赋值,那么它的值就是 undefined

Object类型和Symbol类型

object 类型是一个特殊的类型。

其他所有的数据类型都被称为“原始类型”,因为它们的值只包含一个单独的内容(字符串、数字或者其他)。相反,object 则用于储存数据集合和更复杂的实体。

symbol 类型用于创建对象的唯一标识符。

交互:alert、prompt、confirm

alert

这个我们前面已经看到过了。它会显示一条信息,并等待用户按下 “OK”。

例如:

alert("Hello");

弹出的这个带有信息的小窗口被称为 模态窗。“modal” 意味着用户不能与页面的其他部分(例如点击其他按钮等)进行交互,直到他们处理完窗口。在上面示例这种情况下 —— 直到用户点击“确定”按钮。

prompt

prompt 函数接收两个参数:

result = prompt(title, [default]);

浏览器会显示一个带有文本消息的模态窗口,还有 input 框和确定/取消按钮。

  • title

    显示给用户的文本

  • default

    可选的第二个参数,指定 input 框的初始值。

语法中的方括号 [...]

上述语法中 default 周围的方括号表示该参数是可选的,不是必需的。

访问者可以在提示输入栏中输入一些内容,然后按“确定”键。然后我们在 result 中获取该文本。或者他们可以按取消键或按 Esc 键取消输入,然后我们得到 null 作为 result

prompt 将返回用户在 input 框内输入的文本,如果用户取消了输入,则返回 null

举个例子:

let age = prompt('How old are you?', 100);
​
alert(`You are ${age} years old!`); // You are 100 years old!
confirm

语法:

result = confirm(question);

confirm 函数显示一个带有 question 以及确定和取消两个按钮的模态窗口。

点击确定返回 true,点击取消返回 false

例如:

let isBoss = confirm("Are you the boss?");
​
alert( isBoss ); // 如果“确定”按钮被按下,则显示 true

类型转换

字符串转换

当我们需要一个字符串形式的值时,就会进行字符串转换。

比如,alert(value)value 转换为字符串类型,然后显示这个值。

我们也可以显式地调用 String(value) 来将 value 转换为字符串类型:

let value = true;
alert(typeof value); // booleanvalue = String(value); // 现在,值是一个字符串形式的 "true"
alert(typeof value); // string

字符串转换最明显。false 变成 "false"null 变成 "null" 等。

数字型转换

在算术函数和表达式中,会自动进行 number 类型转换。

比如,当把除法 / 用于非 number 类型:

alert( "6" / "2" ); // 3, string 类型的值被自动转换成 number 类型后进行计算

我们也可以使用 Number(value) 显式地将这个 value 转换为 number 类型。

let str = "123";
alert(typeof str); // stringlet num = Number(str); // 变成 number 类型 123alert(typeof num); // number

当我们从 string 类型源(如文本表单)中读取一个值,但期望输入一个数字时,通常需要进行显式转换。

如果该字符串不是一个有效的数字,转换的结果会是 NaN。例如:

let age = Number("an arbitrary string instead of a number");
​
alert(age); // NaN,转换失败

number 类型转换规则:

变成……
undefinedNaN
null0
true 和 false1 and 0
string去掉首尾空白字符(空格、换行符 \n、制表符 \t 等)后的纯数字字符串中含有的数字。如果剩余字符串为空,则转换结果为 0。否则,将会从剩余字符串中“读取”数字。当类型转换出现 error 时返回 NaN

例子:

alert( Number("   123   ") ); // 123
alert( Number("123z") );      // NaN(从字符串“读取”数字,读到 "z" 时出现错误)
alert( Number(true) );        // 1
alert( Number(false) );       // 0

请注意 nullundefined 在这有点不同:null 变成数字 0undefined 变成 NaN

布尔型转换

布尔(boolean)类型转换是最简单的一个。

它发生在逻辑运算中(稍后我们将进行条件判断和其他类似的东西),但是也可以通过调用 Boolean(value) 显式地进行转换。

转换规则如下:

  • 直观上为“空”的值(如 0、空字符串、nullundefinedNaN)将变为 false
  • 其他值变成 true

比如:

alert( Boolean(1) ); // true
alert( Boolean(0) ); // falsealert( Boolean("hello") ); // true
alert( Boolean("") ); // false

一些编程语言(比如 PHP)视 "0"false。但在 JavaScript 中,非空的字符串总是 true

alert( Boolean("0") ); // true
alert( Boolean(" ") ); // 空格,也是 true(任何非空字符串都是 true

基础运算符、数学计算

支持以下数学运算:

  • 加法 +,
  • 减法 -,
  • 乘法 *,
  • 除法 /,
  • 取余 %,
  • 求幂 **.

通常,加号 + 用于求和。

但是如果加号 + 被应用于字符串,它将合并(连接)各个字符串:

let s = "my" + "string";
alert(s); // mystring

注意:只要任意一个运算元是字符串,那么另一个运算元也将被转化为字符串。

举个例子:

alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"

你看,第一个运算元和第二个运算元,哪个是字符串并不重要。

下面是一个更复杂的例子:

alert(2 + 2 + '1' ); // "41",不是 "221"

在这里,运算符是按顺序工作。第一个 + 将两个数字相加,所以返回 4,然后下一个 + 将字符串 1 加入其中,所以就是 4 + '1' = '41'

alert('1' + 2 + 2); // "122",不是 "14"

这里,第一个操作数是一个字符串,所以编译器将其他两个操作数也视为了字符串。2 被与 '1' 连接到了一起,也就是像 '1' + 2 = "12" 然后 "12" + 2 = "122" 这样。

二元 + 是唯一一个以这种方式支持字符串的运算符。其他算术运算符只对数字起作用,并且总是将其运算元转换为数字。

下面是减法和除法运算的示例:

alert( 6 - '2' ); // 4,将 '2' 转换为数字
alert( '6' / '2' ); // 3,将两个运算元都转换为数字

加号 + 有两种形式。一种是上面我们刚刚讨论的二元运算符,还有一种是一元运算符。

一元运算符加号,或者说,加号 + 应用于单个值,对数字没有任何作用。但是如果运算元不是数字,加号 + 则会将其转化为数字。

例如:

// 对数字无效
let x = 1;
alert( +x ); // 1
​
let y = -2;
alert( +y ); // -2// 转化非数字
alert( +true ); // 1
alert( +"" );   // 0

它的效果和 Number(...) 相同,但是更加简短。

我们经常会有将字符串转化为数字的需求。比如,如果我们正在从 HTML 表单中取值,通常得到的都是字符串。如果我们想对它们求和,该怎么办?

二元运算符加号会把它们合并成字符串:

let apples = "2";
let oranges = "3";
​
alert( apples + oranges ); // "23",二元运算符加号合并字符串

如果我们想把它们当做数字对待,我们需要转化它们,然后再求和:

let apples = "2";
let oranges = "3";
​
// 在二元运算符加号起作用之前,所有的值都被转化为了数字
alert( +apples + +oranges ); // 5
​
// 更长的写法
// alert( Number(apples) + Number(oranges) ); // 5

从一个数学家的视角来看,大量的加号可能很奇怪。但是从一个程序员的视角,没什么好奇怪的:一元运算符加号首先起作用,它们将字符串转为数字,然后二元运算符加号对它们进行求和。

为什么一元运算符先于二元运算符作用于运算元?接下去我们将讨论到,这是由于它们拥有 更高的优先级

逗号运算符 , 是最少见最不常使用的运算符之一。有时候它会被用来写更简短的代码,因此为了能够理解代码,我们需要了解它。

逗号运算符能让我们处理多个表达式,使用 , 将它们分开。每个表达式都运行了,但是只有最后一个的结果会被返回。

举个例子:

let a = (1 + 2, 3 + 4);
​
alert( a ); // 7(3 + 4 的结果)

这里,第一个表达式 1 + 2 运行了,但是它的结果被丢弃了。随后计算 3 + 4,并且该计算结果被返回。

逗号运算符的优先级非常低

请注意逗号运算符的优先级非常低,比 = 还要低,因此上面你的例子中圆括号非常重要。

如果没有圆括号:a = 1 + 2, 3 + 4 会先执行 +,将数值相加得到 a = 3, 7,然后赋值运算符 = 执行 a = 3,然后逗号之后的数值 7 不会再执行,它被忽略掉了。相当于 (a = 1 + 2), 3 + 4

这里有一段代码,要求用户输入两个数字并显示它们的总和。

它的运行结果不正确。下面例子中的输出是 12(对于默认的 prompt 的值)。

为什么会这样?修正它。结果应该是 3

let a = prompt("First number?", 1);
let b = prompt("Second number?", 2);
​
alert(a + b); // 12

解决方案

原因是 prompt 以字符串的形式返回用户的输入。

所以变量的值分别为 "1""2"

let a = "1"; // prompt("First number?", 1);
let b = "2"; // prompt("Second number?", 2);
​
alert(a + b); // 12

我们应该做的是,在 + 之前将字符串转换为数字。例如,使用 Number() 或在 prompt 前加 +

例如,就在 prompt 之前加 +

let a = +prompt("First number?", 1);
let b = +prompt("Second number?", 2);
​
alert(a + b); // 3

或在 alert 中:

let a = prompt("First number?", 1);
let b = prompt("Second number?", 2);
​
alert(+a + +b); // 3

值的比较

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

例如:

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

对于布尔类型值,true 会被转化为 1false 转化为 0

例如:

alert( true == 1 ); // true
alert( false == 0 ); // true

一个有趣的现象

有时候,以下两种情况会同时发生:

  • 若直接比较两个值,其结果是相等的。
  • 若把两个值转为布尔值,它们可能得出完全相反的结果,即一个是 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 值,则会使用其他的类型转换规则。

普通的相等性检查 == 存在一个问题,它不能区分出 0false

alert( 0 == false ); // true

也同样无法区分空字符串和 false

alert( '' == false ); // true

这是因为在比较不同类型的值时,处于相等判断符号 == 两侧的值会先被转化为数字。空字符串和 false 也是如此,转化后它们都为数字 0。

如果我们需要区分 0false,该怎么办?

严格相等运算符 === 在进行比较时不会做任何的类型转换。

换句话说,如果 ab 属于不同的数据类型,那么 a === b 不会做任何的类型转换而立刻返回 false

让我们试试:

alert( 0 === false ); // false,因为被比较值的数据类型不同

同样的,与“不相等”符号 != 类似,“严格不相等”表示为 !==

严格相等的运算符虽然写起来稍微长一些,但是它能够很清楚地显示代码意图,降低你犯错的可能性。

null与0

通过比较 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。

另一方面,undefinednull 在相等性检查 == 中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外,不会等于任何其他的值。这就解释了为什么(2)中 null == 0 会返回 false。

undefined 不应该被与其他值进行比较:

alert( undefined > 0 ); // false (1)
alert( undefined < 0 ); // false (2)
alert( undefined == 0 ); // false (3)

为何它看起来如此厌恶 0?返回值都是 false!

原因如下:

  • (1)(2) 都返回 false 是因为 undefined 在比较中被转换为了 NaN,而 NaN 是一个特殊的数值型值,它与任何值进行比较都会返回 false
  • (3) 返回 false 是因为这是一个相等性检查,而 undefined 只与 null 相等,不会与其他值相等。

以下表达式的执行结果是?

5 > 4
"apple" > "pineapple"
"2" > "12"
undefined == null
undefined === null
null == "\n0\n"
null === +"\n0\n"

解决方案

5 > 4true
"apple" > "pineapple"false
"2" > "12"true
undefined == nulltrue
undefined === nullfalse
null == "\n0\n"false
null === +"\n0\n"false

结果的原因:

  1. 数字间比较大小,显然得 true。
  2. 按词典顺序比较,得 false。"a""p" 小。
  3. 与第 2 题同理,首位字符 "2" 大于 "1"
  4. null 只与 undefined 互等。
  5. 严格相等模式下,类型不同得 false。
  6. 与第 4 题同理,null 只与 undefined 相等。
  7. 不同类型严格不相等。

条件分支:if和‘?’

if语句

if(...) 语句计算括号里的条件表达式,如果计算结果是 true,就会执行对应的代码块。

例如:

let year = prompt('In which year was ECMAScript-2015 specification published?', '');
​
if (year == 2015) alert( 'You are right!' );

在上面这个例子中,条件是一个简单的相等性检查(year == 2015),但它还可以更复杂。

如果有多个语句要执行,我们必须将要执行的代码块封装在大括号内:

if (year == 2015) {
  alert( "That's correct!" );
  alert( "You're so smart!" );
}

有时我们需要测试一个条件的几个变体。我们可以通过使用 else if 子句实现。

例如:

let year = prompt('In which year was ECMAScript-2015 specification published?', '');
​
if (year < 2015) {
  alert( 'Too early...' );
} else if (year > 2015) {
  alert( 'Too late' );
} else {
  alert( 'Exactly!' );
}

这个运算符通过问号 ? 表示。有时它被称为三元运算符,被称为“三元”是因为该运算符中有三个操作数。实际上它是 JavaScript 中唯一一个有这么多操作数的运算符。

语法:

let result = condition ? value1 : value2;

计算条件结果,如果结果为真,则返回 value1,否则返回 value2

使用一系列问号 ? 运算符可以返回一个取决于多个条件的值。

例如:

let age = prompt('age?', 18);
​
let message = (age < 3) ? 'Hi, baby!' :
  (age < 18) ? 'Hello!' :
  (age < 100) ? 'Greetings!' :
  'What an unusual age!';
​
alert( message );

可能很难一下子看出发生了什么。但经过仔细观察,我们可以看到它只是一个普通的检查序列。

  1. 第一个问号检查 age < 3
  2. 如果为真 — 返回 'Hi, baby!'。否则,会继续执行冒号 ":" 后的表达式,检查 age < 18
  3. 如果为真 — 返回 'Hello!'。否则,会继续执行下一个冒号 ":" 后的表达式,检查 age < 100
  4. 如果为真 — 返回 'Greetings!'。否则,会继续执行最后一个冒号 ":" 后面的表达式,返回 'What an unusual age!'

这是使用 if..else 实现上面的逻辑的写法:

if (age < 3) {
  message = 'Hi, baby!';
} else if (age < 18) {
  message = 'Hello!';
} else if (age < 100) {
  message = 'Greetings!';
} else {
  message = 'What an unusual age!';
}

有时可以使用问号 ? 来代替 if 语句:

let company = prompt('Which company created JavaScript?', '');
​
(company == 'Netscape') ?
   alert('Right!') : alert('Wrong.');

根据条件 company =='Netscape',要么执行 ? 后面的第一个表达式并显示对应内容,要么执行第二个表达式并显示对应内容。

在这里我们不是把结果赋值给变量。而是根据条件执行不同的代码。

不建议这样使用问号运算符。

这种写法比 if 语句更短,对一些程序员很有吸引力。但它的可读性差。

下面是使用 if 语句实现相同功能的代码,进行下比较:

let company = prompt('Which company created JavaScript?', '');
​
if (company == 'Netscape') {
  alert('Right!');
} else {
  alert('Wrong.');
}

因为我们的眼睛垂直扫描代码。所以,跨越几行的代码块比长而水平的代码更易于理解。

问号 ? 的作用是根据条件返回其中一个值。请正确使用它。当需要执行不同的代码分支时,请使用 if

逻辑运算符

JavaScript 中有四个逻辑运算符:||(或),&&(与),!(非),??(空值合并运算符)。本文我们先介绍前三个,在下一篇文章中再详细介绍 ?? 运算符。

虽然它们被称为“逻辑”运算符,但这些运算符却可以被应用于任意类型的值,而不仅仅是布尔值。它们的结果也同样可以是任意类型。

JavaScript 的“附加”特性。

拓展的算法如下所示。

给定多个参与或运算的值:

result = value1 || value2 || value3;

或运算符 || 做了如下的事情:

  • 从左到右依次计算操作数。
  • 处理每一个操作数时,都将其转化为布尔值。如果结果是 true,就停止计算,返回这个操作数的初始值。
  • 如果所有的操作数都被计算过(也就是,转换结果都是 false),则返回最后一个操作数。

返回的值是操作数的初始形式,不会做布尔转换。

换句话说,一个或运算 || 的链,将返回第一个真值,如果不存在真值,就返回该链的最后一个值。

例如:

alert( 1 || 0 ); // 1(1 是真值)alert( null || 1 ); // 1(1 是第一个真值)
alert( null || 0 || 1 ); // 1(第一个真值)alert( undefined || null || 0 ); // 0(都是假值,返回最后一个值)

与“纯粹的、传统的、仅仅处理布尔值的或运算”相比,这个规则就引起了一些很有趣的用法。

  1. 获取变量列表或者表达式中的第一个真值。

    例如,我们有变量 firstNamelastNamenickName,都是可选的(即可以是 undefined,也可以是假值)。

    我们用或运算 || 来选择有数据的那一个,并显示出来(如果没有设置,则用 "Anonymous"):

    let firstName = "";
    let lastName = "";
    let nickName = "SuperCoder";
    ​
    alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder
    

    如果所有变量的值都为假,结果就是 "Anonymous"

  2. 短路求值(Short-circuit evaluation)。

    或运算符 || 的另一个用途是所谓的“短路求值”。

    这指的是,|| 对其参数进行处理,直到达到第一个真值,然后立即返回该值,而无需处理其他参数。

    如果操作数不仅仅是一个值,而是一个有副作用的表达式,例如变量赋值或函数调用,那么这一特性的重要性就变得显而易见了。

    在下面这个例子中,只会打印第二条信息:

    true || alert("not printed");
    false || alert("printed");
    

    在第一行中,或运算符 || 在遇到 true 时立即停止运算,所以 alert 没有运行。

    有时,人们利用这个特性,只在左侧的条件为假时才执行命令。

两个 & 符号表示 && 与运算符:

给出多个参加与运算的值:

result = value1 && value2 && value3;

与运算 && 做了如下的事:

  • 从左到右依次计算操作数。
  • 在处理每一个操作数时,都将其转化为布尔值。如果结果是 false,就停止计算,并返回这个操作数的初始值。
  • 如果所有的操作数都被计算过(例如都是真值),则返回最后一个操作数。

换句话说,与运算返回第一个假值,如果没有假值就返回最后一个值。

上面的规则和或运算很像。区别就是与运算返回第一个假值,而或运算返回第一个真值。

例如:

// 如果第一个操作数是真值,
// 与运算返回第二个操作数:
alert( 1 && 0 ); // 0
alert( 1 && 5 ); // 5// 如果第一个操作数是假值,
// 与运算将直接返回它。第二个操作数会被忽略
alert( null && 5 ); // null
alert( 0 && "no matter what" ); // 0

我们也可以在一行代码上串联多个值。查看第一个假值是如何被返回的:

alert( 1 && 2 && null && 3 ); // null

如果所有的值都是真值,最后一个值将会被返回:

alert( 1 && 2 && 3 ); // 3,最后一个值

与运算 && 在或运算 || 之前进行

与运算 && 的优先级比或运算 || 要高。

所以代码 a && b || c && d&& 表达式加了括号完全一样:(a && b) || (c && d)

不要用 ||&& 来取代 if

有时候,有人会将与运算符 && 作为“简化 if”的一种方式。

例如:

let x = 1;
​
(x > 0) && alert( 'Greater than zero!' );

&& 右边的代码只有运算抵达到那里才能被执行。也就是,当且仅当 (x > 0) 为真。

所以我们基本可以类似地得到:

let x = 1;
​
if (x > 0) alert( 'Greater than zero!' );

虽然使用 && 写出的变体看起来更短,但 if 更明显,并且往往更具可读性。因此,我们建议根据每个语法结构的用途来使用:如果我们想要 if,就使用 if;如果我们想要逻辑与,就使用 &&

感叹符号 ! 表示布尔非运算符

语法相当简单:

result = !value;

逻辑非运算符接受一个参数,并按如下运作:

  1. 将操作数转化为布尔类型:true/false
  2. 返回相反的值。

例如:

alert( !true ); // false
alert( !0 ); // true

两个非运算 !! 有时候用来将某个值转化为布尔类型:

alert( !!"non-empty string" ); // true
alert( !!null ); // false

也就是,第一个非运算将该值转化为布尔类型并取反,第二个非运算再次取反。最后我们就得到了一个任意值到布尔值的转化。

有一个略显冗长的方式也可以实现同样的效果 —— 一个内建的 Boolean 函数:

alert( Boolean("non-empty string") ); // true
alert( Boolean(null) ); // false

非运算符 ! 的优先级在所有逻辑运算符里面最高,所以它总是在 &&|| 之前执行。