ES5中部分操作类型转换小抄

JS 在执行过程中会发生类型隐式转换,这里抄了 ECMA 规范的『+』和『==』运算符的解释

ECMA 规范

加法运算符(+)

https://www.w3.org/html/ig/zh/wiki/ES5/%E8%A1%A8%E8%BE%BE%E5%BC%8F#.E5.8A.A0.E6.B3.95.E8.BF.90.E7.AE.97.E7.AC.A6.EF.BC.88.2B.EF.BC.89

加法运算符启动字符串相接或是数值相加。

产生式 AdditiveExpression : AdditiveExpression + MultiplicativeExpression 按照下面的过程执行:

  1. lref 为解释执行 AdditiveExpression 的结果。
  2. lvalGetValue(lref)
  3. rref 为解释执行 MultiplicativeExpression 的结果。
  4. rvalGetValue(rref)。
  5. lprimToPrimitive(lval)。
  6. rprimToPrimitive(rval)。
  7. 如果 Type(lprim) 为 String 或者 Type(rprim) 为 String,则:
    1. 返回由 ToString(lprim) 和 ToString(rprim) 连接而成的字符串。
  8. 返回将加法运算作用于 ToNumber(lprim) 和 ToNumber(rprim) 的结果。参见 11.6.3 后的注解。

注:在 第5步第6步ToPrimitive 的调用没有提供暗示类型。所有除了 Date 对象的 ECMAScript 原生对象会在没有提示的时候假设提示是 NumberDate 对象会假设提示是 String。宿主对象可以假设提示是任何东西。

抽象相等比较算法

https://www.w3.org/html/ig/zh/wiki/ES5/%E8%A1%A8%E8%BE%BE%E5%BC%8F#.E6.8A.BD.E8.B1.A1.E7.9B.B8.E7.AD.89.E6.AF.94.E8.BE.83.E7.AE.97.E6.B3.95

xy 为值进行 x == y 比较会产生的结果可为 truefalse。比较的执行步骤如下:

  1. Type(x) 与 Type(y) 相同, 则
    1. Type(x) 为 Undefined, 返回 true
    2. Type(x)为 Null, 返回 true
    3. Type(x)为 Number,则
      1. xNaN,返回 false
      2. yNaN,返回 false
      3. xy 为相等数值,返回 true
      4. x+0y−0,返回 true
      5. x−0y+0,返回 true
      6. 返回 false
    4. Type(x) 为 String,则当 xy 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true。否则,返回 false
    5. Type(x) 为 Boolean,当 xy 为同为 true 或者同为 false 时返回 true。否则,返回 false
    6. xy 为引用同一对象时返回 true。否则,返回 false
  2. xnull 且 y 为 undefined,返回 true
  3. xundefined 且 y 为 null,返回 true
  4. Type(x) 为 NumberType(y) 为 String,返回 x == ToNumber(y) 的结果。
  5. Type(x) 为 StringType(y) 为 Number,返回比较 ToNumber(x) == y 的结果。
  6. Type(x) 为 Boolean,返回比较 ToNumber(x) == y 的结果。
  7. Type(y) 为 Boolean,返回比较 x == ToNumber(y) 的结果。
  8. Type(x) 为 StringNumber,且 Type(y) 为 Object,返回比较 x == ToPrimitive(y) 的结果。
  9. Type(x) 为 ObjectType(y) 为 StringNumber,返回比较 ToPrimitive(x) == y 的结果。
  10. 返回 false

注:根据上述等于的定义:

  • 字符串比较可以以:"" + a == "" + b 硬性触发。
  • 数值比较可以以:+a == +b 硬性触发。
  • 布尔比较可以以:!a == !b 硬性触发。

注:等于运算符有以下的不变量:

  • A != B!(A == B) 相等。
  • 除了 AB 的执行顺序以外,A == BB == A 相等。

注:等于运算符不总是可传递。举例来说,两个代表相同 String 值但是不同的 String 对象会分别与 String==,但是两个对象间不相等。

注:String 的比较使用代码单元序列的简单等号比较。这里不打算使用更复杂的、语义化的字符或字符串序列,和 Unicode 规范的整理序列进行比较。因此,在 Unicode 标准中相等的 String 值可能在本算法中不相等。也就是,这个算法假定了所有字符串已经正规化。


理解学习

字符串/数字的转换规则

在将目标 O 转换为 StringNumber 类型时,有以下操作:

