一文了解JavaScript中的数据类型

663 阅读23分钟

常见数据类型

简述

JavaScript中的内置类型,七中类型中的又分为两大类:基本类型(值类型)和引用类型 基本类型有六种:nullnumberstringundefinedbooleansymbol

引用类型:ObjectArray等等。

所有基本类型的值都是不可改变的。但需要注意的是,基本类型本身和一个赋值为基本类型的变量的区别。 变量会被赋予一个新值,而原值不能像数组、对象以及函数那样被改变。

基本类型 是储存在栈中 引用类型 引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的位置,堆内存中的值地址引用保存在栈中,我们都是操作栈中的地址引用。

如果想看数据结构如堆、栈、链表等等,可以在本站搜索。

如下图所示:

javascript-type

如果不知道怎么判断数据类型的请看另一篇文章 JavaScript类型判断

基本类型

像基本类型如果String、Boolean、Number我们就不细写了,讲一下里面比较特殊的。如 null、undefined、NaN、symbol等等。

null

null是一个字面量,他不像undefined是一个全局对象的一个属性null是表示缺少的标识,知识变量未被指向任何对象,也可以看做是一个空指针对象

如果我们在浏览器中赋值 null,他会报错如下:

    // chrome Google Chrome 已是最新版本
    // 版本 75.0.3770.100(正式版本) (64 位)
    null = '111';
    // Uncaught ReferenceError: Invalid left-hand side in assignment

undefined

undefined它是一个JavaScript基本类型。它也是一个全局的属性undefined表示undefined,undefined也可以表示一个被声明没有被赋值变量

undefined 属性的属性特性:
writable false
enumerable false
configurable false

在现代浏览器(JavaScript 1.8.5/Firefox 4+),自ECMAscript5标准以来undefined是一个不能被配置(non-configurable),不能被重写(non-writable)的属性。即便事实并非如此,也要避免去重写它。

如果我们在浏览器中赋值 undefined, 因为他的writable是为false,所以我们的赋值没有生效,如下所示:

    // chrome Google Chrome 已是最新版本
    // 版本 75.0.3770.100(正式版本) (64 位)
    undefined = '111';
    undefined === undefined; // true

NaN

全局属性 NaN 的值表示不是一个数字(Not-A-Number),NaN是一种特殊的Number类型. NaN 属性的初始值就是 NaN,和 Number.NaN 的值一样。在现代浏览器中(ES5中),NaN 属性是一个不可配置(non-configurable),不可写(non-writable)的属性。但在ES3中,这个属性的值是可以被更改的,但是也应该避免覆盖。

NaN 属性的属性特性:
writable false
enumerable false
configurable false

如果我们在浏览器中赋值 NaN, 因为他的writable是为false,所以我们的赋值没有生效,如下所示:

    // chrome Google Chrome 已是最新版本
    // 版本 75.0.3770.100(正式版本) (64 位)
    NaN = '111';
    NaN === NaN; // false

判断NaN 我们必须使用 Number.isNaN()isNaN() 函数和比较中不等于自己来判断是否为NaN,为什么NaN不等于NaN自己,这是因为NaN它使表示一个集合

    NaN == NaN; // false
    NaN === NaN; // false
    isNaN(NaN); // true
    Number.isNaN(NaN); // true

symbol

这个技术术语页面同时描述了一种称为 “symbol” 的数据类型,还有一个像类的函数 “Symbol()”,用来创建 symbol 数据类型实例。

数据类型 “symbol” 是一种原始数据类型,该类型的性质在于这个类型的值可以用来创建匿名的对象属性。该数据类型通常被用作一个对象属性的键值——当你想让它是私有的时候。 Symbol 是 JavaScript 的 原始数据类型 ,Symbol实例是唯一且不可改变的. 符号类型是唯一的并且是不可修改的, 并且也可以用来作为Object的key的值(如下). 在某些语言当中也有类似的原子类型(Atoms).
我们只能通过Symbol('ssss')声明symbol,不能通过new声明Symbol.

