==与===,运算符优先级

https://www.cnblogs.com/wisewrong/p/10396002.html

重学前端-总结:05、JavaScript类型:关于类型,有哪些你不知道的细节?

JavaScript 基本类型的装箱与拆箱

【JS迷你书】类型转换之拆箱操作

【JS迷你书】类型转换之装箱操作

一道常被人轻视的前端JS面试题

运算符优先级

定义:运算符的优先级决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。

常用运算符优先级排序:具有相同优先级的运算符按从左到右(或从右到左)的顺序求值。

运算类型 运算符 示例 优先级
圆括号 () (a+b) 20
成员访问 . Obj.a 19
需计算的成员访问 …[…] Obj[1+2]
new(带参数列表) new Person(‘name’,’age’)
函数调用 func()
new(无参数列表) new Foo 等同于 new Foo(),也就是没有指定参数列表 18(从右到左)
后置递增 i++ 17
后置递减 i–
逻辑非 !… ![] 16(从右到左)
一元正号 +… +3 //3 、 +”3” //3、 +true //1
一元负号 -… -1 // -1
前置递增 ++i
前置递减 –i
typeof
delete
await
乘法 * 1*2 14
除法 / 1/2
取余 % 1%2
加法 + 1+2 13
减法 - 1-2
小于 < 11
小于等于 <=
大于 >
大于等于 >=
in
instantceof
等号 == 1 == 1 10
不等 != 1 != 2
全等 === 1 === 1
不全等 !== 1 !== 2
按位与 … & … 9
按位异或 … ^ … 8
按位或 … \ 7
逻辑与 … && … 6
逻辑或 … \ \ 5
条件运算符 … ? … : … 4(从右到左)
赋值 3(从右到左)
yield 2(从右到左)
yield*
展开运算符 …[] 1
逗号 , 0

只写了一些相对比较常见的。

举几个例子

1
console.log("she is "+true? "?":"nt"+" here.") // "?"
1
2
3
var a = {n:1};
a.x = a = {n:2};
console.log(a.x); //undefined

变量提升和函数提升,优先关系:

WX20190717-084923@2x

WX20190717-085011@2x

看一下另外一篇

WechatIMG739

反正就是说也就是说函数声明优先,var声明重复的变量名,会被忽略,之后的函数声明会覆盖前面的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function Foo(){
getName = function(){
console.log(1);
}
return this;
}
Foo.getName = function(){
console.log(2)
}

Foo.prototype.getName = function(){
console.log(3)
}

var getName = function(){
console.log(4)
}
function getName(){
console.log(5)
}

Foo.getName();//2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3

编译阶段变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function Foo(){
getName = function(){
console.log(1);
}
return this;
}
function getName(){
console.log(5)
}
//根据上面截图的提升规则 var getName; 会被忽略
Foo.getName = function(){
console.log(2)
}
Foo.prototype.getName = function(){
console.log(3)
}

getName = function(){
console.log(4)
}

Foo.getName();//2
//略

getName(); //4
//略

Foo().getName(); //1
//在这里 函数调用、(.)成员访问、函数调用同级(19级),所以从左到右依次执行
//调用Foo(),window.getName函数被改变(打印1的那个),返回的this指向window
//接着等于调用window.getName(),返回1

getName(); //1
//调用window.getName(),返回1

new Foo.getName(); //2
// new Foo 无参数列表(18级) (.)成员访问(19级) 函数调用(19级)
//所以转换成 new (Foo.getName)() 组合成为有参列表

new Foo().getName(); //3
//new Foo()有参列表(19级) (.)成员访问(19级) 函数调用(19级)
//从左到右执行
//转换成 (new Foo()).getName()

new new Foo().getName(); //3
//new 有参列表(19级) (.)成员访问(19级) 函数调用(19级)
//转换成 new (new Foo().getName())
//转换成 new ((new Foo()).getName)()

==与===

什么是装箱操作

所谓装箱,就是把基本类型转变为对应的对象。装箱分为隐式显示

隐式装箱

每当读取一个基本类型的值时,后台会创建一个该基本类型所对应的对象。这个对象是个临时对象,调用的方法也是这个对象上的方法。执行方法后对象立刻被销毁。

1
2
3
4
5
num.toFixed(2); // '123.00'
//上方代码在后台的真正步骤为
var c = new Number(123);
c.toFixed(2);
c = null;
1
2
3
4
5
6
7
8
9
10
11
12
13
var a = 1;
a.x = 2;
console.log(a);// 1
console.log(a.x);// undefined
//真正步骤
var a = 1;
var temp1 = new Number(a);
temp1.x = 2;
temp1 = null;
console.log(a);// 1  
var temp2 = new Number(a);
console.log(temp2.x);// undefined  
temp2 = null;

显式装箱

通过内置对象 Boolean、Object、String 等可以对基本类型进行显示装箱。

1
var obj = new String('123');

拆箱操作

对引用类型进行基本类型才该有的操作,会把对象转变为基本类型的值。

对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。

拆箱过程,检查对象中是否有用户显式定义的[Symbol.toPrimitive] 方法,如果有,直接调用。否则调用内部函数 ToPrimitive。该操作接受两个参数,第一个参数是要转变的对象,第二个参数 PreferredType 是对象被期待转成的类型。第二个参数不是必须的(默认为 number),即对象被期待转为数字类型。拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型(如果 PreferrenType 的值为 string。则先调用 toString ,再调用 valueOf)。

1.用户定义的 [Symbol.toPrimitive],以及这个方法可以干扰一个对象转换为原始值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ""); // "[object Object]"

// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果
var obj2 = {
[Symbol.toPrimitive](hint) {
if (hint == "number") {
return 10;
}
if (hint == "string") {
return "hello";
}
return true;
}
};
console.log(+obj2); // 10 -- hint 参数值是 "number"
console.log(`${obj2}`); // "hello" -- hint 参数值是 "string"
console.log(obj2 + ""); // "true" -- hint 参数值是 "default"

2.如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var obj = {
valueOf : () => {console.log("valueOf"); return []},
toString : () => {console.log("toString"); return []}
}

String(obj)
// toString
// valueOf
// Uncaught TypeError: Cannot convert object to primitive value

obj+' '
//valueOf
//toString
// Uncaught TypeError: Cannot convert object to primitive value

Number(obj)
//valueOf
//toString
// Uncaught TypeError: Cannot convert object to primitive value

不同值比较时,转换规则:

WX20190716-123325@2x

toNumber:

WX20190716-124539@2x

toPrimitive:

WX20190716-124547@2x

例子:

1
2
3
4
console.log('string' == true);//false
//1. true先转换为数值,结果是1 等式变为 'string' == 1
//2. 'string'转换为数值,结果是NAN 等式变为 NAN == 1
//3. 所以是false
1
2
3
4
5
6
[] == ![] // true

//1. !…优先级高,转换为 [] == false
//2. 布尔值与任何类型比较,布尔值转换为数值,转换为 [] == 0
//3. 对象跟数值比较,对象调用toString()返回原始值,转换为 "" == 0
//4. 字符串跟数值比较,转为数值,转换为 0 == 0
1
2
3
4
5
{} == !{} //false
//1. !…优先级高,转换为 {} == false
//2. 布尔值与任何类型比较,布尔值转换为数值,转换为 {} == 0
//3. 对象跟数值比较,对象调用toString()返回原始值,转换为 "[object Object]" == 0
//4. 字符串与数值比较,字符串转换为数值,转换为 NaN == 0