目录
2.对象和数组初始化器Object and Array Initializers
3.函数定义表达式Function Definition Expressions
4.属性访问表达式Property Access Expressions
条件属性访问Conditional Property Access
6.对象创建表达式Object Creation Expressions
2.操作数和结果类型Operand and Result Type
1.JavaScript 运算符通常会根据需要转换其操作数的类型
5.运算符关联性Operator Associativity
8 相等操作符Equality and Inequality Operators
表达式是 JavaScript 的一个短语phrase,可以通过计算来产生一个值be evaluated to produce a value。
- 嵌入在程序中的常量constant embedded literally in是一种非常简单的表达式。
- 变量名variable name也是一个简单的表达式,它计算为该变量分配的任何值whatever value has been assigned to that variable。
复杂表达式是从更简单的表达式构建的。
- 数组访问表达式array access expression由一个计算结果为数组的表达式后跟一个左方括号、一个计算结果为整数的表达式和一个右方括号组成。这个新的、更复杂的表达式计算出存储在指定数组的指定索引处的值。
- 函数调用表达式function invocation expression由一个计算结果为函数对象function object的表达式和零个或多个用作函数参数的附加表达式组成。
从更简单的表达式构建复杂表达式的最常见方法是使用运算符。
- 运算符以某种方式组合其操作数(通常是其中的两个)的值并计算为新值。
- 乘法运算符 * 是一个简单的例子。表达式 x * y 计算为表达式 x 和 y 的值的乘积。
- 为简单起见,我们有时会说运算符返回一个值,而不是“评估”一个值an operator returns a value rather than “evaluates to” a value
1.基本表达式Primary Expressions
基本表达式:最简单的表达式,不会包含任何更简单的表达式
- 常量constant或字面量值literal values
字面量是直接嵌入到程序中的常量值 Literals are constant values that are embedded directly in your program
1.23 // A number literal
"hello" // A string literal
/pattern/ // A regular expression literal
- 某些语言关键字
JavaScript 的一些保留字reserved words是基本表达式
true // Evalutes to the boolean true value 评估为布尔型真值
false // Evaluates to the boolean false value 估算为布尔假值
null // Evaluates to the null value
this // Evaluates to the "current" object **评估为“当前”对象**
this不是一个常数——它在程序的不同位置计算为不同的值。、
this 关键字用于面向对象的编程中。
在方法的主体中Within the body of a method, this 计算为调用该方法的对象the object on which the method was invoked。
- 变量引用variable references
对全局对象的变量、常量或属性的引用a reference to a variable, constant, or property of the global object
i // Evaluates to the value of the variable i.
sum // Evaluates to the value of the variable sum.
undefined // **The value of the "undefined" property of the global object 全局对象的“未定义”属性的值**
当任何标识符identifier在程序中单独出现时,JavaScript 假定它是全局对象的变量或常量或属性并查找其值。 如果不存在具有该名称的变量,则尝试评估不存在的变量会引发 ReferenceError
2.对象和数组初始化器Object and Array Initializers
对象和数组初始化器Object and array initializers是其值为新创建对象或数组whose value is a newly created object or array的表达式。
这些初始化式表达式有时被称为对象字面量和数组字面量object literals and array literals。然而,与真正的字面量不同,它们不是基本表达式,因为它们包含许多指定属性和元素值的子表达式subexpressions that specify property and element values。
1.数组初始化器
数组初始化式是用方括号括起来的逗号分隔的表达式列表。数组初始化器的值是一个新创建的数组。
[] // An empty array: no expressions inside brackets means no elements 空数组:方括号内没有表达式意味着没有元素
[1+2,3+4] // A 2-element array. First element is 3, second is 7
数组初始化器中的元素表达式element expressions本身可以是数组初始化器,这意味着这些表达式可以创建嵌套数组nested arrays
let matrix = [[1,2,3], [4,5,6], [7,8,9]];
只要在逗号之间省略一个值,就可以在数组字面量中包含未定义的元素Undefined elements。
例如,下面的数组包含5个元素,其中包括3个未定义的元素
let sparseArray = [1,,,,5]; //**4个逗号,里面省略了3个值**
在数组初始化器中的最后一个表达式之后允许使用单个尾随逗号,这样并不会创建未定义的元素。 数组访问表达式对于(最后一个表达式的索引+1)的索引求值的结果是undefined。
2.对象初始化器
对象初始化器表达式类似于数组初始化器表达式,但方括号被花括号取代,并且每个子表达式都以属性名和冒号作为前缀
let p = { x: 2.3, y: -1.2 }; // An object with 2 properties
let q = {}; // An empty object with no properties
q.x = 2.3; q.y = -1.2; // Now q has the same properties as p
在ES6中,对象字面量拥有更丰富的语法特性
对象字面量可以嵌套
let rectangle = {
upperLeft: { x: 2, y: 2 },
lowerRight: { x: 4, y: 5 }
};
3.函数定义表达式Function Definition Expressions
函数定义表达式定义了一个JavaScript函数,该表达式的值就是新定义的函数。
在某种意义上,函数定义表达式是一个**“函数字面量”“function literal”**,就像对象初始化器是一个“对象字面量”“object literal”一样。
函数定义表达式通常由关键字 function 后跟括号中的零个或多个标识符(参数名称)的逗号分隔列表和花括号中的 JavaScript 代码块(函数体)组成
// This function returns the square of the value passed to it.
let square = function(x) { return x * x; };
函数定义表达式也可以包含函数名a name for the function。
函数也可以使用函数语句function statement而不是函数表达式来定义。
在ES6及以后版本中,函数表达式可以使用紧凑的新“箭头函数”语法a compact new “arrow function” syntax。
4.属性访问表达式Property Access Expressions
属性访问表达式的计算结果为对象属性object property或数组元素array element的值。
JavaScript定义了两种属性访问语法
expression . identifier
expression [ expression ]
第一种: 是一个表达式,后跟一个句点和一个标识符。 表达式指定对象,标识符指定所需属性的名称。
第二种: 在第一个表达式(对象或数组)之后使用方括号中的另一个表达式(指定所需属性的名称或所需数组元素的索引)
let o = {x: 1, y: {z: 3}}; // An example object
let a = [o, 4, [5, 6]]; // An example array that contains the object
o.x // => 1: property x of expression o
o.y.z // => 3: **property z of expression o.y**
o["x"] // => 1: property x of object o
a[1] // => 4: element at index 1 of expression a
a[2]["1"] // => 6: element at index 1 of expression a[2]
a[0].x // => 1: property x of expression a[0]
- 对于任一类型的属性访问表达式,. 或者方括号之前的表达式将首先被评估。
- 如果第一个表达式计算出的值为 null 或未定义undefined,则表达式将引发 TypeError,因为null 或未定义undefined两个值是不能具有属性的
- 如果对象表达式后跟一个点和一个标识符,则该标识符命名的属性的值成为表达式的整体值。
- 如果对象表达式后跟方括号中的另一个表达式,则计算第二个表达式并将其转换为字符串。表达式的整体值就是由该字符串命名的属性的值。
- 在任何一种情况下,如果命名属性不存在,则属性访问表达式的值是未定义的undefined。
第一种访问表达式语法较简单的一种,但如果属性名称包含空格或标点符号,或者是数字(对于数组),则必须使用第二种方括号表示法。
当属性名称不是静态而是本身是计算的结果not static but is itself the result of a computation时,也使用方括号。
条件属性访问Conditional Property Access
ES2020 新增两种属性访问表达式
expression ?. identifier
expression ?.[ expression ]
在 JavaScript 中,值 null 和 undefined 是仅有的两个没有属性的值。 在常规属性访问表达式中使用 . 或 [],如果左侧的表达式计算结果为 null 或未定义,则会收到 TypeError。
您可以使用 ?.和 ?.[ ] 语法来防止这种类型的错误。
- 考虑表达式 a?.b。 如果 a 为 null 或未定义 undefined,则表达式的计算结果为未定义 undefined,而不尝试访问属性 b。
- 如果 a 是其他值,则 a?.b 将评估为 a.b 将评估的任何值
- (如果 a 没有名为 b 的属性,则该值将再次计算为未定义undefined)。
这种形式的属性访问表达式有时称为**“可选链接”“optional chaining”,因为它也适用于更长的“链接”属性访问表达式**longer “chained” property access expressions
let a = { b: null };
a.b?.c.d // => undefined
- a 是一个对象,所以 a.b 是一个有效的属性访问表达式。
- 但是 a.b 的值为 null,所以 a.b.c 会抛出 TypeError。
- 通过使用 ?. 代替 .我们避免了 TypeError,并且 a.b?.c 评估为未定义undefined。
- 这意味着 (a.b?.c).d 将抛出 TypeError,因为该表达式试图访问值未定义undefined.的属性。
- 但是a.b?.c.d(没有括号)只是简单地计算为 undefined 并且不会抛出错误。
这是因为使用 ?. 访问属性是捷径“short-circuiting”:如果?.左边的子表达式计算结果为 null 或未定义undefined,则整个表达式立即计算为未定义undefined,无需任何进一步的属性访问尝试
当然,如果 a.b 是一个对象,并且该对象没有名为 c 的属性,那么 a.b?.c.d 将再次抛出 TypeError,我们将要使用另一个条件属性访问
这里没法使用捷径,因为?.左边并非 null 或未定义undefined,而是一个对象
let a = { b: {} };
a.b?.c?.d // => undefined
条件属性访问也可以使用 ?.[] 代替 []。
在表达式 a?.[b][c] 中,如果 a 的值为 null 或 undefined,则整个表达式立即计算为 undefined,并且子表达式 b 和 c 甚至都不会被计算。
如果这些表达式中的任何一个具有副作用side effects,则如果a未定义a is not defined 则不会发生副作用
let a; // Oops, we forgot to initialize this variable!
let index = 0;
try {
a[index++]; // Throws TypeError 因为a undefined
} catch(e) {
index // => 1: increment occurs before TypeError is thrown
}
a?.[index++] // => undefined: because a is undefined
index // => 1: not incremented because ?.[] short- circuits 所以方括号里的值都不会被计算
a[index++] // !TypeError: can't index undefined.
5.调用表达式Invocation Expressions
调用表达式是 JavaScript 用于调用calling(或执行executing)函数或方法method的语法。
它以一个函数表达式function expression开始,该表达式标识要调用的函数。
函数表达式后跟一个左括号、一个以逗号分隔的零个或多个参数表达式argument expressions列表和一个右括号。
f(0) // f is the function expression; 0 is the argument expression.
Math.max(x,y,z) // Math.max is the function; x, y, and z are the arguments.
a.sort() // a.sort is the function; there are no arguments.
1.函数调用
当对调用表达式求值时,首先对函数表达式求值,然后对参数表达式求值以产生参数值argument value列表。
如果函数表达式的值不是函数,则抛出TypeError。
接下来,按顺序将实参值argument values赋给函数定义时指定的形参名称parameter names
然后执行函数体the body of the function is executed。
如果函数使用return语句返回一个值,那么该值就成为调用表达式的值。否则,调用表达式的值未定义undefined。
2.方法调用
每个调用表达式都包含一对括号和开括号前的表达式。
如果该表达式是属性访问property access expression表达式,则调用称为方法调用method invocation。
在方法调用中,作为属性访问主体的对象或数组在执行函数体时成为 this 关键字的值。
这实现了一种面向对象的编程范式,其中函数(当以这种方式使用时,我们称之为**“方法”**)对它们所属的对象进行操作。
functions (which we call “methods” when used this way) operate on the object of which they are part
3.条件调用Conditional Invocation
在 ES2020 中,您还可以使用 ?.() 而不是 () 来调用函数。
通常,当您调用函数时,如果括号左侧的表达式为 null 或 undefined 或任何其他非函数,则会引发 TypeError。
使用新的 ?.() 调用语法,如果 ? 左侧的表达式计算结果为 null 或 undefined,那么整个调用表达式计算结果为 undefined,并且不会引发异常。
数组对象Array objects有一个sort()方法,可以有选择的向该方法传递一个函数参数,该参数定义数组元素所需的排序顺序defines the desired sorting order for the array elements。
在ES2020之前,如果你想编写一个像sort()这样接受可选**函数参数function argument(这个参数是函数)**的方法,你通常会在调用if语句体之前使用if语句来检查函数参数是否被定义use an if statement to check that the function argument was defined before invoking it in the body of the if
function square(x, log) { // The second argument is an optional function **第二个参数是一个可选函数**
if (log) { // If the optional function is passed
log(x); // Invoke it
}
return x * x; // Return the square of the argument
}
但是,使用 ES2020 的这种条件调用语法,您可以简单地使用 ?.() 编写函数调用,因为只有在实际有要调用的值时才会发生调用invocation will only happen if there is actually a value to be invoked
function square(x, log) { // The second argument is an optional function
log?.(x); // Call the function if there is one **如果有函数就调用该函数**
return x * x; // Return the square of the argument
}
?.() 仅检查左侧是否为空null或未定义undefined。 它不验证该值实际上是否是一个函数。
因此,如果你传递了两个数字给它,这个例子中的square()函数仍然会抛出异常。因为条件调用不负责这种函数级调用的失误。
类似于条件属性访问表达式,使用 ?.() 的函数调用是走捷径的:如果 ? .左侧的值 为 null 或未定义undefined,则不计算括号内的任何参数表达式
let f = null, x = 0;
try {
f(x++); // Throws TypeError because f is null
} catch(e) {
x // => 1: x gets incremented before the exception is thrown **正常的函数调用会先计算参数表达式再计算整体值**
}
f?.(x++) // => undefined: f is null, but no exception thrown
x // => 1: increment is skipped because of short- circuiting 走捷径了
带有 ?.() 的条件调用表达式对于方法method和函数一样有效。
但是因为方法调用还涉及属性访问,所以需要区分
o.m() // **Regular property access, regular invocation** 常规属性访问,常规调用
o?.m() // Conditional property access, regular invocation 条件属性访问,常规调用
o.m?.() // Regular property access, conditional invocation 常规属性访问,条件调用
在第一个表达式中,o 必须是具有属性 m 的对象,并且该属性的值必须是函数。
在第二个表达式中,如果 o 为 null 或未定义,则表达式的计算结果为未定义。 但是如果 o 有任何其他值,那么它必须有一个属性 m,其值是一个函数。
在第三个表达式中,o 不能为 null 或未定义。 如果它没有属性 m,或者如果该属性的值为 null,则整个表达式的计算结果为 undefined。
注意一下?的位置在哪里
6.对象创建表达式Object Creation Expressions
对象创建表达式创建一个新对象并**调用一个函数(称为构造函数)**来初始化该对象的属性invokes a function (called a constructor) to initialize the properties of that object。
对象创建表达式类似于调用表达式,只是它们以关键字 new
为前缀
new Object()
new Point(2,3)
如果在对象创建表达式中没有将参数传递给构造函数,则可以省略空括号对
new Object
new Date
对象创建表达式的值是新创建的对象。
7.运算符概述Operator Overview
运算符用于 JavaScript 的算术表达式arithmetic expressions、比较表达式comparison expressions、逻辑表达式logical expressions、赋值表达式等assignment expressions。
JavaScript operators运算符https://www.notion.so/ea10da9714ae4ac8bfd16bde337b2801
注意:
- 表格按运算符优先级precedence组织。首先列出的运算符的优先级高于最后列出的运算符。由水平线分隔的运算符具有不同的优先级。
- 标记为 A 的列给出了运算符的关联性associativity,可以是 L(从左到右)或 R(从右到左)
- 列 N 指定操作数的数量the number of operands
- 标记为 Types 的列列出了操作数的预期类型the expected types of the operands和(在 → 符号之后)运算符的结果类型the result type for the operator。
1.操作数数量Number of Operands
运算符可以根据它们期望的操作数数量(arity)进行分类。
- 大多数 JavaScript 运算符,如 * 乘法运算符,都是将两个表达式组合成一个更复杂的表达式的二元运算符binary operators。 也就是说,他们期望两个操作数。
- JavaScript 还支持许多一元运算符unary operators,它们将单个表达式转换为单个更复杂的表达式。 表达式 -x 中的 - 运算符是一元运算符,对操作数 x 执行取反运算。
- 最后,JavaScript 支持一个三元运算符ternary operator,即条件运算符 ?:,它将三个表达式组合成一个表达式。
2.操作数和结果类型Operand and Result Type
大多数运算符都希望它们的操作数是特定类型的,并且大多数运算符返回(或计算为)特定类型的值。表
中的 Types 列指定了运算符的操作数类型(箭头之前)和结果类型(箭头之后)。
1.JavaScript 运算符通常会根据需要转换其操作数的类型
- 乘法运算符 * 需要数字操作数,但表达式 "3" * "5" 是合法的,因为 JavaScript 可以将操作数转换为数字。这个表达式的值当然是数字 15,而不是字符串“15”。
- 每个 JavaScript 值要么是“真”,要么是“假”“truthy” or “falsy”,因此期望布尔操作数的运算符可以处理任何类型的操作数operators that expect boolean operands will work with an operand of any type。
2.某些运算符的行为会因所使用的操作数的类型而异
-
- 运算符添加add数字操作数但连接concatenates字符串操作数。
- 比较运算符(如 <)根据操作数的类型按数字或字母顺序numerical or alphabetical order执行比较。
各个运算符的描述解释了它们的类型依赖性type-dependencies并指定了它们执行的类型转换type conversions。
表 4中列出的赋值运算符和其他一些运算符需要一个 lval
类型的操作数。
lvalue是一个历史术语,意思是“可以合法地出现在赋值表达式左侧的表达式”。“an expression that can legally appear on the left side of an assignment expression.”
在 JavaScript 中,变量、对象的属性和数组的元素都是lvalue。
3.操作符副作用Operator Side Effects
对一个简单表达式(如2 * 3)求值不会影响程序的状态,并且程序未来执行的任何计算都不会受到该求值的影响。
但是,有些表达式有副作用,它们的计算可能会影响将来的计算结果。
- 赋值操作符是最明显的例子:如果将一个值赋给一个变量或属性,则会改变使用该变量或属性的任何表达式的值。
- ++和- -递增和递减操作符类似,因为它们执行隐式赋值implicit assignment。
- 删除操作符
delete
也有副作用:删除属性就像(但不等于)给属性赋值undefined。 - 除此以外,其他JavaScript操作符都没有副作用
- 但是如果函数体或构造函数体function or constructor body中使用的任何操作符有副作用,那么函数调用和对象创建表达式就会有副作用
4.运算符优先级Operator Precedence
表 中列出的运算符按优先级从高到低的顺序排列,水平线分隔具有相同优先级的运算符组。
运算符优先级控制执行操作的顺序。 具有较高优先级(靠近表格顶部)的运算符在具有较低优先级(靠近底部)的运算符之前执行
w = x + y*z;
//乘法运算符 * 的优先级高于加法运算符 +,因此在加法之前执行乘法运算。
//赋值运算符 = 的优先级最低,因此在右侧的所有操作都完成后进行赋值。
可以通过显式使用括号来覆盖运算符优先级
w = (x + y)*z;
属性访问和调用表达式的优先级高于表中列出的任何运算符
// my is an object with a property named functions whose value is an
// array of functions. We invoke function number x, passing it argument
// y, and then we ask for the type of the value returned.
//**My是一个对象,其属性名为functions,其值是一个函数数组。我们调用函数x,向它传递参数y,然后询问返回值的类型。**
typeof my.functions[x](y)
尽管typeof是优先级最高的操作符之一,但typeof操作是在属性访问、数组索引和函数调用的结果上执行的,所有这些操作的优先级都高于操作符。
在实践中,如果您完全不确定操作符的优先级,最简单的方法就是使用圆括号来显式地表示求值顺序。
一些最明显的规则:乘法和除法在加法和减法之前执行,赋值的优先级很低,几乎总是最后执行。
5.运算符关联性Operator Associativity
运算符的关联性指定执行相同优先级的操作的顺序。
从左到右的关联性意味着操作是从左到右执行的。 例如,减法运算符具有从左到右的关联性
w = x - y - z;
w = ((x - y) - z);
//以上两式相等
y = a ** b ** c;
x = ~-y;
w = x = y = z;
q = a?b:c?d:e?f:g;
y = (a ** (b ** c));
x = ~(-y);
w = (x = (y = z));
q = a?b:(c?d:(e?f:g));
//以上两式等价
//因为**取幂、一元、赋值和三元条件运算符具有从右到左的结合性**
8 相等操作符Equality and Inequality Operators
关系运算符relational operators:测试两个值之间的关系(例如“等于”、“小于”或“属性”),并根据该关系是否存在返回真或假。
关系表达式Relational expressions的计算结果总是一个布尔值,该值通常用于控制 if、while 和 for 语句中的程序执行流程
==和===操作符使用两种不同的一致性定义definitions of sameness来检查两个值是否相同。
两个操作符都接受任何类型的操作数,如果它们的操作数相同,则都返回true,如果它们不同,则都返回false。
===操作符被称为严格相等操作符strict equality operator(有时也称为恒等操作符identity operator),它使用严格的一致性定义来检查两个操作数是否“相同”“identical”。
==操作符被称为相等操作符equality operator;它使用允许类型转换type conversions的更宽松的一致性定义来检查两个操作数是否“相等”。
JavaScript使用=操作符给变量或属性赋值。
=操作符的左操作数应该是lvalue:一个变量或对象属性(或数组元素)。其右侧操作数是任意类型的任意值。
赋值表达式assignment expression的值是右边操作数的值。
作为一个副作用,=操作符将右边的值赋给左边的变量或属性,以便以后对该变量或属性的引用计算出该值。
赋值表达式的值有可能被用作更大表达式的一部分。
(a = b) === 0
=的优先级very low precedence很低,当赋值的值要在更大的表达式中使用时,括号通常是必要的。
!= 和 !== 运算符测试与 == 和 === 运算符完全相反的情况。
如果两个值根据 == 相等,!= 不等式运算符返回 false,否则返回 true。
如果两个值严格相等,!== 运算符返回 false,否则返回 true。
JavaScript 对象是通过引用而不是值进行比较的。
- 一个对象等于它自己,但不等于任何其他对象An object is equal to itself, but not to any other object
- 如果两个不同的对象具有相同数量的属性,具有相同的名称和值,它们仍然不相等。
- 同样,具有相同元素且顺序相同的两个数组彼此不相等。
1.严格相等STRICT EQUALITY
严格相等操作符===计算其操作数,然后按如下方式比较两个值,不进行类型转换:
- 如果两个值具有不同的类型,则它们不相等。
- 如果两个值都为空或两个值都未定义,则它们是相等的。
- 如果两个值都是布尔值true或都是布尔值false,它们是相等的。
- 如果一个或两个值都是NaN,则它们不相等。(这是令人惊讶的,但NaN值永远不等于任何其他值,包括它自己!要检查一个值x是否为NaN,可以使用x !== x,或者全局的isNaN()函数。)
- 如果两个值都是数字且值相同,则它们是相等的。如果一个值是0,另一个值是-0,它们也是相等的。
- 如果两个值都是字符串,并且在相同的位置包含完全相同的16位值(16-bit value),它们是相等的。如果字符串的长度或内容不同,则它们不相等。两个字符串可能具有相同的含义和相同的视觉外观,但仍然使用不同的16位值序列进行编码encoded using different sequences of 16-bit values。JavaScript不执行Unicode规范化Unicode normalization,像这样的一对字符串使用===或==操作符东都不会被认为是相等的。
- 如果两个值都指向同一个对象、数组或函数,则它们是相等的。如果它们引用不同的对象,它们就不相等,即使两个对象具有相同的属性。
10.逻辑表达式Logical Expressions
逻辑操作符logical operators &&、||和!执行布尔代数,通常与关系运算符一起使用,将两个关系表达式组合成一个更复杂的表达式。
1.Logical AND (&&)
分为3个层次来理解
1.执行布尔 AND 运算
当与布尔操作数一起使用时,&& 对两个值执行布尔 AND 运算:当且仅当它的第一个操作数和它的第二个操作数都为真时,它才返回真。 如果这些操作数中的一个或两个为假,则返回假。
&& 通常用作连接两个关系表达式的连词
x === 0 && y === 0 // true if, and only if, x and y are both 0
关系表达式总是计算为真或假,因此当这样使用时,&& 运算符本身返回真或假。
关系运算符的优先级高于 &&(和 ||),因此可以安全地编写此类表达式而无需括号。
2.作为真值和假值的布尔 AND 运算符
- && 不要求它的操作数是布尔值。
- 所有 JavaScript 值要么是“真实的truthy”,要么是“虚假的falsy”。
- 在 JavaScript 中,任何需要布尔值的表达式或语句都可以接受真值或假值
如果两个操作数都是真值,则运算符返回一个真值returns a truthy value。
否则,一个或两个操作数必然是假的,并且运算符返回一个假值returns a falsy value。
3.捷径
该运算符首先计算其第一个操作数,即其左侧的表达式。
如果左边的值是假的falsy,那么整个表达式的值也一定是假的,所以 && 只是简单地返回左边的值,甚至不计算右边的表达式。
另一方面,如果左边的值是真值,那么表达式的整体值取决于右边的值。如果右边的值是真实的,那么整体的价值一定是真实的, 如果右边的值是假的,那么整体的价值一定是假的。
所以当左边的值是真的时,&& 运算符计算并返回右边的值
let o = {x: 1};
let p = null;
o && o.x // => 1: o is truthy, so return value of o.x **返回的不是truthy或者falsy,返回的就是本来的值**
p && p.x // => null: p is falsy, so return it and don't evaluate p
以下两行代码等效
if (a === b) stop(); // Invoke stop() only if a === b
(a === b) && stop(); // This does the same thing
通常,当您在 && 的右侧编写带有副作用(赋值、递增、递减或函数调用)的表达式时,您必须小心。
这些副作用是否发生取决于左侧的值。
2.Logical OR (||)
|| 运算符对其两个操作数执行布尔或运算。
如果一个或两个操作数为真truthy,则返回一个真值。 如果两个操作数都是假的falsy,则返回一个假值。
与 && 运算符一样,具有更复杂的行为。
- 它首先计算它的第一个操作数,即它左边的表达式。
- 如果第一个操作数的值是真值,它将短路并返回该真值,而无需计算右侧的表达式。
- 另一方面,如果第一个操作数的值是假的,那么 || 计算其第二个操作数并返回该表达式的值。
- 与 && 运算符一样,您应该避免包含副作用的右侧操作数,除非您有意使用右侧表达式可能不被计算的事实。
此运算符的惯用用法是在一组备选方案中选择第一个真值select the first truthy value
// If maxWidth is truthy, use that. Otherwise, look for a value in
// the preferences object. If that is not truthy, use a hardcoded constant.
let max = maxWidth || preferences.maxWidth || 500;
在 ES6 之前,这个习惯用法经常用在函数中为参数提供默认值supply default values for parameters
// Copy the properties of o to p, and return p
function copy(o, p) {
p = p || {}; // If no object passed for p, use a newly created object.
// function body goes here
}
然而,在 ES6 及更高版本中,不再需要这个技巧
因为默认参数值可以简单地写入函数定义本身: function copy(o, p={}) { ... }