引用类型

对象

在计算机科学中, 对象是指内存中的可以被 标识符引用的一块区域. 在 Javascript 里,对象可以被看作是一组属性的集合。用对象字面量语法来定义一个对象时,会自动初始化一组属性。 ECMAScript定义的对象中有两种属性:数据属性和访问器属性。

数据属性 数据属性的特性(Attributes of a data property)

特性 数据类型 描述 默认值
[[Value]] 任何Javascript类型 包含这个属性的数据值。 undefined
[[Writable]] Boolean 如果该值为 false,则该属性的 [[Value]] 特性 不能被改变。 true
[[Enumerable]] Boolean 如果该值为 true,则该属性可以用 for...in 循环来枚举。 true
[[Configurable]] Boolean 如果该值为 false,则该属性不能被删除,并且 除了 [[Value]] 和 [[Writable]] 以外的特性都不能被改变。 true

还有一些过时的数据属性Read-onlyDontEnumDontDelete 访问器属性

特性 数据类型 描述 默认值
[[Get]] 函数对象或者 undefined 该函数使用一个空的参数列表,能够在有权访问的情况下读取属性值。另见 get。 undefined
[[Set]] 函数对象或者 undefined 该函数有一个参数,用来写入属性值,另见 set。 undefined
[[Enumerable]] Boolean 如果该值为 true,则该属性可以用 for...in 循环来枚举。 true
[[Configurable]] Boolean 如果该值为 false,则该属性不能被删除,并且 除了 [[Value]] 和 [[Writable]] 以外的特性都不能被改变。 true
  • 注意:这些特性只有 JavaScript 引擎才用到,因此你不能直接访问它们。所以特性被放在两对方括号中,而不是一对。

"标准的" 对象, 和函数

对象 一个 Javascript 对象就是键和值之间的映射.。键是一个字符串(或者 Symbol) ,值可以是任意类型的值。 这使得对象非常符合 哈希表。 函数 函数是一个附带可被调用功能的常规对象。 日期 当你想要显示日期时,毋庸置疑,使用内建的 Date 对象。 数组和类型数组 数组是一种使用整数作为键(integer-key-ed)属性和长度(length)属性之间关联的常规对象。 Int8Array、Uint8Array、Uint8ClampedArray 、Int16Array、Uint16Array、Int32Array、Uint32Array、Float32Array、Float64Array 键控集: Maps, Sets, WeakMaps, WeakSets Map, Set, WeakMap, WeakSet

动态类型

JavaScript是一种弱类型或者说动态语言。这意味着我们不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。

    var one = 'one'; // one is a String now
    one = 456; // one is a Number now

总结

JavaScript是一种弱类型或者说动态语言。 JavaScript中的内置类型,七中类型中的又分为两大类:基本类型(值类型)和引用类型

基本类型有六种:nullnumberstringundefinedbooleansymbol 引用类型:object

所有基本类型的值都是不可改变的。但需要注意的是,基本类型本身和一个赋值为基本类型的变量的区别。

基本类型 是储存在栈中

引用类型 引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的位置,堆内存中的值地址引用保存在栈中,我们都是操作栈中的地址引用。

类型转换

简述

在JavaScript中关于类型转换的规则是非常混乱的,有强制转换(显示转换)隐式转换,并且转换的规则没有完整可参考的文档,只有当时的提议书。并且在隐式转换的时候会出现很多不可思议的bug.

类型转换发生在静态类型的语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时(runtime);

我们先来解释显示转换再来隐式转换。 如果想了解javaScript中的类型,或者if运算符转换规则的知识可以看看我其他的博客。

显示转换

  1. ToString
  2. ToNumber
  3. ToBoolean

首先介绍显示转换中的基本类型互转,字符串、数字、布尔值、null、undefined之间的转换。

隐式转换

  1. ToPrimitive(转换为原始值)
  2. valueOf(返回指定对象的原始值)
  3. 运算符中的转换(+、-、*、/)
  4. ==

