跳到主要内容

操作符

运算符优先级

image-20200730105454730

等于

===

x === y:严格相等

严格相等运算符最大的特点是不会进行隐式的类型转换,它是直接基于类型相等的情况才会进行下去比较过程,类型不等直接就false

根据 ES 规范文档的描述,严格相等运算符的规则如下:

  • 两边类型不一样,直接返回false
  • 如果左边是Number或者BigInt类型,那就执行Number::equal(x, y)这个操作,如下
    • 如果 x 或者 y 是NaN,则返回false
    • 如果 x 或者 y 是 0,不论正负,返回true
    • 如果 x 和 y 是精确的数值(包括无理数 Π)且相等,则返回true
  • 剩余类型比较如下:
    • undefined只和undefined相等
    • null只和null相等
    • Boolean,两边都是true或者都是false
    • Symbol,仅当在使用Symbol.for(key)创建的两个Symbol值时才会相等
    • String,仅当两个字符串在相应的索引处具有相同的长度和相同的字符时才相等
    • Object,仅当引用同一对象才相等

==

x == y

当两边比较的值是同种类型时,==的行为和严格相等===一致;但是两边类型不等时,==有时候会进行一些隐式的类型转换

根据 ES 规范文档的描述,其比较规则也是相当复杂,需要注意的是以下步骤是从上到下依次判断的:

  • 如果两边类型相同,返回使用===比较的结果;

  • undefined == null

  • 如果等号一侧是NumberBigInt,另一边是String,则String需进行 ToNumber/StringToBigInt 转换,重复上述步骤进行比较;

  • NaN不和任何值相等,返回false

  • 如果等号一侧是Boolean类型,将布尔值进行ToNumber转换,转成 1 或者 0,然后重复上面步骤进行比较;

  • 如果等号一侧是原始值类型String, Number, BigInt, or Symbol,而另一侧是Object,需要将Object进行ToPrimitive(hint number)转换,然后重复上面步骤进行比较;

  • 如果是BigIntNumber比较,一边出现正负无穷±∞NaN,则返回false;否则两边值相等则返回true

  • 不符合上面比较情况的返回false

// 1. false需要进行ToBoolea转换,变成比较 " " == 0
// 2. 空白字符串" "进行ToNumber转换,得到 0,变成比较 0 == 0
// 3. 两边类型相同,比较 0 === 0
" " == false

// 1. true进行ToNumber转换,变成比较1 == "1"
// 2. 字符串"1"进行ToNumber转换,得到 1
// 3. 最后变成1 === 1

true == "1" => true

// 1. Boolean转Number,变成比较 1 == {}
// 2. Object进行ToPrimitive(hint number)转换,也就是ToNumber,先获取valueOf得到的不是原始值,再尝试获取toString,得到字符串"[object, Object]",变成比较 1 == "[object, Object]"
// 3. "[object, Object]"转Number,得到NaN,变成比较 1 == NaN
true == {} => false

// 1. fasle转Number,变成比较 0 == undefined
// 2. 不符合上述任何条件比较,直接最后返回false
false == undefined => false

// 1. fasle转Number,变成比较 0 == []
// 2. []进行ToPrimitive(hint number)转换,尝试获取valueOf实际执行ToObject,得到原值[];再次尝试toString,实际内部执行[].join(","),得到空字符串,变成比较 0 == ""
// 3. 空字符串ToNumber,得到0,最后就是比较 0 === 0
false == [] => true

false == "0" => true

false == "" => true

"" == 0 => true

"" == [] => true

[] == 0 => true

// [null]转原始值得到空字符串
"" == [null] => true

// 换行符转number也是看作 0
0 == "\n" => true

Object.is

Object.is(x, y)

Number::sameValueZero ( x, y )

SameValueNonNumeric ( x, y )

Object.is(x, y)的判断过程不涉及任何类型转换;

  • xy类型不相同,直接返回false
  • xy类型相同,
    • 如果都是Number或者BigInt类型,只要两边长得不一样就返回false,如果都是NaN±0则返回true
    • undefined = undefined
    • null = null
    • true = true
    • Object类型只有引用一致才相等

Object.is(x, y)和严格相等===唯一的区别就是处理NaN这个值

console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false

console.log(Object.is(null, undefined)); // false
console.log(null === undefined); // false

加号

二元 +

ECMA - Addition Operator

二元相加运算符只会进行两种运算,要么是字符串连接,要么是数字相加,根据 ES 规范文档的描述,相加运算符是有明确规则的:

  • 如果加号一边有Symbol类型对象,直接TypeError
  • +两边操作数都进行ToPrimitive转换,得到原始值;
    • !!!这点尤其重要,此时的ToPrimitive转换并不会像 ToNumber 和 ToString 传递显式的 hint,默认是除了Date类型外都默认进行ToPrimitive(hint number)转换Date类型是ToPrimitive(hint string)转换;
  • 对原始值类型再进行判断,如果一边有String类型,则都进行ToString转换,然后进行字符串连接;
  • 否则就都进行ToNumber转换,然后判断两边类型是否相同,如果不相同则TypeError;相同则相加运算返回结果
// 1. ToPrimitive,都是原始值类型,所以直接返回;
// 2. 右边是String,则都ToString
// 3. 最后字符串连接
1 + '1' = '11';

