
文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载本文基于《现代 JavaScript 教程》原始类型的方法章节中的经典练习题回答一个看似简单却直击 JavaScript 类型模型本质的问题为什么str.test 5无法为字符串变量挂上属性读完本文你将彻底理解原始类型primitive与对象object的差异、对象包装器object wrapper的临时生命周期、严格模式use strict对这一行为的影响以及为什么原始类型本质上不能存储额外的数据。问题回顾给字符串添加属性本教程在 1-string-new-property/task.md 中提出了如下练习let str Hello; str.test 5; alert(str.test);思考一下这段代码能正常工作吗alert会弹出什么如果你凭直觉认为字符串和对象一样可以挂属性那么结果可能会出乎意料。运行结果严格模式与非严格模式截然不同实际运行代码可在教程的在线沙盒中执行会得到两种结果取决于是否开启了严格模式非严格模式sloppy mode弹出undefined代码不报错严格模式use strict直接抛出错误TypeError。也就是说无论哪种模式str.test 5都没有真正把属性存到str上——非严格模式下它静默失败严格模式下它被明确拒绝。关于严格模式本身可以参考本教程 现代模式use strict 一章use strict需要写在脚本或函数的最顶部才生效且一旦启用就无法撤销同时现代 JavaScript 的class和module会自动启用严格模式无需手动书写该指令。本教程的绝大多数示例默认运行在严格模式下。原理拆解在str.test 5这一行究竟发生了什么要理解上述现象关键在于 JavaScript 对访问原始类型属性这一操作的特殊处理。本教程在 原始类型的方法 中给出了明确的机制说明整个执行过程可以分为三步当访问str的属性时一个对象包装器被创建了。字符串str本身是原始值并不是对象。但在访问其属性无论是读str.test还是写str.test 5的那一刻JavaScript 会临时创建一个对应的包装对象对字符串来说是String包装器它携带原始字符串的值并暴露一组方法如toUpperCase()、toLowerCase()等。在严格模式下向包装器写入内容会报错。严格模式下给这个临时对象赋属性被视为非法操作引擎直接抛出异常。这是use strict对这类看似对象、实为原始值的操作的明确约束。在非严格模式下操作继续执行但属性随包装器一同消失。非严格模式下写入会成功——临时对象确实获得了test属性。但紧接着这个临时包装器就被销毁了只剩原始值str。因此在最后一行alert(str.test)时str上没有任何test属性的踪迹读取操作再次创建一个全新的临时包装器而新包装器上不存在test属性于是返回undefined。这个例子的最终结论也正是本教程反复强调的核心事实原始类型不是对象它们不能存储额外的数据。为什么原始类型可以调用方法却不能持有属性这里存在一个初学者常见的困惑既然str.toUpperCase()可以正常工作为什么str.test 5就不行答案就在本教程 原始类型的方法 中对当作对象的原始类型这一悖论的解释中原始类型string、number、bigint、boolean、symbol应当保持尽可能简单轻量只是单个值但开发者又希望对它们执行很多操作大小写转换、取整、截断等最好能通过方法访问于是 JavaScript 的解决方案是原始类型保持原始仅在访问属性/方法的瞬间临时创建对象包装器使用后立即销毁。let str Hello; alert( str.toUpperCase() ); // HELLOstr.toUpperCase()内部发生的事情与str.test 5完全相同创建临时包装器 → 调用方法 → 方法返回新字符串 → 包装器销毁。区别只在于方法调用把结果还给了你而属性写入的成果则随着包装器的销毁而丢失。针对每种原始类型JavaScript 都有对应的包装器构造函数String、Number、Boolean、Symbol和BigInt它们分别提供不同的方法。例如数字的toFixed(n)可以把数字舍入到指定精度let n 1.23456; alert( n.toFixed(2) ); // 1.23值得补充的是正如本教程指出的JavaScript 引擎对这个过程做了高度优化甚至可能跳过创建额外对象的步骤——但规范要求它的行为必须表现得像确实创建并销毁了包装器一样。这也是为什么你无法在外部观察到包装器的存在。用更多实验验证原始类型不是对象结合本教程 原始类型的方法 中的相关示例还可以做几个更直观的验证实验实验一new Number(0)得到的不是数字而是对象alert( typeof 0 ); // number alert( typeof new Number(0) ); // object!实验二包装对象在if中恒为真let zero new Number(0); if (zero) { // zero 为 true因为它是一个对象 alert( zero is truthy?!? ); }一个值为0的对象在布尔判断中居然是true——这正是因为它是一个对象而对象在if中始终为真。本教程因此明确告诫出于历史原因JavaScript 允许使用new Number(1)、new Boolean(false)显式创建包装对象但极其不推荐因为会引发这类反直觉的问题。而不带new调用String(...)/Number(...)/Boolean(...)是安全且常用的类型转换方式例如let num Number(123); // 将字符串转成数字结果是原始类型 number实验三null和undefined连包装器都没有本教程特别指出原始类型null和undefined是例外中的例外它们没有对应的对象包装器也不提供任何方法是最原始的类型。访问它们的属性会直接报错alert(null.test); // error实践启示何时可以用属性、何时不可以理解了包装器机制之后日常编码中应当遵循以下结论不要试图给原始值挂属性。无论是字符串、数字还是布尔值str.xxx value都是无效的非严格模式下静默丢弃严格模式下抛错。如果需要附加数据应使用对象如{}、Map、WeakMap来承载。放心调用原始类型的内置方法。字符串、数字等原始类型的方法调用成本很低因为引擎会高效地处理临时包装器str.toUpperCase()、n.toFixed(2)这类操作都是安全且惯用的。更多方法可继续阅读本教程的 字符串 与 数字类型 章节。区分构造器与转换函数。new String(...)产生的是对象应避免使用而String(...)返回原始值可放心用于类型转换。开启严格模式更安全。严格模式会把这个静默的陷阱变成显式的报错帮助你尽早发现问题。这也是为什么 现代模式use strict 建议在脚本顶部启用严格模式。总结原始类型string、number等不是对象不能存储额外的属性访问原始类型的属性/方法时JavaScript 会临时创建对应类型的对象包装器String、Number、Boolean、Symbol、BigInt用后即毁因此str.test 5在非严格模式下返回undefined写入的test属性随包装器销毁而丢失在严格模式下直接报错原始类型方法如toUpperCase、toFixed正是借助这一机制实现的且被引擎高度优化调用成本很低null和undefined没有任何包装器与方法访问其属性必然报错。完整练习与解答见 task.md 与 solution.md机制讲解见 原始类型的方法。赞分享文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载相关推荐现代 JavaScript 教程为什么字符串原始类型不能添加自定义属性——对象包装器与严格模式深度解析现代 JavaScript 教程为什么字符串原始类型不能添加自定义属性——对象包装器与严格模式深度解析 在《现代 JavaScript 教程》zh.java文档教程前端JavaScript 字符串能添加自定义属性吗——原始类型、对象包装器与严格模式深度解析JavaScript 字符串能添加自定义属性吗——原始类型、对象包装器与严格模式深度解析 本篇文章围绕《Modern JavaScript Tutorial》文档/教程前端Rust 编译器 E0109 错误详解为什么不能给 u32、bool 这类类型添加类型参数Rust 编译器 E0109 错误详解为什么不能给 u32、bool 这类类型添加类型参数 本文基于 Rust 编译器仓库中的错误码文档 E0109.md h编程语言编译器语言运行时标准库创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考