如果期望类型是 String,优先尝试调用 OtoString 方法,如果调用结果是基本类型,则将其转换为 String 并返回结果;如果 toString 方法不存在,或者返回结果不是基本类型,则继续尝试调用 OvalueOf 方法,如果结果是基本类型,则将其转换为 String 类型并返回结果,如果结果不是基本类型,则抛出错误。

转换为 Number 类型的过程与转换为 String 的过程基本一致,区别在于转换为 Number 是优先调用 OvalueOf 方法。

这里 O 是一个对象,需要注意的是基本类型的情况,基本类型没有方法属性,但是 JS 存在自动包装机制,会自动转换成对应的类型对象实例,如:(1).toString() 相当于 new Number(1).toString(),所以你没有办法重写一个基本类型变量的toStringvalueOf方法,只能重写它的原型对象的方法,例 a=1; a.toString(); 内部逻辑是 (new Number(a)).toString(),每次都会生成一个新对象

// 对象默认继承了Object,其拥有默认的`valueOf`及`toString`方法
var obj1 = {};
String(obj1); // -> [object Object]
Number(obj1); // -> NaN (相当于Number('[object Object]')),因为valueOf返回的结果不是基本类型,所以使用 toString 方法的返回值

// 覆盖了默认的 valueOf、toString 方法
var obj2 = {valueOf: () => 1, toString: () => 'a'};
String(obj2); // -> a
Number(obj2); // -> 1

[Symbol.toPrimitive] 属性

ES6 添加了新的基本类型: Symbol,Symbol 的 toPrimitive 静态属性可以用来给对象部署类型转换的方法,其调用优先级高于 valueOftoString ,且如果存在 [Symbol.toPrimitive] 属性,不论其执行结果如何,不会再调用 valueOf 及 toString 方法(即覆盖)。其执行逻辑与 valueOf 及 toString 类似,都是返回基本类型再转换成目标类型,否则报错。

Symbol.toPrimitive 属性方法有一个参数 type 表示期望类型,取值有 default,number,string

var obj = {
  [Symbol.toPrimitive](type){
    console.log(type);
    return type === 'number' ? 1 : 'toPrimitive';
 },
 valueOf(){return 2;},
 toString(){return 'toString';}
}

Number(obj); // -> 1
String(obj); // -> toPrimitive

type 有一个特殊的取值: default,在执行 加法运算符(+) 操作的时候,会进行这个类型的转换,就是上面 加法运算符(+) 规范的第 5、6 条所涉及的 ToPrimitive 操作,且在下面的注释里提到了:『没有提供暗示类型』,没有即取 default
所以上面的 obj 在执行 obj+1 的时候打印的信息是:

obj+1
// default
// toPrimitive1

扩展理解

在『加法运算符』规范的注释里还解释道:

所有除了 Date 对象的 ECMAScript 原生对象会在没有提示的时候假设提示是 Number;Date 对象会假设提示是 String。宿主对象可以假设提示是任何东西。

也就是说 ECMAScript 原生对象在执行 + 操作的时候,Date 会转换为String基本类型,而其他原生对象会转换为Number基本类型,而根据之前总结的『字符串/数字的转换规则』,换句话说,如果没有部署[Symbol.toPrimitive]方法,Date 就是先执行的 toString 方法, 其它原生对象先执行的 valueOf 方法。

d = new Date;
console.log(d + 1);
// 输出: 'Mon Feb 01 2021 19:22:55 GMT+0800 (中国标准时间)1'

// 重写 toString 方法
d.valueOf= () => 1;
d.toString = () => 2;
console.log(d + 1);
// 输出 3

// ------------

// 重写数组的valueOf 与 toString 方法
arr = [];
arr.valueOf = () => 1;
arr.toString = () => 'a';
console.log(a+1);
// 输出 2

确实是这样。再来创建个对象试一下

obj = {
  toString(){return 'a'},
  valueOf(){return 1}
}
console.log(obj+1);
// 输出 2
console.log(obj + '1');
// 输出 "11"

也遵循这一规则。

所以简单总结的个人理解就是:在『+』、『==』运算中对象隐式转换为基本类型的规则为 除了原生 Date 对象是优先调用 toString,其它对象优先调用 valueOf,前一个返回了一个基本类型的值,就会停止调用下一个

注. 这里没有把 toPrmitive 方法放进来一起讨论,如果有 toPrimitive,都是执行对象的 Stmbol.toPrimitive('default') 方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容