操作符和表达式✅
== 和 === 区别
答案
==
内部调用 IsLooselyEqual 宽松比较 将不同类型的值转换为为同类型后再进行比较,===
内部调用 IsStrictlyEqual 严格比较 如果类型不同直接返回 false,类型相同再进行值比较!
宽松比较的核心类型转换规则为
- 原始类型比对时中 String、Boolean 都会调用 ToNumber 转换为数值后比较
- 对象比对时会调用 ToPrimitive 转换为原始值后在回退到规则 1 进行比较
- 注意一个特殊的细节,是
document.all == false
返回 ture,stackoverflow Why is document.all falsy? 了解细节
提示
这里我们以 [] == false
为例,基于规范详细说明为什么会返回 true
==
比较触发内部 IsLooselyEqual 宽松比较[]
是一个对象,调用 ToPrimitive 转换为原始值- ToPrimitive 首先尝试调用对象实例内部
valueOf
方法,返回[]
,然后尝试调用toString
方法,返回空字符串""
- 然后变为
"" == false
比较 - false 进一步调用 ToNumber 转换为数值 0
"" == 0
,""
调用 ToNumber 转换为数值 0- 数值 0 == 0 返回 true
注意宽松比较不会抛出错误,所以规范中会递归调用直到返回一个结果,但是引擎实现时,会根据具体情况进行优化,可能会提前返回结果。
typeof 和 instanceof 的区别
答案
特性 | typeof | instanceof |
---|---|---|
用途 | 判断数据类型 | 判断实例归属关系 |
返回值 | 字符串 | 布尔值 |
适用范围 | 基本类型/引用类型 | 引用类型(对象) |
局限性/注意点 | null 返回 "object" ,无法区分数组/对象 | 不能判断基本类型,依赖原型链 |
总结:
typeof
适合判断基本类型,部分引用类型(如函数),但无法区分数组和对象,null
返回"object"
。instanceof
用于判断对象是否为某构造函数的实例,适合引用类型,无法判断基本类型。
操作符优先级
答案
操作符优先级(Operator Precedence)决定了在表达式中多个操作符同时出现时,谁先参与运算。优先级高的操作符会比优先级低的操作符先执行。
如果没有优先级,表达式如 2 + 3 * 4
就无法确定是先加还是先乘。优先级规则保证了表达式的计算顺序符合数学和语言设计的直觉。
核心规则
- 括号
()
拥有最高优先级,可强制改变默认顺序。 - 一元操作符(如
!
,typeof
,++
,--
)优先级高于二元操作符(如+
,-
,*
,/
)。 - 乘除(
*
,/
,%
)高于加减(+
,-
)。 - 赋值操作符(
=
,+=
,-=
等)优先级较低,通常最后执行。 - 逻辑与(
&&
)高于逻辑或(||
)。
常见错误
- 忽略括号导致运算顺序错误,如
a + b * c
实际为a + (b * c)
。 - 连续赋值时,
a = b = c
实际为a = (b = c)
,右侧先执行。 - 混用逻辑与或时,
a || b && c
实际为a || (b && c)
。
优先级表,数字越大优先级越高:
优先级(高→低) | 操作符 | 说明 |
---|---|---|
20 | () | 圆括号 |
17 | ++ , -- , ! , typeof | 一元操作符 |
15 | * , / , % | 乘、除、取余 |
14 | + , - | 加、减 |
13 | << , >> , >>> | 位移 |
12 | < , > , <= , >= | 比较 |
11 | == , != , === , !== | 相等/不等 |
6 | && | 逻辑与 |
5 | ` | |
3 | = , += , -= | 赋值 |
建议:表达式复杂时,优先使用括号明确运算顺序,避免歧义和潜在 bug。
延伸阅读
说明下面的代码运行结果
(function () {
const a = b = 3
})()
console.log('a defined? ' + (typeof a !== 'undefined'))
console.log('b defined? ' + (typeof b !== 'undefined'))
答案
运行结果是:
a defined? false
b defined? true
详细解释:
-
const a = b = 3
这行代码其实等价于:b = 3
:由于没有用var
/let
/const
声明,b
变成了全局变量(在浏览器环境下会挂载到window
上)。const a = b
:a
只是当前函数作用域内的常量。
-
由于整个赋值语句在一个自执行函数
(function () { ... })()
内部,a
只在函数作用域内有效,外部访问不到。 -
b
虽然在函数内部赋值,但由于没有用var
/let
/const
声明,实际上是给全局对象添加了一个属性,所以函数外部也能访问到。 -
console.log('a defined? ' + (typeof a !== 'undefined'))
a
在全局作用域未定义,typeof a
返回'undefined'
,所以结果是false
。
-
console.log('b defined? ' + (typeof b !== 'undefined'))
b
已经变成全局变量,typeof b
返回'number'
,所以结果是true
。
易错点提示:
- 赋值表达式
const a = b = 3
,b
没有声明关键字,容易误以为b
只在函数内有效,实际是全局变量。 - 推荐始终使用
let
或const
显式声明变量,避免污染全局作用域。
Object.is() 与比较操作符 “===”、“==” 有什么区别
答案
特性/比较方式 | == | === | Object.is() |
---|---|---|---|
类型转换 | 有 | 无 | 无 |
NaN 与 NaN | 不相等 | 不相等 | 相等 |
+0 与 -0 | 相等 | 相等 | 不相等 |
对象比较 | 引用地址 | 引用地址 | 引用地址 |
备注 | 宽松相等,自动类型转换 | 严格相等,类型和值都需相同 | 区分 +0/-0 和 NaN |
总结:
Object.is()
与===
类似,但能区分+0
和-0
,且NaN
与自身相等。==
存在类型转换,通常不推荐用于判断相等性。