犀牛书第七版学习笔记:表达式和运算符

本文详细介绍了JavaScript中的表达式,包括基本表达式、对象和数组初始化器、函数定义表达式、属性访问表达式、调用表达式、运算符概述。重点讲解了运算符的类型转换、副作用、优先级和关联性,以及相等和逻辑运算符的用法,特别是逻辑AND(&&)的捷径行为。此外,还提到了条件属性访问和条件调用的最新特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.基本表达式Primary Expressions

2.对象和数组初始化器Object and Array Initializers

1.数组初始化器

2.对象初始化器

3.函数定义表达式Function Definition Expressions

4.属性访问表达式Property Access Expressions

条件属性访问Conditional Property Access

5.调用表达式Invocation Expressions

1.函数调用

2.方法调用

3.条件调用Conditional Invocation

6.对象创建表达式Object Creation Expressions

7.运算符概述Operator Overview

1.操作数数量Number of Operands

2.操作数和结果类型Operand and Result Type

1.JavaScript 运算符通常会根据需要转换其操作数的类型

2.某些运算符的行为会因所使用的操作数的类型而异

3.操作符副作用Operator Side Effects

4.运算符优先级Operator Precedence

5.运算符关联性Operator Associativity

8 相等操作符Equality and Inequality Operators

1.严格相等STRICT EQUALITY

10.逻辑表达式Logical Expressions

1.Logical AND (&&)

1.执行布尔 AND 运算

2.作为真值和假值的布尔 AND 运算符

3.捷径

2.Logical OR (||)


表达式是 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]
  • 对于任一类型的属性访问表达式,. 或者方括号之前的表达式将首先被评估。
    1. 如果第一个表达式计算出的值为 null 或未定义undefined,则表达式将引发 TypeError,因为null 或未定义undefined两个值是不能具有属性
    2. 如果对象表达式后跟一个点和一个标识符,则该标识符命名的属性的值成为表达式的整体值。
    3. 如果对象表达式后跟方括号中的另一个表达式,则计算第二个表达式并将其转换为字符串。表达式的整体值就是由该字符串命名的属性的值。
    4. 在任何一种情况下,如果命名属性不存在,则属性访问表达式的值是未定义的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
  1. a 是一个对象,所以 a.b 是一个有效的属性访问表达式。
  2. 但是 a.b 的值为 null,所以 a.b.c 会抛出 TypeError。
  3. 通过使用 ?. 代替 .我们避免了 TypeError,并且 a.b?.c 评估为未定义undefined。
  4. 这意味着 (a.b?.c).d 将抛出 TypeError,因为该表达式试图访问值未定义undefined.的属性。
  5. 但是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

注意:

  1. 表格按运算符优先级precedence组织。首先列出的运算符的优先级高于最后列出的运算符。由水平线分隔的运算符具有不同的优先级。
  2. 标记为 A 的列给出了运算符的关联性associativity,可以是 L(从左到右)或 R(从右到左)
  3. 列 N 指定操作数的数量the number of operands
  4. 标记为 Types 的列列出了操作数的预期类型the expected types of the operands和(在 → 符号之后)运算符的结果类型the result type for the operator。

1.操作数数量Number of Operands

运算符可以根据它们期望的操作数数量(arity)进行分类。

  1. 大多数 JavaScript 运算符,如 * 乘法运算符,都是将两个表达式组合成一个更复杂的表达式的二元运算符binary operators。 也就是说,他们期望两个操作数。
  2. JavaScript 还支持许多一元运算符unary operators,它们将单个表达式转换为单个更复杂的表达式。 表达式 -x 中的 - 运算符是一元运算符,对操作数 x 执行取反运算。
  3. 最后,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

严格相等操作符===计算其操作数,然后按如下方式比较两个值,不进行类型转换:

  1. 如果两个值具有不同的类型,则它们不相等。
  2. 如果两个值都为空或两个值都未定义,则它们是相等的。
  3. 如果两个值都是布尔值true或都是布尔值false,它们是相等的。
  4. 如果一个或两个值都是NaN,则它们不相等。(这是令人惊讶的,但NaN值永远不等于任何其他值,包括它自己!要检查一个值x是否为NaN,可以使用x !== x,或者全局的isNaN()函数。)
  5. 如果两个值都是数字且值相同,则它们是相等的。如果一个值是0,另一个值是-0,它们也是相等的。
  6. 如果两个值都是字符串,并且在相同的位置包含完全相同的16位值(16-bit value),它们是相等的。如果字符串的长度或内容不同,则它们不相等。两个字符串可能具有相同的含义和相同的视觉外观,但仍然使用不同的16位值序列进行编码encoded using different sequences of 16-bit values。JavaScript不执行Unicode规范化Unicode normalization,像这样的一对字符串使用===或==操作符东都不会被认为是相等的。
  7. 如果两个值都指向同一个对象、数组或函数,则它们是相等的。如果它们引用不同的对象,它们就不相等,即使两个对象具有相同的属性。

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={}) { ... }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值