19 | a + b:动态类型是灾难之源还是最好的特性?(下)
19 | a + b:动态类型是灾难之源还是最好的特性?(下)
讲述:周爱民
时长20:04大小18.38M
集中精力办大事
步骤一
步骤二
步骤三:作为原始值处理
步骤四:进入“传统的类型转换逻辑”
解题 1:从对象到原始值
解题 2:“加号(+)”运算的戏分很多
解题 3:预期 vs. 非预期
其他
显式的 vs. 隐式的转换
字符串在“+”号中的优先权
Symbol.toPrimitive 的处理
结语与思考
赞 6
提建议
精选留言(16)
- weineel2019-12-27强行找到一种方法, 但和本节所讲没啥关系: Object.defineProperty(global, 'x', { get: function() { return Math.random(); } }) x === x // false展开
作者回复: 赞! 的确,这是除NaN之外我认为最可行的一个答案。事实上,这也是我在课程中提升“动态语言特性”这个方向的原因:一部分动态特性是基于OOP来实现的,这正是JavaScript的混合语言特性的应用。 不过这个例子其实可以变成更简单。例如: ``` Object.defineProperty(global, 'x', { get: Symbol }) // 或 Object.defineProperty(global, 'x', { get: Math.random }) ``` AND, @晓小东 给出的Symbol()方案对这个getter方法是一个很好的补充,很好地利用了“symbol总是唯一”的特性。
共 2 条评论10 - 陈强2020-04-08这里是一个拓展阅读:https://2ality.com/2012/01/object-plus-object.html 为什么{} + {} 在浏览器中打印的是 "[object Object][object Object]" 因为“the Node.js REPL parses its input differently from either Firefox or Chrome (which even uses the same V8 JavaScript engine as Node.js). The following input is parsed as expressions and the results are less surprising:” 实际上chrome自己加了console.log(),相当于({} + {}),firefox的控制台会输出NaN。 // test in chrome {} + {} // "[object Object][object Object]" {} + {} === "[object Object][object Object]" // false ({} + {}) === "[object Object][object Object]" // true展开
作者回复: 谢谢 👍+3
8 - Geek_185c7d2020-01-19请问老师,为什么{} + {} 在浏览器中打印的是 "[object Object][object Object]"
作者回复: 浏览器把引擎包了一层,你执行的不是真正的引擎环境。 在node中用如下命令行试试: ``` > node -p -e '{} + {}' NaN > node -p -e '{} + []' 0 ```
共 3 条评论4 - westfall2021-02-08关于 chrome 控制台 {} + {} 打印出 "[object Object][object Object]",找到一个解释,chrome 会将所有已 '{' 开头,已 '}' 结尾的输入强行用 () 包起来,因此输入 {} 得到的是一个对象字面量的求值 ({}),而不是一个语句块,同样 {} + {} 得到的是 ({} + {})。源码在此:https://chromium.googlesource.com/chromium/src.git/+/4fd348fdb9c0b3842829acdfb2b82c86dacd8e0a%5E!/#F23
- 晓小东2019-12-28难道是这个吗, 如果作为标识符var x 确实没想出。 >> Symbol() === Symbol() // false
作者回复: 参见 @sprinty 的答案。呵呵,我自己也不知道有没有更多的可能了。
2 - undefined2021-04-30> node -v v12.20.1 > node -p '{} + {}' NaN > node -p '({} + {})' [object Object][object Object] > node -p "eval('{} + {}')" NaN > node -p "eval('({} + {})')" [object Object][object Object]展开
作者回复: 这个?问题是? 1、3是按语句执行,所以是NaN;2、4是按表达式执行,所以是[object Object]*2。 正文中(解题2)说的都是第一种情况。而第二种情况的分析路径在正文中也是交待了的哦。
1 - wheatup2020-06-10和sprinty的方法类似,只不过避免了污染全局作用域: with ({ get x() { return Symbol() } }) { console.log(x === x); // false } 这个能成功的原因是这里的 x 其实是有 [withObj].x 取值的一个操作,然后利用getter返回不同的值造成的。 --- 真要说利用本节课学到的内容的话,可以把题目的 x === x 换成 x >= x,单纯的x === x是不会触发隐式类型转换的,而js在比较大小时一定会将对象转换成数值再进行比较,所以这里就会调用[Symbol.toPrimitive],这时候就可以重写这个方法做些手脚: let x = { [Symbol.toPrimitive]() { return Math.random(); } }; console.log(x >= x); // true和false都有可能展开
作者回复: +1
1 - 👣2022-03-25new Object === new Object
- 波哥2022-01-07老师您好,我在浏览器控制台,node环境控制台或者代码中:{} + {}的执行结果都是"[object Object][object Object]",但是教程中的结果是NaN,是不是还有其他理解方式?
作者回复: 评论中有解说过这个问题,是node/控制台包过一次。可以用用如下命令行试试: ``` > node -p -e '{} + {}' NaN > node -p -e '{} + []' 0 ```
1 - 油菜2020-11-19老师,Number([]) == Number([]) 是true,但[] == [] 是false,动态类型转换如何解释这个案例呢? ------------ var x = []; x === x // true; x === [] // false; [] === x // false; [] === [] //false;展开
作者回复: 对于`Number([])`来说,过程如下: ==== Number([]) => Number(ToNumber(ToPrimitive([]))) => Number(ToNumber([].valueOf())) => Number(ToNumber('')) => Number(0) => 0 ==== 注意这里的结果值是一个值类型,而不是对象类型,所以`typeof Number([])`的值将是"number"。所以,第一个式子等义于`0 == 0`,当然是true。 对于`[] == []`来说,两个对象实例的比较总是false。
- HoSalt2020-05-25「Date() 类中仍然是会调用 toString 或 valueOf 的,这是因为在它的Symbol.toPrimitive实现中仅是调整了两个方法的调用顺序,而之后仍然是调用原始的、内置的ToPrimitive()方法的」 调整顺序是值hint值为default的情况下?
作者回复: 是。如果hint不是default,那么就相当于指定顺序了。 https://tc39.es/ecma262/#sec-toprimitive
- K4SHIFZ2020-03-28自动分号插入并不会将{}+{} 变成{};+{} 吧?因为没有行结束符。
作者回复: 会的。:)
共 2 条评论 - 晓小东2019-12-27老师我测很多代码得出一个总结: 参与 + 或 - 运算 + - 只认那五种值类型数据, 从包装对象实例(String, Number, Boolean, Symbol),和数组Object 对象调用valueOf可以看出 只要valueOf 返回五种值类型数据, 就不会toString()方法, 反之如果还是对象类型,即使是包装对象实例,还是会调用toString方法 总结是: 在toPrimitive()中要获取五种值类型数据包括undefined 和 null, 大部分情况下引擎都是按数据值类型进行预测: 如下: {}; + [] String(1) + []; 1 - []; 都是valueOf -> toString 过程 最终在toPrimitivie() 中 根据 + 或者 - 以及运算符两侧值类型 调用相应String 或者Number 进行转换 求值 所以最终的结果只有三种 字符串、数值、和 NaN 直接toString就是在模板字符串中(目前发现这一种除了Date) `${x}` 测试代码如下 代码链接 https://repl.it/@XiaoDHuang/MadEuphoricTabs let valueOf = Array.prototype.valueOf let toString = Array.prototype.toString; Array.prototype.valueOf = function() { console.log(this.flag + " valueOf") return valueOf.call(this); } Array.prototype.toString = function() { console.log(this.flag + ' toString'); return toString.call(this); } var x = []; x.flag = '{}; + []'; {}; + x; x.flag = 'String(1) + []'; console.log(1 + x); x.flag = '1 - []'; console.log(1 - x); x.flag = '`${x}`' console.log(`${x}`);展开
作者回复: 是的呀。 > 总结是: 在toPrimitive()中要获取五种值类型数据包括undefined 和 null, ... ------ 在上一小节里不是讲过了么?原文是: > > 一种关于“原始值”的简单解释是:所有 5 种能放入私有槽(亦即是说它们有相应的包装类)的值(Values),都是原始值;并且,再加上两个特殊值 undefined 和 null,那么就是所谓原始值(Primitive values)的完整集合了。 > 只要valueOf 返回五种值类型数据, 就不会toString()方法, 反之如果还是对象类型,即使是包装对象实例,还是会调用toString方法... ---- 这在这一讲的“步骤4”中也讲到了。原文是: > > 这需要利用到对象的valueOf()和toString()方法:当预期是“number”时,valueOf()方法优先调用;否则就以toString()为优先。并且,重要的是,上面的预期只决定了上述的优先级,而当调用优先方法仍然得不到非对象值时,还会顺序调用另一方法。 最后,关于Date()类型中顺序相反的问题,本讲里也是解释了的哟哟哟哟~ ^^.
- Astrogladiator-埃蒂...2019-12-27https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness 看了下mdn,还真是只有NaN这么一种情况。 If Type(x) is different from Type(y), return false. If Type(x) is Number or BigInt, then Return ! Type(x)::equal(x, y). Return ! SameValueNonNumeric(x, y). Assert: Type(x) is not Number or BigInt. Assert: Type(x) is the same as Type(y). If Type(x) is Undefined, return true. If Type(x) is Null, return true. If Type(x) is String, then If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true; otherwise, return false. If Type(x) is Boolean, then If x and y are both true or both false, return true; otherwise, return false. If Type(x) is Symbol, then If x and y are both the same Symbol value, return true; otherwise, return false. If x and y are the same Object value, return true. Otherwise, return false. 按照https://tc39.es/ecma262/#sec-samevaluenonnumeric的说明测试了下也没有找到其他的可能 好奇这个答案是什么展开
作者回复: 绝对是还有的。至少一个。^^.
共 2 条评论 - 晓小东2019-12-27老师这个“其中的 boolean 是通过查表来进行的“ 这个查表该如何理解???
作者回复: Here: https://tc39.es/ecma262/#sec-toboolean
- 潇潇雨歇2019-12-27想不出啦……NaN不是唯一的吗
作者回复: 参见 @sprinty 的答案哟。总算有人给出来这个标准答案了。呵呵~
共 7 条评论