再来看一下ToPrimitive把对象转为原始值,当然也有Object.prototype.toString()的介绍,再试valueOf返回对象的原始值,再最后就是运算符在基本类型和object之间的运算,其中最坑的应该是==的隐士转换,因为他的左右规则不同,左右类型不同执行的又不同。

ToPrimitive的规则

我们要用到的ToPrimitive的规则,因为显示转换也会用到ToPrimitive的规则,如果还想所有的转换规则请看.ecma规则 ToPrimitive(转换为原始值) ToPrimitive 运算符接受一个值,和一个可选的 期望类型作参数。

/**
* @params obj 要转换的对象 ,required
* @params type 期望转换为的原始数据类型, 
*/
ToPrimitive(obj, type);

根据type的不同会他后面的步骤也不相同,如下所示

type为string

  1. 先调用obj的toString方法,如果返回原始值,则不往下执行,如果返回不是原始值,则执行第二步。
  2. 调用obj的valueOf方法,如果为原始值,则不往下执行,如果返回不是原值值,则执行第三步。
  3. 抛出TypeError异常

type为Number

  1. 先调用obj的valueOf方法,如果返回原始值,则不往下执行,如果返回不是原始值,则执行第二步。
  2. 调用obj的toString方法,如果为原始值,则不往下执行,如果返回不是原值值,则执行第三步。
  3. 抛出TypeError异常

type参数为空

  1. 该对象为Date,则type被设置为String
  2. 否则,type被设置为Number

这基本上就是object常用的转换的一些规则,在下面会验证ToPrimitive规则是否正确。

显示转换

我们先看一下基本类型之间的转换的图标如下:

Null Undefined Boolean(true) Boolean(false) Number String
Boolean false false - - (0/NAN)=>false OR true ('')=>false OR true
Number 0 NaN 1 0 - ('')=>false OR true
String 'null' 'undefined' 'true' 'false' 'Number' -

我们下面一个一个说明ToStringToNumberToBoolean的大致规则。

ToString

抽象操作ToString负责处理非字符串到字符串的强制类型转换。 验证一下上面表格中的转换规则,验证的时候加上object的转换验证,代码如下:

console.log(String(null)); // 'null'
console.log(String(undefined)); // 'undefined'
console.log(String(true)); // 'true'
console.log(String(false)); // 'false'
console.log(String(1)); // '1'
console.log(String(0)); // '0'
console.log(String(NaN)); // 'NaN'
console.log(String([1, 2, 3])); // '1,2,3' or [].toString()
console.log(String({a: 123})); // [object Object]
console.log(String(new Date())); // 'Sat Apr 20 2019 00:00:00 GMT+0800 (中国标准时间)'

上面的代码证明了我们上面table中的基本类型的·String·转换规则。

注意:对象这里要先转换为原始值,调用ToPrimitive转换,type就指定为string了,继续回到ToPrimitive进行转换(看ToPrimitive)。

ToNumber

抽象操作ToNumber负责处理非NumberNumber的强制类型转换。 验证一下上面表格中的转换规则,验证的时候加上object的转换验证,代码如下:

console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number('1')); // 1
console.log(Number('0')); // 0
console.log(Number('abcd')); // 'NaN'
console.log(Number([1, 2, 3])); // NaN
console.log(Number({a: 123})); // NaN
console.log(Number(new Date())); // 1555689600000

上面的代码证明了我们上面table中的基本类型的Number转换规则。

注意:对象这里要先转换为原始值,调用ToPrimitive转换,type就指定为number了,继续回到ToPrimitive进行转换(看ToPrimitive)。

ToBoolean

抽象操作ToBoolean负责处理非BooleanBoolean的强制类型转换。 验证一下上面表格中的转换规则,验证的时候加上object的转换验证,代码如下:

