18 | a + b:动态类型是灾难之源还是最好的特性?(上)
18 | a + b:动态类型是灾难之源还是最好的特性?(上)
讲述:周爱民
时长20:34大小18.84M
类型系统的简化
先搞定一半
值 VS 原始值(Primitive values)
干掉那两个碍事儿的
隐式转换
好玩的
且听下回分解
赞 6
提建议
精选留言(12)
- Y2019-12-25这应该是由于对象类型转换为值类型时的拆箱操作导致的。 []拆箱的话会先执行[].valueOf(),得到的是[],并不是原始值,就执行[].toString(),得到的结果是''。 {}拆箱会先执行{}.valueOf(),得到的是{},并不是原始值,于是执行toString(),得到的结果是[object Object]。 []+{}就相当于""+"[object Object]",结果就是[object Object]。 {}+[]的话,js会把开头的{}理解成代码块,所以这句话就相当于+[],也就是等于+"",将空字符串转换为数字类型,结果就是0。 {}+{}的话,也是和上面一样的道理,相当于+"[object Object]",将字符串转化为数字类型,结果是NaN。 []+[]就相当于""+"",所以结果还是""。展开
作者回复: 赞的。^^. 今天的课程可以对答案哟。
共 3 条评论27 - 王大可2019-12-26在chrome浏览器(版本 79.0.3945.88(正式版本) (64 位))计算 {} + {} 结果是"[object Object][object Object]" edge 下是计算 {} + {} 结果是 NaN
作者回复: 确实。 不过在浏览器的控制台上,和在引擎的层面上执行也是会有区别的。都是使用 v8,NodeJS在Shell中与chrome也一样。但是你写在.js文件中,或者直接从node的命令行上执行,效果就不一样了,例如: ``` > node -p -e '{} + {}' NaN ```
共 2 条评论3 - 潇潇雨歇2019-12-25[]和{}在转换为值类型时会先调用valueOf然后调用toString。 1、[]+{},前者转换'',后者转换为[object Object],这里为字符串连接操作,所以结果为'[object Object]' 2、{}+[],前者为代码块,后者+操作符将''转换为0,所以结果为0 3、{}+{},前者为代码块,后者+操作符将'[object Object]'转换为NaN,因为它不能转换为一个正常的数值 4、[]+[],前者为'',后者也为'',这里是正常的字符串连接,所以结果为''展开
作者回复: 赞的,+1票。^^. 今天的课程就分析这个了。
3 - 晓小东2019-12-25老师有个问题, 既然您讲了数据的值类型与引用类型概念, 像weakSet与weakMap 对对象的弱引用该如何理解, 这个弱引用到底是个啥。
作者回复: 弱引用是向weakSet/weakMap中添加一个目标对象的引用,但添加是目标对象的引用计数不增加。比较来说: ``` var x = {}; // <-右边的对象字面量的引用计数加1 var y = x; // <- 再加1 weakSet.add(x); // <-不加1 weakSet.add(y); // <-也不加1 delete x; // 减1 delete y; // 再减1 ... ``` 到这里,由于对象的引用计数为0了,所以weakSet中的那个被add()进去的x、y就自动被回收了。——weakSet/weakMap具备这种机制。 所以weakSet/weakMap没有size这个属性,它不安全。——你刚读了它的值,它自己自动回收了一下,就又变掉了。
3 - FG佳2020-07-13周老师 的《程序原本》棒棒的,里面刻舟求剑的印象非常深2
- 晓小东2019-12-27老师对于下面两段话,我理解的不是很清楚 (没看出来,判断两次还是判断一次逻辑???) NOTE: 在 ECMAScript 6 之前,由于[PrimitiveValue]来存放对应的封装类。也就是说,只有当obj.[Class]存放着false值时,它才是false值所对应的对象实例。而 ECMAScript 6 将上述的依赖项变成了一个,也就是说只要有一个对象有内部槽[[BooleanData]],那么它就是某个 boolean 值对应的对象。这样处理起来就简便了,不必每次做两项判断。展开
作者回复: ES6之前,是需要判断两次的。 * 有[[PrimitiveValue]]内部槽,说明是一个用包装类得到的值。然后, * 查看[[Class]]内部槽,找到对应的包装类,从而知道类型。 在ES6之后,由于每种包装类有独立的一个槽,所以如果对象obj有[[BooleanData]],那就说明了包装类是Boolean(),且被包装的数据在[[BooleanData]]槽中。
2 - undefined2021-04-25Chrome 90.0.4430.85 中(非 DevTools) 创建 index.html 和 app.js app.js -> console.log({} + {}) 的值竟然也是 "[object Object][object Object]"
作者回复: 在之前的回复中说过这个问题。如下执行测试: ``` > node -p -e '{} + {}' NaN ``` `-e`参数用于直接执行,而不是作为模块执行。
共 4 条评论1 - undefined2021-04-25#提个小问题# 原文中 「一共就有了 5 个对应的私有槽:[[BooleanData] [[NumberData]]、[[StringData] [[SymbolData]]和[[BigIntData]]」 修改为 一共就有了 5 个对应的私有槽:[[BooleanData]]、[[NumberData]]、[[StringData]]、[[SymbolData]] 和 [[BigIntData]] 要好一些。 原文少了顿号,缺少了两个 ] 符号,中文和英文之间加上一个空格要美观些(个人感受)。展开
作者回复: 多谢多谢。^^. 刚才我查了一下md原稿,原文是没错的。不过这个改起来有点困难。内容网页是用markdown自动生成的,估计极客时间用的也是第三方的生成组件,然后自动吞掉了……
1 - HoSalt2020-05-25「由于[PrimitiveValue]来存放对应的封装类。也就是说,只有当obj.[Class]存放着false值时」 ES6之前PrimitiveValue里面放的对象(封装类),ES6后 [[xxxData]]里面放的是值还是封装类,若放的封装类,那值依旧放在obj.[Class]? 不太明白PrimitiveValue为什么要放封装类,直接放基本类型不就行了吗?
作者回复: ES6之前是两个私有域 [[Class]] [[PrimitiveValue]] 而ES6之后是一个。以String(5)为例,那么就是: [[StringData]] 但是由于传入参数是数字5,所以 [[StringData]]里存放的是转换后的字符串值"5",同样的原因,如果是ES5那么[[PrimitiveValue]]也存放的是字符串值"5",而[[Class]]将存放String这个类的引用。 在ES5里面,Boolean/String/Number只使用了一种结构,三种对象的内部结构是一样的;在ES6里面,是三种不同的变体对象(结构/内部槽是不一样的)。这是两种不同的设计思路。至于为什么要放转换后的值,是因为创建这个对象是一次性的,而使用是多次的,所以预先转换的效率高。
1 - 授人以摸鱼2020-05-23Symbol这个类型,可能还是因为需要一些可以保证独一无二的东西吧 比如说两个库都给对象添加一些功能,需要通过给字段添加一些特定属性来实现 但结果偏偏两个库作者的思路很类似,起名撞车了,会造成的结果就完全不可控了 Symbol不仅可以保证独一无二,还可以不做转换直接用作字段名,这个其实是让js像其他对象类型不可变的语言一样,保证了字段的唯一性,即使可能两个字段的描述相同,它们也不会相互覆盖展开1
- weineel2019-12-25在您的文章里,经常出现 "界面" 这个词,怎么理解呢? 我简单的当做 "编程接口" 的近义词。
作者回复: 是的。同义。 只是我一直的工作是架构,所以架构中通用它的直译,也就是“界面”,而不是“接口/编程接口”,因为有些架构中的界面,并不是用编程来实现的。在这种情况下,界面更多的是一种规约。
1 - 鲜于.css2019-12-26js就是学不明白闭包和原型原型链共 1 条评论