JS 代码执行题( == 与 隐式转化)
我们先来看个例子:
var a = [0];
if (a) {
console.log(a == true);
} else {
console.log(a);
}
// 写出执行结果,并解释原因
大家猜想结果会是什么呢?
- 答案是:
false;
为什么呢?
解析
(1) 当 a 出现在 if 的条件中时,被转成布尔值,而 Boolean([0])为 true,所以就进行下一步判断 a == true,在进行比较时,[0]被转换成了 0,所以 0==true 为 false
数组从非primitive 转为primitive的时候会先隐式调用 join 变成0,string 和 boolean 比较的时候,两个都先转为number类型再比较,最后就是 0==1 的比较了,至于为什么会调用 join 在后面剖析。
深度剖析
ToBoolean
我们先看看 ToBoolean 转化规则
当我们进行 if 判断时 会进行 Boolean([0]) 转化得到的结果是 true 在进行 == 判断
!![] //true 空数组转换为布尔值是 true,
Boolean([]) // 空数组转换为布尔值是 true
!![0]//true 数组转换为布尔值是 true
[0] == true;//false 数组与布尔值比较时却变成了 false
Number([])//0 当 Number 中 为 Object 时还会调用 ToPrimitive
Number(false)//0
Number(['1'])//1 当 Number 中 为 Object 时还会调用 ToPrimitive
我们看看 == 判断得隐式转化规则
== 判断规则
我们先看看几个简单的例子
console.log(1 == 1);
// expected output: true
console.log('hello' == 'hello');
// expected output: true
console.log('1' == 1);
// expected output: true
console.log(0 == false);
// expected output: true
比较x == y,其中x和y是值,产生true或 false。如下进行这样的比较:
-
如果x和y引用同一个对象,则返回true 。 否则,返回false。********
-
如果x为null且y 未定义,则返回true。
-
如果x 未定义且y为null ,则返回true。
-
如果Type ( x ) 是 Number 并且Type ( y ) 是 String,则
返回比较结果x == ToNumber ( y )。 -
如果Type ( x ) 是 String 并且Type ( y ) 是 Number ,则
返回比较结果ToNumber ( x ) == y。 -
如果Type ( x ) 是 String 或 Number 并且Type ( y ) 是 Object,则
返回比较结果x == ToPrimitive ( y )。 -
如果Type ( x ) 是 Object 并且Type ( y ) 是 String 或 Number ,
则返回比较ToPrimitive ( x ) == y的结果。 -
返回假。
我们看看下面几个例子
"1" == 1; // true
1 == "1"; // true
0 == false; // true
0 == null; // false
0 == undefined; // false
null == undefined; // true
const number1 = new Number(3);
const number2 = new Number(3);
number1 == 3; // true
number1 == number2; // false
注意:
- 等式运算符并不总是可传递的。例如,可能有两个不同的 String 对象,每个对象代表相同的 String 值;操作员会认为每个 String 对象都等于 String 值
==,但两个 String 对象不会彼此相等。例如:new String("a")=="a"并且"a"==new String("a")都是true。new String("a")==new String("a")是false的。
ToPrimitive
当使用提示String调用O的[[DefaultValue]]内部方法时,采取以下步骤:
-
令toString为使用参数 " " 调用对象O的 [[Get]] 内部方法的结果
toString。 -
如果IsCallable ( toString) 为真,则
- 令str为调用toString的 [[Call]] 内部方法的结果,其中O作为 this值和一个空参数列表。
- 如果str是原始值,则返回str。
-
令valueOf为使用参数 " " 调用对象O的 [[Get]] 内部方法的结果
valueOf。 -
如果IsCallable ( valueOf) 为真,则
- 令val为调用valueOf的 [[Call]] 内部方法的结果,其中O作为 this 值和一个空参数列表。
- 如果val是原始值,则返回val。
-
抛出TypeError异常。
当O的 [[DefaultValue]] 内部方法用提示 Number 调用时,采取以下步骤:
-
令valueOf为使用参数 " " 调用对象O的 [[Get]] 内部方法的结果
valueOf。 -
如果IsCallable ( valueOf) 为真,则
- 令val为调用valueOf的 [[Call]] 内部方法的结果,其中O作为 this值和一个空参数列表。
- 如果val是原始值,则返回val。
-
令toString为使用参数 " " 调用对象O的 [[Get]] 内部方法的结果
toString。 -
如果IsCallable ( toString) 为真,则
- 令str为调用toString的 [[Call]] 内部方法的结果,其中O作为 this 值和一个空参数列表。
- 如果str是原始值,则返回str。
-
抛出TypeError异常。
当O的 [[DefaultValue]] 内部方法在没有提示的情况下被调用时,它的行为就像提示是数字一样,除非O是 Date 对象(参见 15.9.6),在这种情况下,它的行为就像提示是细绳。
本机对象的上述 [[DefaultValue]] 规范只能返回原始值。如果宿主对象实现了自己的 [[DefaultValue]] 内部方法,它必须确保其 [[DefaultValue]] 内部方法只能返回原始值。
数组中的toString
当数组调用toString 方法的时候 ,会调用内部的join 方法, 所以 [0].join("") 时变成 "0"
当我们进行 == 判断是 此时 的 "0" == 1 对比时会遵循规则
此时又会将 "0" 转化成 0 在与1 对比 , 所以最终 返回的结果为false
解释一下Number(['1']) 执行过程
剖析Number([1]) 转化
当我们使用Number(['1']) 时调用的是valueOf 方法 、toString 、join
我们来重写valueOf,toString,join看看
const orginValueOf = Array.prototype.valueOf // 保留原来的valueOf 方法
Array.prototype.valueOf = function () {
console.log("调用 valueOf")
return orginValueOf.call(this)
}
const orginTostring = Array.prototype.toString // 保留原来的 toString 方法
Array.prototype.toString = function () {
console.log("调用 toString")
return orginTostring.call(this)
}
const orginJoin = Array.prototype.join; // 保留原来的join 方法
Array.prototype.join = function () {
console.log("调用 Array.join() ")
return orginJoin.call(this)
}
const res = Number([1])
console.log(typeof res, res)// number 1
从上述例子中我们可以看出 Number([1]) 依次执行 valueOf 、toString、 join
我们看看 valueOf 的返回值
JavaScript调用valueOf方法将对象转换为原始值。你很少需要自己调用valueOf方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。
默认情况下,valueOf方法由Object后面的每个对象继承。 每个内置的核心对象都会覆盖此方法以返回适当的值。如果对象没有原始值,则valueOf将返回对象本身。
JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。因此,不同类型对象的valueOf()方法的返回值和返回值类型均可能不同。
| 对象 | 返回值 |
|---|---|
| Array | 返回数组对象本身。 |
| Boolean | 布尔值。 |
| Date | 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。 |
| Function | 函数本身。 |
| Number | 数字值。 |
| Object | 对象本身。这是默认情况。 |
| String | 字符串值。 |
| Math 和 Error 对象没有 valueOf 方法。 |
你可以在自己的代码中使用valueOf将内置对象转换为原始值。 创建自定义对象时,可以覆盖Object.prototype.valueOf()来调用自定义方法,而不是默认Object方法。
剖析 [1] == 1 转化规则
const orginValueOf = Array.prototype.valueOf // 保留原来的valueOf 方法
Array.prototype.valueOf = function () {
console.log("调用 valueOf")
return orginValueOf.call(this)
}
const orginTostring = Array.prototype.toString // 保留原来的 toString 方法
Array.prototype.toString = function () {
console.log("调用 toString")
return orginTostring.call(this)
}
const orginJoin = Array.prototype.join; // 保留原来的join 方法
Array.prototype.join = function () {
const result = orginJoin.call(this)
console.log("调用 Array.join() = ",typeof result ,result)
return result
}
const orginToNumber = Number; // 保留全局的 Number
window.Number = function (...arg) {
console.log("调用 Number ")
return orginToNumber(arg)
}
console.log([1] == 1) // true
从上面的结果我们可以看出[1] == 1 的过程 也会 依次执行 valueOf 、toString、 join 最终变成 "1" 在执行内部的 ToNumber 方法 最终将 [1] ==> 1
"true" == true 的结果
肯定很多小伙伴都会觉得这个的答案是 true
然而结果并不是我们想的那样, 我们来看看他是如何转化的呢?
true==>Number(true)==>1
If Type(y) is Boolean, return the result of the comparison x == ToNumber(y). 2.
1=="true"==> 将"true"=>Number("true")==>NANIf Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.
1==NAN==>false
所以最终的结果为 false
剖析 Number(1) == 1
const n = new Number(2)
const orginValueOf = Number.prototype.valueOf // 保留原来的valueOf 方法
Number.prototype.valueOf = function () {
const value = orginValueOf.call(this)
console.log("调用 valueOf","value = "+value,"typeof: "+typeof value)
return value
}
const orginTostring = Number.prototype.toString // 保留原来的 toString 方法
Number.prototype.toString = function () {
console.log("调用 toString")
return orginTostring.call(this)
}
const orginJoin = Number.prototype.join; // 保留原来的join 方法
console.log(n == 2)
我们可以 发现和前面的 Array 不同的是 Number 重写了 valueOf 方法 所以不会调用 内部的 Tostring 方法,
我们可以试着修改 Number的valueOf 改成原来的 Object.prototype.valueOf看看结果如何?
所以我们可以看到 Number 中是重写了valueOf方法,所以不会调用 ToString ,直接讲 number 类型的数据返回
总结
我们在进行 == 判断 转化式遵循以下规则即可,其实我们可以从中发现,类型不同时,最终主要都还是进行Number转化
喜欢我的小伙伴可以 关注我的博客哦: 白鹤之家