console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean('true')); // true
console.log(Boolean('false')); // true
console.log(Boolean('')); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(1)); // true
console.log(Boolean(0)); // false
console.log(Boolean('abcd')); // true
console.log(Boolean([1, 2, 3])); // true
console.log(Boolean({a: 123})); // true
console.log(Boolean([])); // true
console.log(Boolean({})); // true
console.log(Boolean(new Date())); // 1555689600000

在表格上只有基本类型的转换为Boolean的规则,具体的转换规则为:

  • false
  • null
  • undefined
  • 空字符串' '
  • 数字零 0
  • NaN

只有上面的会转为false,其他的都会转为true. 他和if()条件运算符转换的规则基本上一至,请看if运算符转换

隐士转换

在上面已经列出了ToPrimitive的常用的转换规则,在下面会验证上面的规则是否正确。最坑的也是规则对多的就是运算符的转换,可能现在只知道一部分,后面会慢慢补全。

ToPrimitive验证

Object转String规则验证 首先验证Obejct转换为String的规则,直接上代码。

var obj = {
  toString: function () { console.log('toString'); return {}; },
  valueOf: function () { console.log('valueOf'); return {};}
};
String(obj);
// 1. toString 
// 2. valueOf
// Uncaught TypeError: Cannot convert object to primitive value 

根据上面的输出结果,证明上面的String(),走了ToPrimitive(obj, string)的type为string的规则。详情见上面。

Object转Number规则验证 再验证Obejct转换为Number的规则,直接上代码。

var obj = {
  toString: function () { console.log('toString'); return {}; },
  valueOf: function () { console.log('valueOf'); return {};}
};
Number(obj);
// 1. valueOf
// 2. toString
// Uncaught TypeError: Cannot convert object to primitive value 

根据上面的输出结果,证明上面的Number(),走了ToPrimitive(obj, number)的type为number的规则。详情见上面。

其他转变如数组转String或者Number 再验证Obejct转换为Number的规则,直接上代码。

var obj = {
  toString: function () { console.log('toString'); return {}; },
  valueOf: function () { console.log('valueOf'); return {};}
};
Number(obj);
// 1. valueOf
// 2. toString
// Uncaught TypeError: Cannot convert object to primitive value 

根据上面的输出结果,证明上面的Number(),走了ToPrimitive(obj, number)的type为number的规则。详情见上面。

valueOf

JavaScript 调用 valueOf() 方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是我们很少需要自己调用此函数,valueOf 方法一般都会被 JavaScript 自动调用。

  • String => 返回字符串值
  • Number => 返回数字值
  • Boolean => 返回Boolean的this值
  • Object => 返回this
  • Date => 返回一个数字,即时间值,字符串中内容是依赖于具体实现的

代码如下:

console.log('abc'.valueOf()); // 'abc'
console.log(Number(123).valueOf()); // 123
console.log(Boolean(true).valueOf()); // true
console.log({a: 123}.valueOf()) // {a: 123}
console.log(new Date()); // Sat Apr 20 2019 00:00:00 GMT+0800 (中国标准时间)

运算符中的转换(+、-、*、/)

在运算符中的转换又分为两种,自动转换为string类型、自动转换为number类型。

自动转换为string类型

在基础类型中,当一个值为字符串,另一个值非字符串,则后者转为字符串,当有一个是对象时,会走ToPrimitive(obj, number)ToPrimitive转换请看上面,下面看代码。

console.log('a' + 1); // 'a1'
console.log('a' + true); // 'atrue'
console.log('a' + false); // 'afalse'
console.log('a' + undefined); // 'aundefined'
console.log('a' + null); // 'anull'
console.log('a' + []); // 'a'
console.log('a' + {toString: function () { return '1' }}); // 'a1'
console.log('a' + {valueOf: function () { return 1 }}); // 'a1'

自动转换为number类型

加法运算符,如果没有一个为string类型的时候,都会优先转换为Number类型,如果有一个为object时,会走ToPrimitive(obj, number)ToPrimitive转换请看上面。一元运算符,也是需要注意。下面看代码。

