JavaScript 核心原理解析
周爱民
《JavaScript 语言精髓与编程实践》作者,南潮科技(Ruff)首席架构师
32699 人已学习
新⼈⾸单¥59
登录后,你可以任选3讲全文学习
课程目录
已完结/共 28 讲
开篇词 (1讲)
JavaScript 核心原理解析
15
15
1.0x
00:00/00:00
登录|注册

18 | a + b:动态类型是灾难之源还是最好的特性?(上)

从引用到值
从值到引用
值类型
引用类型
@graybernhardt 的四个示例
下回分解的内容
隐式转换的复杂性
JavaScript中类型显式转换的容易处理
系统只处理boolean/string/number三种值类型
使用typeof(x)检查数据类型
包装类转换成对象的内部槽
引用类型和值类型的区别
JavaScript中类型处理的背景信息
结果的收敛性
示例:[] + {}
隐式转换的不可预测性
ECMAScript规范中的类型错误
隐式转换的潜规则
JavaScript核心库的统一处理方法
函数参数的类型声明
符号的必要性
ECMAScript 6中的私有槽
原始值的私有槽
符号对象转换成符号
函数转换成字符串的含义
任何东西都可以转换成字符串
对象的toString()和valueOf()方法
类型转换规则
JavaScript类型系统的简化划分
ECMAScript对语言类型的约定
JavaScript有两套类型系统
学习上易学难精,使用中易用易错
动态执行与动态类型
JavaScript的动态语言特性
下回分解
好玩的
隐式转换
值VS原始值
类型系统的简化
动态类型
JavaScript动态类型系统

该思维导图由 AI 生成,仅供参考

你好,我是周爱民,欢迎回到我的专栏。今天我们讲的主题是 JavaScript 的动态类型系统。
动态类型是 JavaScript 的动态语言特性中最有代表性的一种。
动态执行与动态类型是天生根植于 JavaScript 语言核心设计中的基础组件,它们相辅相成,导致了 JavaScript 在学习上是易学难精,在使用中是易用易错。成兹败兹,难以得失论。

类型系统的简化

从根底上来说,JavaScript 有着两套类型系统,如果仅以此论,那么还算不上复杂。
但是 ECMAScript 对语言类型的约定,又与 JavaScript 原生的、最初的语言设计不同,这导致了各种解释纷至沓来,很难统一成一个说法。而且,ECMAScript 又为规范书写而订立了一套类型系统,并不停地演进它。这就如同雪上加霜,导致 JavaScript 的类型系统越发地说不清楚了。
在讨论动态类型的时候,可以将 JavaScript 类型系统做一些简化,从根底里来说,JavaScript 也就是 typeof() 所支持的 7 种类型,其中的“对象(object)”与“函数(function)”算一大类,合称为引用类型,而其他类型作为值类型
无论如何,我们就先以这种简单的类型划分为基础,来讨论 JavaScript 中的动态类型。因为这样一来,JavaScript 中的类型转换变得很简单、很干净,也很易懂,可以用两条规则概括如下:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

