基本概念
在javaScript中,可以使用 ==, ===,Object.is()进行类型的比较。两等号判等,会在比较时进行类型转换。三等号判等(判断严格),比较时不进行隐式类型转换,(类型不同则会返回false)。Object.is() 在三等号判等的基础上特别处理了 NaN 、-0 和 +0 ,保证 -0 和 +0 不再相同,但 Object.is(NaN, NaN) 会返回 true。Object.is() 应被认为有其特殊的用途,而不能用它认为它比其它的相等对比更宽松或严格。下面我们看一下javaScript的 ==。
1 == '1' // true
1 == true // true
1 == [] // false
1 == [1] // true
1 == {} // false
true == "1" // true
true == "true" // false
true == [] // true
true == [1] // true
true == {} // false
[] == "1"// false
[] == true // false
[] == false // true
[] == [] // false
[] == {} // false
[] == ![] // true (注意!!!)
{} == 1 // Unexpected token '=='
{} == "1" // Unexpected token '=='
{} == true // Unexpected token '=='
{} == false // Unexpected token '=='
{} == [] // Unexpected token '=='
{} == {} // false
"0" == null // false
"0" == undefined // false
"0" == false // true
"0" == NaN // false
"0" == 0 // true
"0" == "" // false
false == null // false
false == undefined // false
false == NaN // false
false == 0 // true
false == "" // true
false == [] // true
false == {} // false
"" == null // false
"" == undefined //false
"" == NaN // false
"" == 0 //true
"" == [] // true
"" == {} // false
0 == null // false
0 == undefined // false
0 == NaN // false
0 == [] // true
0 == {} // false
+0 == -0 // true
null == null // true
null == undefined // true
null == "" // false
null == 0 // false
undefined == "" // false
undefined == 0 // false
NaN == NaN // false
== 相等操作中,如果两边的操作数不同的话,都会进行类型转换,而且优先转为数字,再进行比较,如果转换后还不同则再次转换,直到相同为止。
- 字符串和数字之间的相等比较:
- 如果Type(x)是数字,Type(y)是字符串,则返回
x == ToNumber(y)
的结果。 - 如果Type(x)是字符串,Type(y)是数字,则返回
ToNumber(x) == y
的结果。
- 如果Type(x)是数字,Type(y)是字符串,则返回
- 其他类型值和布尔类型之间的相等比较:
- 如果Type(x)是布尔类型,这返回
ToNumber(x) == y
的结果 - 如果Type(y)是布尔类型,则返回
x == ToNumber(y)
的结果
- 如果Type(x)是布尔类型,这返回
- null和undefined之间的相等比较
- 如果x为null,y为undefined,则结果为true
- 如果x为undefined,y为null,则结果为true
- 对象和非对象之间的相等比较:
- 如果Type(x)是字符串或者数字,Type(y)是对象,则返回
x == ToPrimitive(y)
的结果 - 如果Type(x)是对象,Type(y)是字符串或者数字,则返回
toPrimitive(x) == y
的结果
- 如果Type(x)是字符串或者数字,Type(y)是对象,则返回
- 如果一个操作值为 NaN ,则相等比较返回 false( NaN 本身也不等于 NaN )。
- 如果两个操作值都是对象,则比较它们是不是指向同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true,否则,返回 false。
在数据类型我们知道v8的数据的类型继承关系。在v8::Value中定义了两个函数。这两个函数只要是做数据类型的比较。
class V8_EXPORT Value : public Data {
public:
// 相对与javaScript的 ==
V8_WARN_UNUSED_RESULT Maybe<bool> Equals(Local<Context> context,
Local<Value> that) const;
// 相对与javaScript的 ===
bool StrictEquals(Local<Value> that) const;
};
这就是javaScript在v8的入口。下面让我们看这两个函数的具体实现,为你解密javaScript的数据类型比较在javaScript中的实现。
Equals()
Maybe<bool> Value::Equals(Local<Context> context, Local<Value> that) const {
i::Isolate* isolate = Utils::OpenHandle(*context)->GetIsolate();
ENTER_V8(isolate, context, Value, Equals, Nothing<bool>(), i::HandleScope);
auto self = Utils::OpenHandle(this);
auto other = Utils::OpenHandle(*that);
// 正真执行类型的比较函数。
Maybe<bool> result = i::Object::Equals(isolate, self, other);
has_pending_exception = result.IsNothing();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return result;
}
在上面的实现中我们可以看到真的执行类型比较的是v8::internal::Object::Equals()函数。我们看一下具体的实现。我们把左边的作为X,右边作为Y。对下面的方法比较逐个解析。
Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x,
Handle<Object> y) {
while (true) {
if (x->IsNumber()) {
if (y->IsNumber()) {
return Just(StrictNumberEquals(x, y));
} else if (y->IsBoolean()) {
return Just(
StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
} else if (y->IsString()) {
return Just(StrictNumberEquals(
x, String::ToNumber(isolate, Handle<String>::cast(y))));
} else if (y->IsBigInt()) {
return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
} else if (y->IsJSReceiver()) {
if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} else if (x->IsString()) {
if (y->IsString()) {
return Just(String::Equals(isolate, Handle<String>::cast(x),
Handle<String>::cast(y)));
} else if (y->IsNumber()) {
x = String::ToNumber(isolate, Handle<String>::cast(x));
return Just(StrictNumberEquals(x, y));
} else if (y->IsBoolean()) {
x = String::ToNumber(isolate, Handle<String>::cast(x));
return Just(
StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
} else if (y->IsBigInt()) {
return BigInt::EqualToString(isolate, Handle<BigInt>::cast(y),
Handle<String>::cast(x));
} else if (y->IsJSReceiver()) {
if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} else if (x->IsBoolean()) {
if (y->IsOddball()) {
return Just(x.is_identical_to(y));
} else if (y->IsNumber()) {
return Just(
StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
} else if (y->IsString()) {
y = String::ToNumber(isolate, Handle<String>::cast(y));
return Just(
StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
} else if (y->IsBigInt()) {
x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x));
return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
} else if (y->IsJSReceiver()) {
if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x));
} else {
return Just(false);
}
} else if (x->IsSymbol()) {
if (y->IsSymbol()) {
return Just(x.is_identical_to(y));
} else if (y->IsJSReceiver()) {
if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
.ToHandle(&y)) {
return Nothing<bool>();
}
} else {
return Just(false);
}
} else if (x->IsBigInt()) {
if (y->IsBigInt()) {
return Just(BigInt::EqualToBigInt(BigInt::cast(*x), BigInt::cast(*y)));
}
return Equals(isolate, y, x);
} else if (x->IsJSReceiver()) {
if (y->IsJSReceiver()) {
return Just(x.is_identical_to(y));
} else if (y->IsUndetectable()) {
return Just(x->IsUndetectable());
} else if (y->IsBoolean()) {
y = Oddball::ToNumber(isolate, Handle<Oddball>::cast(y));
} else if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(x))
.ToHandle(&x)) {
return Nothing<bool>();
}
} else {
return Just(x->IsUndetectable() && y->IsUndetectable());
}
}
}
当X为数字
当Y为数字
当x和y都为数字类型的时候。执行 StrictNumberEquals(Handle