console.log(true + false); // 1
console.log(true + null); // 1
console.log(true + undefined); // NaN
console.log(true + []); // ‘true’
console.log(true + null); // 1
console.log(true - false); // 1
console.log(true - true); // 0
console.log('1' - '0'); // 1
console.log('1' * 0); // 0
console.log('0' * 1); // 0
console.log('1' - '0'); // 1
console.log('abc' - 0); // NaN
console.log('abc' - '0'); // NaN
console.log(null - 0); // 0
console.log('1' - 0); //NaN
console.log(1 - {valueOf: function () { return 1 }}); // 0
console.log(1 - {valueOf: function () { return {} }, toString: function () { return 1 }}); // 0
// 一元运算符
console.log(+'abc'); // NaN
console.log(-'abc'); // NaN
console.log(+'1'); // 1
console.log(-'1'); // -1
console.log(+true); // 1
console.log(-false); // -0
console.log(+true); // 1

注意:null转为数值时为0,而undefined转为数值时为NaN

==

== 抽象相等比较与+运算符不同,不再是String优先,而是Nuber优先。假设左面为x、y为右面,大概的规则如下。

  1. 如果x,y都为number,直接比较
  2. 如果x为string,y为number,x转换为number比较,反则相反。
  3. 如果存在对象,通过ToPrimitive(obj, number)type为number进行转换,再进行比较。
  4. 如果x,y有一方存在boolean,按照ToNumber将boolean转换为1或0,再进行比较。

验证代码如下:

// x,y 都number 比较
console.log(1 == 2) // false
console.log(1 == 1) // true

// 一方为string一方为number
console.log('1' == 2) // false
console.log(1 == '1') // true

// 如果一方为 对象
console.log(null == 2) // false
console.log(1 == undefined) // false
// 因为对象的规则比较繁杂,如果普通对象[] 和 ![] 规则不同请看下面文章

// 存在 booelan
console.log(true == 0); // false
console.log(true == '0'); // false
console.log(false == '0'); // true

在这里就不多赘述了,看我另一篇文章 ![] == [],通过一道面试题。来讲解基本的转换规则,因为这个规则其实挺复杂的,一两句话讲不清楚。

总结

JavaScript中的类型转换基本上分为显示转换、隐式转换

显示转换

  1. ToString
  2. ToNumber
  3. ToBoolean

隐式转换

  1. ToPrimitive(转换为原始值)
  2. valueOf(返回指定对象的原始值)
  3. 运算符中的转换(+、-、*、/)
  4. ==

IF 转换规则

简介

在 JavaScript 中使用 if 的时候,自己如果不注意的话很可能出现判断进错,其实在 JavaScript 中只有固定的几个值会转为 false,其它的统一认为为 true。

  • false
  • null
  • undefined
  • 空字符串' '
  • 数字零 0
  • NaN

其他的全部都算为 true,'false'、'0'也是为 true,其实这也是一种隐性的类型转换。和 == 又有不同。

逻辑运算符

由于逻辑表达式是从左往右计算的,由于运算符优先级的存在,下面的表达式的结果却不相同。如下例所示

(false && true) || true; // 结果为 true
false && (true || true); // 结果为 false

右侧被小括号括起来的操作变成了独立的表达式。 转换规则:

逻辑与(&&)

尽管 &&|| 运算符能够使用非布尔值的操作数, 但它们依然被看作是布尔操作符,因为它们的返回值总是能够被转换为布尔值。 expr1 && expr2 如果expr1能转换为false则返回expr1,否则返回expr2。因此,与布尔值一起使用时,如果两个操作数都为true时&&返回true,否则返回false.

a1 = true && true; // t && t 结果为 true
a2 = true && false; // t && f 结果为 false
a3 = false && true; // f && t 结果为 false
a4 = false && 3 == 4; // f && f 结果为 false
a5 = "Cat" && "Dog"; // t && t 结果为 Dog
a6 = false && "Cat"; // f && t 结果为 false
a7 = "Cat" && false; // t && f 结果为 false