JavaScript的动态类型系统是其最具代表性的特性之一。本文深入探讨了JavaScript类型系统的复杂性和简化规则。作者指出,JavaScript的类型系统可以简化为7种类型,其中的“对象”和“函数”被合称为引用类型,其他类型则为值类型。在讨论动态类型时,文章提出了两条简单的类型转换规则,即从值到引用调用Object(x)函数,从引用到值调用valueOf()方法或值类型的包装类函数。文章还强调了从值到引用的转换相对容易,因为主要的值类型都有对应的引用类型,可以通过简单方法一一对应地将它们转换过去。作者还详细介绍了使用Object(x)函数进行转换的安全性和灵活性。此外,文章还讨论了ECMAScript 6中新增的Symbol.toPrimitive符号以及原始值与对象类型之间的关系。总的来说,本文全面探讨了JavaScript的动态类型系统,展现了其复杂性和简化规则,为读者提供了对JavaScript类型系统的深入了解。 文章还讨论了ECMAScript 6中新增的Symbol.toPrimitive符号以及原始值与对象类型之间的关系。此外,文章还讨论了ECMAScript 6中新增的Symbol.toPrimitive符号以及原始值与对象类型之间的关系。文章还讨论了ECMAScript 6中新增的Symbol.toPrimitive符号以及原始值与对象类型之间的关系。文章还讨论了ECMAScript 6中新增的Symbol.toPrimitive符号以及原始值与对象类型之间的关系。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《JavaScript 核心原理解析》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(12)

  • 最新
  • 精选
  • Y
    这应该是由于对象类型转换为值类型时的拆箱操作导致的。 []拆箱的话会先执行[].valueOf(),得到的是[],并不是原始值,就执行[].toString(),得到的结果是''。 {}拆箱会先执行{}.valueOf(),得到的是{},并不是原始值,于是执行toString(),得到的结果是[object Object]。 []+{}就相当于""+"[object Object]",结果就是[object Object]。 {}+[]的话,js会把开头的{}理解成代码块,所以这句话就相当于+[],也就是等于+"",将空字符串转换为数字类型,结果就是0。 {}+{}的话,也是和上面一样的道理,相当于+"[object Object]",将字符串转化为数字类型,结果是NaN。 []+[]就相当于""+"",所以结果还是""。

    作者回复: 赞的。^^. 今天的课程可以对答案哟。

    2019-12-25
    3
    30
  • 王大可
    在chrome浏览器(版本 79.0.3945.88(正式版本) (64 位))计算 {} + {} 结果是"[object Object][object Object]" edge 下是计算 {} + {} 结果是 NaN

    作者回复: 确实。 不过在浏览器的控制台上,和在引擎的层面上执行也是会有区别的。都是使用 v8,NodeJS在Shell中与chrome也一样。但是你写在.js文件中,或者直接从node的命令行上执行,效果就不一样了,例如: ``` > node -p -e '{} + {}' NaN ```

    2019-12-26
    2
    3
  • 潇潇雨歇
    []和{}在转换为值类型时会先调用valueOf然后调用toString。 1、[]+{},前者转换'',后者转换为[object Object],这里为字符串连接操作,所以结果为'[object Object]' 2、{}+[],前者为代码块,后者+操作符将''转换为0,所以结果为0 3、{}+{},前者为代码块,后者+操作符将'[object Object]'转换为NaN,因为它不能转换为一个正常的数值 4、[]+[],前者为'',后者也为'',这里是正常的字符串连接,所以结果为''

    作者回复: 赞的,+1票。^^. 今天的课程就分析这个了。

    2019-12-25
    3
  • 晓小东
    老师有个问题, 既然您讲了数据的值类型与引用类型概念, 像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这个属性,它不安全。——你刚读了它的值,它自己自动回收了一下,就又变掉了。

    2019-12-25
    3
  • 晓小东
    老师对于下面两段话,我理解的不是很清楚 (没看出来,判断两次还是判断一次逻辑???) NOTE: 在 ECMAScript 6 之前,由于[PrimitiveValue]来存放对应的封装类。也就是说,只有当obj.[Class]存放着false值时,它才是false值所对应的对象实例。而 ECMAScript 6 将上述的依赖项变成了一个,也就是说只要有一个对象有内部槽[[BooleanData]],那么它就是某个 boolean 值对应的对象。这样处理起来就简便了,不必每次做两项判断。

    作者回复: ES6之前,是需要判断两次的。 * 有[[PrimitiveValue]]内部槽,说明是一个用包装类得到的值。然后, * 查看[[Class]]内部槽,找到对应的包装类,从而知道类型。 在ES6之后,由于每种包装类有独立的一个槽,所以如果对象obj有[[BooleanData]],那就说明了包装类是Boolean(),且被包装的数据在[[BooleanData]]槽中。

    2019-12-27
    2
  • undefined
    Chrome 90.0.4430.85 中(非 DevTools) 创建 index.html 和 app.js app.js -> console.log({} + {}) 的值竟然也是 "[object Object][object Object]"

    作者回复: 在之前的回复中说过这个问题。如下执行测试: ``` > node -p -e '{} + {}' NaN ``` `-e`参数用于直接执行,而不是作为模块执行。

    2021-04-25
    4
    1
  • undefined
    #提个小问题# 原文中 「一共就有了 5 个对应的私有槽:[[BooleanData] [[NumberData]]、[[StringData] [[SymbolData]]和[[BigIntData]]」 修改为 一共就有了 5 个对应的私有槽:[[BooleanData]]、[[NumberData]]、[[StringData]]、[[SymbolData]] 和 [[BigIntData]] 要好一些。 原文少了顿号,缺少了两个 ] 符号,中文和英文之间加上一个空格要美观些(个人感受)。

    作者回复: 多谢多谢。^^. 刚才我查了一下md原稿,原文是没错的。不过这个改起来有点困难。内容网页是用markdown自动生成的,估计极客时间用的也是第三方的生成组件,然后自动吞掉了……

    2021-04-25
    1
  • HoSalt
    「由于[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里面,是三种不同的变体对象(结构/内部槽是不一样的)。这是两种不同的设计思路。至于为什么要放转换后的值,是因为创建这个对象是一次性的,而使用是多次的,所以预先转换的效率高。

    2020-05-25
    1
  • weineel
    在您的文章里,经常出现 "界面" 这个词,怎么理解呢? 我简单的当做 "编程接口" 的近义词。

    作者回复: 是的。同义。 只是我一直的工作是架构,所以架构中通用它的直译,也就是“界面”,而不是“接口/编程接口”,因为有些架构中的界面,并不是用编程来实现的。在这种情况下,界面更多的是一种规约。

    2019-12-25
    1
  • FG佳
    周老师 的《程序原本》棒棒的,里面刻舟求剑的印象非常深
    2020-07-13
    2
收起评论
显示
设置
留言
12
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部