每种现代编程语言通常有两个主要部分:词法语法和标准库。编程语言语法通过保留关键字和特殊 ASCII 字符构建了独特的编程语法,供程序员编写代码。编程语言语法实现了一些语言特性,帮助开发者高效编写自解释的代码以满足各种开发需求。同时,标准库提供了访问预先开发的通用编程功能(如字符串处理、数据结构、日期/时间处理等)的接口。
JavaScript 具有一个预先导入的功能齐全的标准库,为开发者提供了各种预开发对象。例如,你可以使用标准的 ArrayBuffer
对象在 JavaScript 中处理原始二进制数据。JavaScript 语法提供了基本的标准编程语法和现代、高效的 JavaScript 专用语言特性,帮助开发者更快、更干净地编写代码。
在这篇文章中,我将讨论一些只有少数开发者知道的隐藏的 JavaScript 词法语法特性。实践并使用这些高级 JavaScript 语言特性,通过最大限度地利用 JavaScript 成为 JavaScript 专家!
标记模板字面量函数
现如今,开发者使用 ES6 模板字面量进行字符串插值,而不是旧式的字符串连接。模板字面量允许你通过在字符串段中嵌入表达式来构建字符串,语法简洁易读。JavaScript 模板字面量还支持使用标记模板函数来解析带有自定义逻辑的模板字符串。
标记模板函数允许开发者访问解析后的模板字符串的各个部分,因此你可以根据自定义解析需求轻松处理它。例如,以下 cap
标记模板函数将每个表达式结果大写:
function cap(strings, ...exps) {
return strings.reduce((acc, str, i) =>
(acc + str + (exps[i]?.toString()?.toUpperCase() ?? '')), '');
}
let str = cap`Hello ${'JavaScript'}... Hello ${'everyone'}...`;
console.log(str); // Hello JAVASCRIPT... Hello EVERYONE...
如上面的代码片段所示,你可以使用传递给标记函数的参数处理评估后的模板字符串。你甚至可以从标记函数返回对象。因此,使用标记函数进行字符串到对象数据转换是标记模板字面量的一个很好的用例。以下 json 标记函数实现了一种更好的语法方法来解析代码中的 JSON 字符串:
function json(strings, ...exps) {
return JSON.parse(strings.reduce((acc, str, i) =>
(acc + str + (exps[i] ?? '')), ''));
}
let obj = json`{
"msg": "Hello JavaScript",
"code": ${1000}
}`;
console.log(obj); // {msg: 'Hello JavaScript', code: 1000}
类似地,你可以开发标签来转换 YAML 和 HTML 格式:
let yamlObj = yaml`...`;
let htmlObj = html`...`;
流行的 Google 开源库 zx 让你可以使用标记模板字面量特性在 Node.js 中编写类似 Bash 的脚本。
原生非十进制数字字面量
人类通常使用十进制数系统,因此每种编程语言都将数字字面量视为十进制数。但在某些开发场景中,我们必须在源代码中使用其他数制。例如,我们应该使用八进制数来通过 Node.js API 更改文件系统访问权限。任何现代编程语言,包括 JavaScript,使用一组通用前缀来表示非十进制数字字面量。
看看如何使用标准前缀在 JavaScript 中使用二进制、八进制和十六进制数字字面量:
let n1 = 0b101;
let n2 = 0o21;
let n3 = 0xff;
console.log(n1); // 5
console.log(n2); // 17
console.log(n3); // 255
如你所见,每个非十进制数字立即转换为十进制数字,这意味着 JavaScript 在解析源文件时实现了这种非十进制数字支持,并在运行时使用通用的 Number 对象。
然而,内置的 toString()
方法和 parseInt()
函数帮助开发者通过将非十进制数字存储为字符串来处理它们:
let n1 = '0xff';
let n2 = parseInt(n1, 16);
console.log(n2); // 255 (decimal)
let n3 = n2.toString(2);
console.log(n3); // 11111111 (binary)
使用字符串格式显示二进制数字并不是大问题,但在字符串中存储大量二进制数据不是性能友好的方法,因此你可以改用类型化数组。
使用对象方法防止 JavaScript 对象修改
默认情况下,JavaScript 不会对对象进行严格检查——它允许你随意操作任何对象。你甚至可以操作内置对象:
JSON.parse = (str) => console.log(str);
例如,你可以通过在浏览器控制台中覆盖内置对象方法来破坏生产网站(仅限当前浏览器标签页),如上面的代码片段所示。
JavaScript 允许你使用 Object.seal()
和 Object.freeze()
方法防止对象修改。例如,seal()
方法通过允许现有属性值修改来防止新属性添加:
const obj = {
a: 100,
f: () => console.log('Hello...')
};
Object.seal(obj);
obj.a = 1000; // works
obj.b = 100; // doesn't work
delete obj.a; // doesn't work
Object.freeze()
防止添加新属性并阻止现有属性修改,如下代码片段所示:
Object.freeze(obj);
obj.a = 1000; // doesn't work
obj.b = 100; // doesn't work
delete obj.a; // doesn't work
默认情况下,密封和冻结对象不会因无效操作而抛出错误,因此你必须启用 JavaScript 严格模式才能实现这一点。
嵌套解构和重命名
现如今,由于 React 等库教程的影响,开发者经常使用 ES6 解构特性与对象和数组。解构特性提供了一种更好的语法糖,用于通过从数组和对象中提取字段来创建新变量/常量。基本对象解构通常从第一级属性提取属性,如下所示:
const obj = {
a: 10,
b: 20,
c: 30
};
let {a, b, c} = obj;
console.log(a, b, c); // 10 20 30
如果你需要重命名变量名,可以使用解构重命名语法,如下所示:
let {a: A, b: B, c: C} = obj;
console.log(A, B, C); // 10 20 30
我们看看一个高级对象解构示例。我们可以从嵌套属性中提取数据并使用重命名语法,如下代码片段所示
const customer = {
info: {
name: 'John',
score: 80,
address: {postalCode: 2000}
}
};
let {
info: {
name: customerName,
address: {postalCode: postCode }
}
} = customer;
console.log(customerName, postCode); // John 2000
在这里,我们通过结合解构和重命名特性提取嵌套属性,创建了两个变量。你甚至可以使用剩余参数语法获取解构过程中未解包的对象属性:
let {
info: {
name: customerName,
address: { postalCode: postCode },
...rest
}
} = customer;
上面的 ...rest
对象包含 { score: 80 }
对象,因为 score 属性未被解包到单独的变量中。
使用计算属性创建键值对象
在某些开发场景中,我们需要动态访问对象属性。我们可以轻松地使用括号语法动态设置/获取属性,但使用多个重复的设置/获取语句无疑会影响代码可读性。
看看以下代码片段,使用括号语法设置动态属性名:
function createElement(tag, score) {
let doc = {
id: 100,
tag,
};
doc[tag + 'Score'] = score * 100;
doc[tag + 'Type'] = 'internal';
return doc;
}
console.log(createElement('box', 50));
// {id: 100, boxScore: 5000, boxType: 'internal'}
我们可以使用计算属性语法简化上述函数,如下所示:
const createElement = (tag, score) => ({
id: 100,
tag,
[tag + 'Score']: score * 100,
[tag + 'Type']: 'internal'
});
现代 JavaScript 语法是如此富有成效--它甚至支持对象重构语法中的计算属性,因此您可以使用动态生成的属性名来解包属性。例如,下面的代码片段使用对象重构和计算属性语法,找到了使用上述 createElement()
函数生成的特定对象的分数字段:
let obj = {
id: 100,
tag: 'box',
boxScore: 5000,
boxType: 'internal'
};
let {[obj.tag + 'Score']: score} = obj;
console.log(score); // 5000
总结
这篇文章探讨了一些鲜为人知的 JavaScript 语言特性,这些特性可以帮助你通过编写简洁的代码块来提高编码效率。除了这些特性之外,指数运算符、类似逗号运算符的语法增强功能也可以帮助你以专家的方式编写 JavaScript 代码,并保持最新的知识!
ECMAScript 通过讨论社区提案积极改进标准的 JavaScript 规范。ECMAScript TC39 小组维护了一个 GitHub 仓库,列出了最新的 JavaScript 增强提案。其中一些提案试图扩展 JavaScript 语法以提高开发者的生产力。例如,一些提案尝试标准化 function.sent 元属性、为 throw 关键字支持表达式以及类似函数管道的语言修改,适用于所有流行的网页浏览器。
首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。