// 1. ToPrimitive,都是原始值类型,所以直接返回;
// 2. 两边没有String类型,都进行ToNumber
// 3. null进行ToNumber,按照规范表中的指示,得到+0
// 4. 两边相加
1 + null = 1;

// undefined进行ToNumber,按照规范表中的指示,得到NaN
// NaN和任何数运算都是NaN
1 + undefined = NaN;

// 1. 左边ToPrimitive,直接返回
// 2. 右边ToPrimitive(number),也就是ToNumber,
// 3. 先尝试获取valueOf,返回原值 {},非原始值类型,
// 4. 继续尝试toString,直接调用Object.prototype.toString,返回字符串[object, Object]
// 5. 右边原始值得到字符串[object, Object],所以左边ToString
// 6. 最后做字符串连接
1 + {} = '1[object, Object]';

// 1. 右边ToPrimitive(number),也就是ToNumber,
// 2. 尝试获取valueOf,返回原值[],非原始值类型
// 3. 再次尝试toString,Array重写了toString,相当于调用join,则返回空字符串""
// 4. 左边ToString
// 5. 最后字符串连接
1 + [] = '1';

// 1. 右边ToPrimitive(string),优先调用Date.prototype[@@toPrimitive],返回一大串时间字符串
// 2. 左边ToString
// 3. 字符串连接
1 + new Date() = '1Mon Jul 27 2020 00:37:07 GMT+0800 (中国标准时间)';

判断下面函数结果

function sum(a, b) {
return a + b;
}

sum(1, '2'); // "12"

一元 +

ECMA - Unary + Operator

一元加运算符,只会进行ToNumber转换

console.log(+''); // 0
console.log(+'1212e0'); // 1212
console.log(+'12.3'); // 12.3
console.log(+'.89'); // 0.89
console.log(+'2.5²'); // NaN
console.log(+'2.5e8'); // 250000000
console.log(+'0b0110'); // 6
console.log(+'0o1244'); // 676
console.log(+'0xFFFF'); // 65535
console.log(+'0o1244.8'); // NaN 除了十进制,其它进制一概不允许出现小数点

console.log(+'1121afgrt'); // NaN 数字字母混合,直接NaN

"b"+"a"+ +"a"+"a"

无论什么时候,+都是从左往右按顺序执行的,"b"+"a"+ +"a"+"a"的执行过程如下:

  • "b"+"a""ba"
  • "ba"+ (+"a")"baNaN"
  • "baNaN"+"a""baNaNa"
console.log(('b' + 'a' + +'a' + 'a').toLowerCase());

// banana

逻辑运算符

!

逻辑非

根据 ES 规范文档的描述,逻辑非运算符规则如下:

  • 对操作数执行ToBoolean转换
  • 如果转换结果是 true,则返回 false
  • 返回 true
类型结果
Undefinedfalse
Nullfalse
Number±0,NaN => false
其它都是 true
String空字符串 => false
其它都是 true
Symboltrue
BigInt0n => false
其它都是 true
Objecttrue

&&和||

&&||

从 ES 规范文档的描述来看,&&||主要是评估左侧表达式的值来决定输出结果。他们两个的共同特点是产生的值将始终是两个操作数表达式之一的值,并不一定都是Boolean类型的。

对于a && b,记为左侧为假就返回左侧

  • 首先将左侧a进行ToBoolean转换得到lbool

  • 如果lboolfalse,那么就返回a,注意是返回a

  • 否则,就返回b

对于a || b,记为左侧为真就返回左侧

  • 首先将左侧a进行ToBoolean转换得到lbool

  • 如果lbooltrue,那么就返回a,注意是返回a

  • 否则,就返回b

console.log('cat' && 'dog'); // "dog"

console.log('' && 'dog'); // ""

console.log('cat' || 'dog'); // "cat"

console.log('' || 'dog'); // "dog"

还有一点是&&的优先级高于||

console.log(true || (false && false)); // true

console.log((true || false) && false); // false

逻辑或||是左侧为真则返回左侧;逻辑与&&是左侧为真则返回右侧;但是 ES 规范定义的经过ToBoolean可以转换成false的值有七个:

false
空字符串''
null
undefined
±0
NaN

?:

?::条件运算符,或者叫三元运算符

位于?左侧的变量会先进行隐式类型转换成boolean类型,然后再判断其是否为true,因此要注意以下假值:

±0
NaN
null
undefined
''
false

?.

?.:可选链操作符,ES2020 支持,目前也还是stage 4的状态

主要用于对象获取属性,避免从undefined上获取属性时报错的问题,当左侧引用的对象为null或者undefined的时候会直接返回,避免报错。在这个符号之前,你需要使用&&来判断对象

obj && obj.first && obj.first.second;

obj?.first?.second;

??

??:空值合并操作符,ES2020 支持

??,空值合并表达式,其也是通过评估左侧表达式的值来决定输出结果的,但是??不涉及任何类型转换,其规则用一句话总结就是:

当且仅当左侧为nullundefined的时候,才返回左侧的值(也就是返回nullundefined),其余情况都是返回右侧的值

console.log(null ?? ''); // ""

这个操作符最常用的就是和?.一起使用,当?.返回undefined或者null的时候,再使用??补上一个恰当的值来保证整个运算的完整。

let obj: Object | null = {
a: 'oxygen',
};

const a = obj?.a ?? 'oxygen';