逻辑与(||)

expr1 && expr2 如果expr1能转换为true则返回expr1,否则返回expr2。因此,与布尔值一起使用时,如果任意一个操作数为true时||返回true.

o1 = true || true; // t || t 结果为 true
o2 = false || true; // f || t 结果为 true
o3 = true || false; // t || f 结果为 true
o4 = false || 3 == 4; // f || f 结果为 false
o5 = "Cat" || "Dog"; // t || t 结果为 Cat
o6 = false || "Cat"; // f || t 结果为 Cat
o7 = "Cat" || false; // t || f 结果为 Cat

逻辑非(!)

!expr 如果单个表达式能转换为true的话返回false,否则返回true。

n1 = !true; // !t 结果为 false
n2 = !false; // !f 结果为 true
n3 = !"Cat"; // !t 结果为 false

总结

在 JavaScript 中使用 if 的时候,自己如果不注意的话很可能出现判断进错,其实在 JavaScript 中只有固定的几个值会转为 false,其它的统一认为为 true。

  • false
  • null
  • undefined
  • 空字符串' '
  • 数字零 0
  • NaN

== 混乱的转换规则

简介

JavaScript 有两种比较方式:严格比较运算符转换类型比较运算符。对于严格比较运算符(===)来说,仅当两个操作数的类型相同且值相等true,而对于被广泛使用的比较运算符(==)来说,会在进行比较之前,将两个操作数转换成相同的类型

比较的特点:

  • 对于两个拥有相同字符顺序,相同长度,并且每个字符的位置都匹配的字符串,应该使用严格比较运算符。
  • 对于两个数值相同的数字应该使用严格比较运算符,NaN和任何值不相等,包括其自身,正数零等于负数零。
  • 对于两个同为true或同为false的布尔操作数,应使用严格比较运算符。
  • 不要使用严格比较运算符或比较运算符来比较两个不相等的对象。
  • 当比较一个表达式和一个对象时,仅当两个操作数引用相同的对象(指针指向相同对象)。
  • 对于Null 和 Undefined 类型而言,应使用严格比较运算符比较其自身,使用比较运算符进行互相比较。

一致/严格相等 (===)

一致运算符不会进行类型转换,仅当操作数严格相等时返回true

相等(==)

比较操作符会为两个不同类型的操作数转换类型,然后进行严格比较。当两个操作数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同

非严格比较操作符 == 是会做强制类型转换的,那么根据 ECMA 262 它的规则是:

图1-1

! ==
ToPrimitive: 图1-2
! ==
图1-3
! ==

图1-4

! ==
ToBoolean:

图1-5

! ==
图1-6

! ==

ecma规则

[] == false or ![] == [] or ![] == false 为true

mdn运算符优先级参考表

! ==

! ==

==的优先级16 !的优先级10

[] == false 结果为 true

根据图1-1可知

  • 第 7 条:If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
  • 第 9 条:If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.

所以 [] == false 的比较是对 x 执行 ToPrimitive(x),然后和 ToNumber(false) (为 0)进行比较。

看一下 ToPrimitive: 根据上图1-2、1-3、1-4的规则对于ToPrimitive([]),先执行[].valueOf(),返回result的是'[]',因为Type(result)是Object,所以继续执行[].toString(),返回””。 因此实际上最终是比较"" == 0,结果为true

![] == [] 结果为 true

按照优先级,先执行 ![],根据规范,实际上是 !(ToBoolean([])): 根据上图1-5、1-6可看出,实际上 ToBoolean([])returntrue, ![] 就是 false. [] 上文已经讲过了 是 ""。 所以对比就是 false == "",结果为true

![] == false 结果为 true

按照优先级,先执行 ![],根据规范,实际上是 !(ToBoolean([])): 根据上图可看出,实际上 ToBoolean([]) 会return出true, ![] 就是 false. false == false ,结果为true

!![] == false 结果为 false

