正文之前,先抛出几组问题:
// 第一组
[] == [] //false
// 第二组
[] == ![] //true
{} == !{} //false
{} == ![] // false
[] == !{} //true
{} == 1 // false
// 第三组
{} < 1 // false
{} > 1 // false
看到这几个问题,是不是一脸懵逼?
稍微有点基础的同学,应该一眼就能看出 [] == [] 输出 false,因为 Object 是引用类型,两个引用类型做 == 比较,如果它们引用的是同一个地址,输出 true,否则输出 false。但是后面几道题可能会有一点点麻烦。
后面几道题都涉及到 JavaScript 中的一个难点:隐式转换。本文将会带领大家深入了解 JavaScript 中的类型转换机制。
js数据类型分为两大类:
2.1 ToPrimitive ( input [, PreferredType] ) 转换为原始值
抽象操作 ToPrimitive(input[, PreferredType]),将input参数转换为一个非对象类型的值,即原始值类型。转换规则如下:
当 Type(input) 为 Object 时,可以将抽象操作 ToPrimitive(input[, PreferredType]) 的执行过程用如下代码解释:
// 仿抽象操作 ToPrimitive(input[, PreferredType]) 的执行过程
function ToPrimitive(input: Object, PreferredType: undefined | 'String' | 'Number') {
let hint;
// 如果没有送 PreferredType ,hint 为 'default'
// 如果 PreferredType 为 'String', hint 为 'string'
// 如果 PreferredType 为 'Number', hint 为 'number'
if(!PreferredType) {
hint = 'default';
} else if (PreferredType === 'String') {
hint = 'string';
} else if(PreferredType === 'Number') {
hint = 'number';
}
// 获取对象的 @@toPrimitive 方法,如果对象自身没有,会一直顺着原型链查找
let exoticToPrim = GetMethod(input, Symbol.toPrimitive);
// 如果该方法不为undefined,用对象调用该方法,赋值给result
// 如果 result 不是 Object 类型,返回 result;否则抛出 TypeError 异常
if(exoticToPrim !== undefined) {
let result = exoticToPrim.call(input, hint);
if (typeof result !== 'object') {
return result;
}
throw new TypeError();
}
// 如果该方法为undefined
// 如果 hint 为 'default',令 hint 为 'number'
// 返回 抽象操作 OrdinaryToPrimitive 的调用结果
if(hint === 'default'){
hint = 'number';
}
return OrdinaryToPrimitive(input,hint);
}
这里涉及到另一个抽象操作 GetMethod,我们先看一下ECMAScript 6 规范中对 GetMethod 的定义:
简单说来就是: 抽象操作 GetMethod(O,P) 获取对象 O 的 P 属性,如果 该属性是 undefined,返回 undefined;如果该属性是一个函数,返回此函数;否则抛出一个 TypeError 异常。
下面我们看一下 抽象操作 OrdinaryToPrimitive 的过程:
// 仿抽象操作 OrdinaryToPrimitive(O, hint) 的执行过程
function OrdinaryToPrimitive(O: Object, hint: 'string' | 'number') {
// 假定 hint 是一个字符串,并且其值只能是'string' 或 'number'
// 如果 hint === 'string',令 methodNames = ['toString', 'valueOf']
// 如果 hint === 'number',令 methodNames = ['valueOf', 'toString']
let methodNames;
if(hint === 'string') {
methodNames = ['toString', 'valueOf'];
} else {
methodNames = ['valueOf', 'toString'];
}
// 遍历 methodNames,获取对象的方法,赋值给 result
// 如果 result 不是 Object,终止遍历,并返回 result
// 抛出一个 TypeError 异常
for (let item of methodNames) {
let method = O[item];
let result = method.call(O);
if(typeof(result) !== 'object') {
return result;
}
}
throw new TypeError();
}
Date 对象和 Symbol 对象的原型上已经部署了 [@@toPrimitive] 方法,这个方法是不可枚举(enumerable: false),不可改写的(writable: false)。对于Date对象原型上的[@@toPrimitive] 方法,如果没有送hint,会将hint当作'string'。
我们可以使用 Symbol.toPrimitive 来给 Object 添加 [@@toPrimitive] 方法:
Object.prototype[Symbol.toPrimitive] = function(hint) {
if(hint === 'default') {
let thisType = Object.prototype.toString.call(this);
if(thisType === '[object Date]') {
hint = 'string';
} else {
hint = 'number';
}
}
let methodNames;
if(hint === 'string') {
methodNames = ['toString', 'valueOf'];
} else if(hint === 'number') {
methodNames = ['valueOf', 'toString'];
} else {
throw new TypeError('Invalid hint: ' + hint);
}
for (let key of methodNames) {
let method = this[key];
let result = method.call(this);
if(typeof(result) !== 'object') {
return result;
}
}
throw new TypeError();
}
2.1.1 对象的 valueOf() 方法和 toString() 方法
对象在执行 ToPrimitive 转换时,需要用到对象的valueOf()和toString()方法。我们可以在Object.prototype上找到这两个方法。在JavaScript中,Object.prototype是所有对象原型链的顶层原型,因此,任何对象都有valueOf()和toString()方法。
JavaScript的许多内置对象都重写了这两个方法,以实现更适合自身的功能需要。
不同类型对象的valueOf()方法的返回值:
不同类型对象的toString()方法的返回值:
2.2 ToBoolean 转换为布尔值类型
抽象操作 ToBoolean 根据下列规则将其参数转换为布尔值类型的值:
2.3 ToNumber 转换为数值类型
抽象操作 ToNumber 根据下列规则将其参数转换为数值类型的值:
2.3.1 对字符串类型应用 ToNumber
对字符串应用 ToNumber 时,如果符合如下规则,转为数值:
如果字符串不符合上述规则,将转为NaN。
2.4 ToString 转为字符串类型
抽象操作 ToString 根据下列规则将其参数转换为字符串类型的值:
2.4.1 对数值类型应用 ToString
抽象操作 ToString 运算符将数字 m 转换为字符串格式的给出如下所示:
2.5,抽象操作 GetValue
先看一下 ECMAScript 规范中定义的 GetValue 方法:
注意区分这一句:2. If Type(V) is not Reference, return V. 中的 Reference 和我们平时说的 引用类型 的区别。
我们平时说的 引用类型 指的是 ECMAScript 规范中 语言类型的 Object 类型(例如 Object, Array, Date 等);而这里的 Reference 指的是ECMAScript 规范中 规范类型的 Reference 类型 ,是一个抽象的概念。
按规范的描述,Reference 是一个 name binding,由三部分组成:
举个例子:赋值语句 let obj.a = 1 中的 obj.a 产生的 Reference,base 是 obj,referreference name 是 'b',至于 strict mode flag 是用来检测是否处于严格模式。
Reference 和 环境记录(Environment Record) 这些概念是为了更好地描述语言的底层行为逻辑才存在的,并不存在于我们实际的 js 代码中。
逻辑非运算符(!) 按下列过程将表达式转换为布尔值
因此,逻辑非运算符(!)可以当作是:对 ToBoolean 操作的结果取反。
比较运算 x==y,按如下规则进行:
1,若 Type(x) 与 Type(y) 相同, 则
1) 若 Type(x) 为 Undefined, 返回 true。
2) 若 Type(x) 为 Null, 返回 true。
3) 若 Type(x) 为 Number, 则
(1)、若 x 为 NaN, 返回 false。
(2)、若 y 为 NaN, 返回 false。
(3)、若 x 与 y 为相等数值, 返回 true。
(4)、若 x 为 +0 且 y 为 −0, 返回 true。
(5)、若 x 为 −0 且 y 为 +0, 返回 true。
(6)、返回 false。
4) 若 Type(x) 为 String,则当 x 和 y 为完全相同的字符序列时返回 true。 否则,返回 false。
5) 若 Type(x) 为 Boolean,当 x 和 y 为同为 true 或者同为 false 时返回 true。 否则, 返回 false。
6) 若 Type(x) 为 Symbol,如果 x 和 y 是同一个 Symbol,返回true。否则,返回 false。
7) 若 Type(x) 为 Object,当 x 和 y 是对同一对象的引用时返回 true。否则,返回 false。
2,若 x 为 null 且 y 为 undefined,返回 true。
3,若 x 为 undefined 且 y 为 null,返回 true。
4,若 Type(x) 为 Number 且 Type(y) 为 String,返回 x == ToNumber(y)的结果。
5,若 Type(x) 为 String 且 Type(y) 为 Number,返回 ToNumber(x) == y的结果。
6,若 Type(x) 为 Boolean, 返回 ToNumber(x) == y 的结果。
7,若 Type(y) 为 Boolean, 返回 x == ToNumber(y) 的结果。
8、若 Type(x) 为 String 或 Number 或 Symbol,且 Type(y) 为 Object,返回 x == ToPrimitive(y) 的结果。
9、若 Type(x) 为 Object 且 Type(y) 为 String 或 Number 或 Symbol, 返回 ToPrimitive(x) == y 的结果。
10、返回 false。
现在,我们来分析一下文章开头提出的问题:[] == ![] // true
其他几道题我就不一一分析了,有兴趣的同学们可以自己分析验证。提示一下,需要注意 Object.prototype.toString 和 Array.prototype.toString 的区别
比较运算 x < y,按照如下规则执行
1,令 px = ToPrimitive(x),令 py = ToPrimitive(y)。
2,如果 Type(px) 和 Type(py) 都是 String,则
1)、如果 py 是 px 的前缀,返回 false。
2)、如果 px 是 py 的前缀,返回 true。
3)、找出 px 和 py 中 相同下标处第一个不同的字符串单元,将其 词典排序 分别记为 m 和 n。
4)、如果 m < n,返回 true,否则,返回 false。
3,令 nx = ToNumber(px),令 ny = ToNumber(py)
1)、如果 Type(nx) 是 NaN,返回 false。
2)、如果 Type(ny) 是 NaN,返回 false。
3)、如果 nx 是 +0,ny 是 -0,返回 true。
4)、如果 nx 是 -0,ny 是 +0,返回 true。
5)、如果 nx 是 +Infinity,返回 false。
6)、如果 ny 是 +Infinity,返回 true 。
7)、如果 ny 是 -Infinity,返回 false。
8)、如果 nx 是 -Infinity,返回 true。
9)、如果 nx 的数学值小于 ny 的数学值,返回 true,否则返回 false。
在JavaScript中存在这样两种原始类型:Null与Undefined。这两种类型常常会使JavaScript的开发人员产生疑惑,在什么时候是Null,什么时候又是Undefined?Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。
主要介绍了JS中检测数据类型的几种方式,typeof运算符用于判断对象的类型,但是对于一些创建的对象,它们都会返回\'object\',有时我们需要判断该实例是否为某个对象的实例,那么这个时候需要用到instanceof运算符
对于object和number、string、boolean之间的转换关系,ToPrimitive是指转换为js内部的原始值,如果是非原始值则转为原始值,调用valueOf()和toString()来实现。
Undefined类型表示未定义,它的类型只有一个值为undefined。undefined和null有一定的表意差别。非整数的Number类型无法使用 == 或 === 来比较,因为 JS 是弱类型语言,所以类型转换发生非常频繁
近在做项目代码重构,其中有一个要求是为代码添加智能提示和类型检查。智能提示,英文为 IntelliSense,能为开发者提供代码智能补全、悬浮提示、跳转定义等功能,帮助其正确并且快速完成编码。
基本类型:按值访问,可以操作保存在变量中实际的值;引用类型数据存在堆内存,而引用存在栈区,也就是说引用类型同时保存在栈区和堆区,关于==的执行机制,ECMASript有规范,因为==前后的值交换顺序,返回的值也是一样的,所以在此对规范做出如下总结
JavaScript 是一种弱类型或者说动态类型语言。所以你不用提前声明变量的类型,在程序运行时,类型会被自动确定,你也可以使用同一个变量保存不同类型的数据。
js的值传递和引用(地址)传递:js的5种基本数据类型 number,string,null,undefined,boolean 在赋值传递时是值传递,js的引用数据类型(object,array,function)进行引用传递,其实底层都是对象。
JS中所有的值都可以转换成布尔类型 使用Boolean()或者 !!(两个感叹号),JS中所有的值都可以转换成数字类型,使用Number()或+。数字类型转换场景目的只有一个,用于计算,将后台传递的数据,从字符串转换为数字并参与计算
众所周知,JS在很多情况下会进行强制类型转换,其中,最常见两种是:1.使用非严格相等进行比较,对==左边的值进行类型转换2.在if判断时,括号内的值进行类型转换,转化为布尔值
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!