## name 变量名与 window.name ```js // 假设你想声明一个数字类型的 name var name = 123; // 你以为它是数字,但它其实变成了字符串 console.log(typeof name); // 输出: "string" ❌ console.log(name + 1); // 输出: "1231" (字符串拼接,而不是数学加法) ❌ ``` 在浏览器中,`var name = 123;` 实际上等同于 `window.name = 123;`。而 `window.name` 是一个内置属性,浏览器在底层会强制调用 `String(123)`,将其变为 `"123"`。 参考:[Window: name property - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/name) ## 交互:alert、prompt 和 confirm ```js alert("Hello"); result = prompt(title, [default]); result = confirm(question); // true | false ``` ## 类型转换 ### 字符串转换 我们也可以显式地调用 `String(value)` 来将 value 转换为字符串类型: ```js let value = true; alert(typeof value); // boolean value = String(value); // 现在,值是一个字符串形式的 "true" alert(typeof value); // string ``` ### 数字型转换 在算术函数和表达式中,会自动进行 number 类型转换。 ```js alert( "6" / "2" ); // 3, string 类型的值被自动转换成 number 类型后进行计算 ``` 显式转换。 ```js let str = "123"; alert(typeof str); // string let num = Number(str); // 变成 number 类型 123 alert(typeof num); // number ``` `Number` 类型的转换规则: | 值 | 变成…… | | :-------------- | :----------------------------------------------------------- | | `undefined` | `NaN` | | `null` | `0` | | `true 和 false` | `1` and `0` | | `string` | 去掉首尾空白字符(空格、换行符 `\n`、制表符 `\t` 等)后的纯数字字符串中含有的数字。如果剩余字符串为空,则转换结果为 `0`。否则,将会从剩余字符串中“读取”数字。当类型转换出现 error 时返回 `NaN`。 | ### 布尔型转换 它发生在逻辑运算中,也可以通过调用 Boolean(value) 显式地进行转换。 转换规则如下: - 直观上为“空”的值(如 `0`、空字符串、`null`、`undefined` 和 `NaN`)将变为 `false`。 - 其他值变成 `true`。 ## 基础运算符,数学运算 ### 术语:“一元运算符”,“二元运算符”,“运算元” 运算元:比如说乘法运算 `5 * 2`,有两个运算元:左运算元 `5` 和右运算元 `2`。有时候人们也称其为“参数”而不是“运算元”。 如果一个运算符对应的只有一个运算元,那么它是 **一元运算符**。比如说一元负号运算符(unary negation)`-`,它的作用是对数字进行正负转换: ```js let x = 1; x = -x; alert( x ); // -1,一元负号运算符生效 ``` 如果一个运算符拥有两个运算元,那么它是 **二元运算符**。减号还存在二元运算符形式: ```js let x = 1, y = 3; alert( y - x ); // 2,二元运算符减号做减运算 ``` ### 数学运算 支持以下数学运算: - 加法 `+`, - 减法 `-`, - 乘法 `*`, - 除法 `/`, - 取余 `%`, - 求幂 `**`. ### 数字转化,一元运算符 `+` 加号 `+` 有两种形式。一种是上面我们刚刚讨论的二元运算符,还有一种是一元运算符。 一元运算符加号,或者说,加号 `+` 应用于单个值,对数字没有任何作用。但是如果运算元不是数字,加号 `+` 则会将其转化为数字。 ```JS let apples = "2"; let oranges = "3"; alert( apples + oranges ); // "23",二元运算符加号合并字符串 ``` ```JS let apples = "2"; let oranges = "3"; // 在二元运算符加号起作用之前,所有的值都被转化为了数字 alert( +apples + +oranges ); // 5 // 更长的写法 // alert( Number(apples) + Number(oranges) ); // 5 ``` ### 逗号运算符 逗号运算符能让我们处理多个表达式,使用 , 将它们分开。每个表达式都运行了,但是只有最后一个的结果会被返回。 ```js let a = (1 + 2, 3 + 4); alert( a ); // 7(3 + 4 的结果) // 一行上有三个运算符 for (a = 1, b = 3, c = a * b; a < 10; a++) { ... } ``` ## 值的比较 ### 字符串比较 在比较字符串的大小时,JavaScript 会使用 **Unicode 编码顺序** 顺序进行判定。 换言之,字符串是按字符(母)逐个进行比较的。 例如: ```javascript alert( 'Z' > 'A' ); // true alert( 'Glow' > 'Glee' ); // true alert( 'Bee' > 'Be' ); // true ``` 字符串的比较算法非常简单: 1. 首先比较两个字符串的首位字符大小。 2. 如果一方字符较大(或较小),则该字符串大于(或小于)另一个字符串。算法结束。 3. 否则,如果两个字符串的首位字符相等,则继续取出两个字符串各自的后一位字符进行比较。 4. 重复上述步骤进行比较,直到比较完成某字符串的所有字符为止。 5. 如果两个字符串的字符同时用完,那么则判定它们相等,否则未结束(还有未比较的字符)的字符串更大。 ### 不同类型间的比较 当对不同类型的值进行比较时,除了 `null` 和 `undefined`,JavaScript 会首先将其转化为数字(number)再判定大小。 ```js alert( '2' > 1 ); // true,字符串 '2' 会被转化为数字 2 alert( '01' == 1 ); // true,字符串 '01' 会被转化为数字 1 alert( true == 1 ); // true alert( false == 0 ); // true ``` ### 严格相等 普通的相等性检查 `==` 存在一个问题,它不能区分出 `0` 和 `false`: ```js alert( 0 == false ); // true ``` 也同样无法区分空字符串和 `false`: ```javascript alert( '' == false ); // true ``` **严格相等运算符 `===` 在进行比较时不会做任何的类型转换。** ```js alert( 0 === false ); // false,因为被比较值的数据类型不同 ``` ### null 与 undefined 比较 它们不相等,因为它们属于不同的类型。 ```js alert( null === undefined ); // false ``` JavaScript 存在一个特殊的规则,会判定它们相等。 ```js alert( null == undefined ); // true ``` `undefined` 和 `null` 在相等性检查 `==` 中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外,不会等于任何其他的值。 ```js alert( null > 0 ); // (1) false, null => 0 alert( null == 0 ); // (2) false, null => null alert( null >= 0 ); // (3) true, null => 0 ``` ### undefined 与其他值比较 `undefined` 不应该被与其他值进行比较: ```javascript alert( undefined > 0 ); // false alert( undefined < 0 ); // false alert( undefined == 0 ); // false ``` ## 条件分支:if 和 '?' ### 布尔转换 - 数字 `0`、空字符串 `""`、`null`、`undefined` 和 `NaN` 都会被转换成 `false`。因为它们被称为“假值(falsy)”。 - 其他值被转换为 `true`,所以它们被称为“真值(truthy)”。 ## 逻辑运算符 JavaScript 中有四个逻辑运算符:`||`(或),`&&`(与),`!`(非),`??`(空值合并运算符)。 ### 或运算 或运算:寻找**第一个真值**或**最后一个假值**。 一个或运算 `||` 的链,将返回第一个真值,如果不存在真值,就返回该链的最后一个值。 ```js alert( 1 || 0 ); // 1(1 是真值) alert( null || 1 ); // 1(1 是第一个真值) alert( null || 0 || 1 ); // 1(第一个真值) alert( undefined || null || 0 ); // 0(都是假值,返回最后一个值) ``` 有趣的用法: ```js let firstName = ""; let lastName = ""; let nickName = "SuperCoder"; alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder true || alert("not printed"); false || alert("printed"); ``` ### 与运算 与运算:寻找第一个假值或最后一个真值 ```js // 如果第一个操作数是真值, // 与运算返回第二个操作数: 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)`。 ### !(非) 两个非运算 `!!` 有时候用来将某个值转化为布尔类型: ```javascript alert( !!"non-empty string" ); // true alert( !!null ); // false alert( Boolean("non-empty string") ); // true alert( Boolean(null) ); // false ``` 也就是,第一个非运算将该值**转化为布尔类型并取反**,第二个非运算**再次取反**。最后我们就得到了一个任意值到布尔值的转化。 非运算符 `!` 的优先级在所有逻辑运算符里面最高,所以它总是在 `&&` 和 `||` 之前执行。 ## 空值合并运算符 '??' 由于它对待 `null` 和 `undefined` 的方式类似,所以在本文中我们将使用一个特殊的术语对其进行表示。为简洁起见,当一个值既不是 `null` 也不是 `undefined` 时,我们将其称为“已定义的(defined)”。 `a ?? b` 的结果是: - 如果 `a` 是已定义的,则结果为 `a`, - 如果 `a` 不是已定义的,则结果为 `b`。 我们可以使用我们已知的运算符重写 `result = a ?? b`,像这样: ```javascript result = (a !== null && a !== undefined) ? a : b; ``` ### 与 `||` 比较 它们之间重要的区别是: - `||` 返回第一个 **真** 值。 - `??` 返回第一个 **已定义的** 值。 ```js let height = 0; alert(height || 100); // 100 alert(height ?? 100); // 0 ``` ## "switch" 语句 ### 类型很关键 强调一下,这里的相等是严格相等。被比较的值必须是相同的类型才能进行匹配。 比如,我们来看下面的代码: ```js let arg = prompt("Enter a value?") switch (arg) { case '0': case '1': alert( 'One or zero' ); break; case '2': alert( 'Two' ); break; case 3: alert( 'Never executes!' ); break; default: alert( 'An unknown value' ) } ``` 1. 在 `prompt` 对话框输入 `0`、`1`,第一个 `alert` 弹出。 2. 输入 `2`,第二个 `alert` 弹出。 3. 但是输入 `3`,因为 `prompt` 的结果是字符串类型的 `"3"`,不严格相等 `===` 于数字类型的 `3`,所以 `case 3` 不会执行!因此 `case 3` 部分是一段无效代码。所以会执行 `default` 分支。 ## 函数 ```js function name(parameter1, parameter2, ... parameterN) { ...body... } name(); ``` ### 参数默认值 ```js function showMessage(from, text = "no text given") { alert( from + ": " + text ); } showMessage("Ann"); // Ann: no text given ``` ```js function showMessage(from, text = anotherFunction()) { // anotherFunction() 仅在没有给定 text 时执行 // 其运行结果将成为 text 的值 } ``` ### 后备的默认参数 有些时候,将参数默认值的设置放在函数执行(相较更后期)而不是函数声明时,也行得通。 我们可以通过将参数与 `undefined` 进行比较,来检查该参数是否在函数执行期间被传递进来: ```js function showCount(count) { // 如果 count 为 undefined 或 null,则提示 "unknown" alert(count ?? "unknown"); } showCount(0); // 0 showCount(null); // unknown showCount(); // unknown ``` ### 返回值 **空值的** `return` **或没有** `return` **的函数返回值为** `undefined`。 ```js function doNothing() { /* 没有代码 */ } function doNothing() { return; // return undefined; } alert( doNothing() === undefined ); // true ``` ## 函数表达式 在 JavaScript 中,函数不是“神奇的语言结构”,而是一种特殊的值。 函数声明语法: ```js function sayHi() { alert( "Hello" ); } alert(sayHi); // 显示函数代码 ``` 函数表达式语法: ```js let sayHi = function() { alert( "Hello" ); }; alert(sayHi); // 显示函数代码 ``` ### 回调函数 ```js function ask(question, yes, no) { if (confirm(question)) yes() else no(); } function showOk() { alert( "You agreed." ); } function showCancel() { alert( "You canceled the execution." ); } // 用法:函数 showOk 和 showCancel 被作为参数传入到 ask ask("Do you agree?", showOk, showCancel); ``` ### 与函数声明区别 函数表达式是在代码执行到达时被创建,并且仅从那一刻起可用。 函数声明则不同,在函数声明被定义之前,它就可以被调用。这是内部算法的缘故。当 JavaScript 准备 运行脚本时,首先会在脚本中寻找全局函数声明,并创建这些函数。 ## 箭头函数 ```js let func = (arg1, arg2, ..., argN) => expression; let func = function(arg1, arg2, ..., argN) { return expression; }; ```