按照优先级,先执行 !![],根据规范,实际上是 !!(ToBoolean([])): 根据上图可看出,实际上 ToBoolean([])会return出true, !![] 就是 true. true == false ,结果为 false

总结

基本上一个对象转为原始值的大致过程再进行对比,如果不太清楚可以看另一篇博客JavaScript数据类型(二) 类型转换,但是在本篇博客中最重要的时ToBoolean([])的转换比较不容易理解。

常见的面试题

简述

在另一篇文章中有表述大致显示转化规则隐式转换规则,着篇文章中会收集一些前端比较经典的关于类型转换的问题。下面就开始比较经典的面试题。

![] == []

这个题在另一篇面试中有记录过,![] == [].

(a == 1 && a == 2 && a == 3)

这个真的有很多种办法,这里只记录我知道的方法,如果有好的方法,请在下方留言,大家一起进步。 首先要知道==和===的区别。

== 宽松匹配 ==会先将左右两两边的值转化成相同的原始类型,然后再去比较他们是否相等。 === 他是不转化直接比较,如果类型不同直接就是false,如果类型相同,原始值相同就为true

这里主要讲解(a == 1 && a == 2 && a == 3)相等的多种解法,如下:

  1. 重写ObjectvalueOf
  2. 重写ObjecttoString
  3. 重写ToPrimitivees6 smybol('')
  4. 通过劫持 obj 的getter方法
  5. 数组join、shift
  6. 字符串骚操作

重写 Object 的 valueOf、重写 Object 的 toString

重写代码的 valueOf 方法、和 toString 方法,如果不知道 ToPrimitive(obj, type)规则,请看ToPrimitive 规则我的另一篇博客。代码如下:

const a = { value: 0 };
a.valueOf = function() {
  return (this.value += 1);
};
console.log(a == 1 && a == 2 && a == 3); // true

const a1 = { value: 0 };
a1.valueOf = function() {
  return {};
};
a1.toString = function() {
  return (this.value += 1);
};
console.log(a1 == 1 && a1 == 2 && a1 == 3); // true

重写 ToPrimitive,es6 smybol('')

在 ES6 之后,还允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为,得到我们想要的结果。看代码:

const a = { value: 0 };
a[Symbol.toPrimitive] = function() {
  return (this.value += 1);
};
console.log(a == 1 && a == 2 && a == 3); // true

Obj.defineProperty

通过劫持对象的属性值的 getter 操作,让他累加来做到我们想要的。

var value = 0;
Object.defineProperty(window, "a", {
  get: function() {
    return (this.value += 1);
  }
});
console.log(a == 1 && a == 2 && a == 3); // true

通过数组的方式实现

通过改变数组的 join 方法实现

var a = [1, 2, 3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3); // true

字符串的骚操作

通过定义变量是包含空格,实现视觉上的相等。

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
console.log(aᅠ == 1 && a == 2 && ᅠa == 3);

上面就是知道的可以让(aᅠ == 1 && a == 2 && ᅠa== 3 )的六种方法,如果还有后面会接着补充,下面来说一下(a === 1 && a === 2 && a === 3)。

(a === 1 && a === 2 && a === 3)

在上面介绍过==与===的区别, ===是不会进行类型转换的,所以==的很多规则并不适用,那只有劫持方法字符串的操作可以实现,其他方法都不能实现。

Obj.defineProperty

通过劫持对象的属性值的 getter 操作,让他累加来做到我们想要的。

var value = 0;
Object.defineProperty(window, "a", {
  get: function() {
    return (this.value += 1);
  }
});
console.log(a === 1 && a === 2 && a === 3); // true

字符串的骚操作

通过定义变量是包含空格,实现视觉上的相等。

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
console.log(aᅠ === 1 && a === 2 && ᅠa === 3);

未完待续。。。。如有经典试题可评论

参考

经常被面试官考的JavaScript数据类型知识你真的懂吗?

数据类型转换

深入理解JS的类型、值、类型转换 #34