JavaScript 最初是为了给网站添加一些功能而被设计出来的简单语言。但是时至今日,它无处不在,而且还更加复杂了。随着 Web 应用程序越来越复杂,JavaScript 也发展得越来越快。编写简洁、可读且可维护的 JavaScript 程序不再像以前那么容易了。今天我们总结 9 个实用的 JavaScript 技巧,帮助你更优雅的编写前端代码
1. 以 JavaScript 方式打破嵌套循环
许多编程语言都有用于跳出循环的break关键字。然而,该关键字仅用于跳出当前循环。如果您必须打破嵌套循环,则可能很难保持代码整洁。
例如,如何实现以下代码?
for (let i in arr1) { for (let j in arr2) { for (let k in arr3) { if (k === j - 4) { // need to break out of the second loop } if (k === i - 3) { // need to break out of the outermost loop } } }}
在其他语言中,您可能需要将布尔变量声明为外部循环的“flags”,并在进入相应循环时检查中断的“标志”。此方法有效,但如果有很多布尔标志,会使您的代码有点混乱。
JavaScript 针对这种场景提供了语法级的解决方案——label。
您可以使用标签来标识循环,然后参考该标签来打破相应的循环。
因此,上述代码的JavaScript实现方式如下:
loop1: for (let i in arr1) { loop2: for (let j in arr2) { for (let k in arr3) { if (k === j - 4) { break loop2 } if (k === i - 3) { break loop1 } } } }
Loop1 和 Loop2 是这两个外部循环的标签,因此使用其标签可以轻松打破匹配的循环。无需将其他变量声明为“flags”。
2. 利用扩展运算符进行解构赋值
扩展运算符是整洁 JavaScript 程序的关键。
let leaders = { me: "Yang", T: "Elon", A: "Tim", MS: "Bill"}let {me, ...others} = leadersconsole.log(me)// "Yang"console.log(others)// {T: "Elon", A: "Tim", MS: "Bill"}
如上面的示例所示,我们使用了一个简单的三个点的扩展运算符,将leader[“me”]的值分配给名为me的变量,并将其他键值对分配给数组others。
在 React 中,此技巧通常用于在构建 UI 组件时从 props 接收多个值。
3. 浅复制对象或数组的几种方法
众所周知,JavaScript 中的对象和数组等非原始数据类型是通过引用传递的。
因此,如下例所示,更改“新”数组也会更改原始数组:
let a = [1, 2, 3]let b = ab.push(8)console.log(a, b)// [ 1, 2, 3, 8 ] [ 1, 2, 3, 8 ]
要真正将数组 a 复制到新数组 b,JavaScript 中至少有 4 种方法。
使用 slice() 方法
slice()方法是提取数组的一部分。鉴于它在新数组中返回提取的部分,我们可以提取整个数组并将返回的数组作为副本:
let a = [1, 2, 3]let b = a.slice()b.push(8)console.log(a, b)// [ 1, 2, 3 ] [ 1, 2, 3, 8 ]
使用扩展运算符
扩展运算符不仅擅长解构赋值,还能够从数组或对象中解包项目:
let a = [1, 2, 3]let b = [...a]b.push(8)console.log(a, b)// [ 1, 2, 3 ] [ 1, 2, 3, 8 ]
使用内置的 Array.from() 方法
事实上,有一个专门设计的方法来进行复制——Array.from():
let a = [1, 2, 3]let b = Array.from(a)b.push(8)console.log(a, b)// [ 1, 2, 3 ] [ 1, 2, 3, 8 ]
使用 concat() 方法
concat() 方法用于合并两个或多个数组。由于此方法返回一个新数组而不更改现有数组,因此我们也可以利用它进行复制:
let a = [1, 2, 3]let b = [].concat(a)b.push(8)console.log(a, b)// [ 1, 2, 3 ] [ 1, 2, 3, 8 ]
let leader = { name:'Yang', age:'30'}let fake_leader = {...leader}fake_leader['skill']='coding'console.log(leader,fake_leader)// { name: 'Yang', age: '30' } { name: 'Yang', age: '30', skill: 'coding' }
另一种方法是使用内置的 Object.assign() 方法:
let leader = { name:'Yang', age:'30'}let fake_leader = Object.assign({},leader)fake_leader['skill']='coding'console.log(leader,fake_leader)// { name: 'Yang', age: '30' } { name: 'Yang', age: '30', skill: 'coding' }
这种类型的复制实际上称为浅复制,这意味着它只有一层深。它仅复制元素的引用,而不复制元素本身。因此,如果元素是对象或数组,复制的数组仍将引用相同的对象或数组。
例如,如果数组 a 包含一个内部数组(两级深),则浅拷贝无法真正复制它们,并且编辑数组 b 的内部数组也会更改 a 的:
let a = [1, [2, 2, 2], 3]let b = [].concat(a)b[1].push(8)console.log(a, b)// [ 1, [ 2, 2, 2, 8 ], 3 ] [ 1, [ 2, 2, 2, 8 ], 3 ]
4. 使用 JSON 技巧进行深度复制
要实现深度复制,一个流行的技巧是结合使用 JSON.stringify() 和 JSON.parse()。
这个想法是将对象(或数组)序列化为 JSON 格式的字符串,然后将其解析回新对象。此过程有效且优雅地创建原始数组或对象的深层副本:
let a = [1, [2, 2, 2], 3]let b = JSON.parse(JSON.stringify(a))b[1].push(8)console.log(a, b)// [ 1, [ 2, 2, 2 ], 3 ] [ 1, [ 2, 2, 2, 8 ], 3 ]
基于 JSON 的技巧在大多数简单情况下都很有用。但是,我们需要知道,要使该方法有效,对象必须是 JSON 可序列化的。
我们看一个反例:
const obj = { func: function() { console.log("hello world!"); }}const cp_obj=JSON.parse(JSON.stringify(obj))console.log(cp_obj['func'])// undefined
obj['func'] 的值是一个函数。它不能再通过 JSON 技巧进行复制。
在这种情况下,我们可以利用一个著名的第三方 JS 库 lodash:
const _ = require('lodash');const obj = { func: function() { console.log("hello world!"); }}const cp_obj=_.cloneDeep(obj)cp_obj['func']()// hello world!
如上所示,lodash 中的 cloneDeep 方法完美克隆了 obj 内的函数,并且可以在新的 cp_obj 上成功执行。
5. 熟练地实现For循环
如果您仍在 JavaScript 中使用 C/C++ 风格的 for 循环,那么,您肯定需要提升您的技能。
当然,下面的代码是正确的,但它还不够“JavaScript”。
const arr = ['Y', 'a', 'n', 'g']for (let i = 0; i < arr.length; i++) { console.log(arr[i])}// Y// a// n// g
在 JavaScript 中编写上述代码的惯用方法如下:
使用 forEach() 方法
forEach 方法非常适合迭代数组的元素:
const author = [ 'Y', 'a', 'n', 'g' ];author.forEach((c)=>{console.log(c)})// Y// a// n// g
使用map()函数
如果您阅读开源 JavaScript 程序,您可能会遇到 map() 函数。它是 JavaScript 中最流行的方法之一:
const author = [ 'Y', 'a', 'n', 'g' ];author.map((c)=>{console.log(c)})// Y// a// n// g
map() 函数的行为与 forEach() 基本相似,但有一个显着差异:
map() 方法返回一个与原始数组长度相同的新数组,其中每个元素都是对相应元素调用函数的结果。原始数组保持不变。forEach() 方法不返回任何内容。
以下代码说明了如何使用map()函数获取新数组:
const author = ['Y', 'a', 'n', 'g'];const cute_author = author.map((c) => c + ':)')console.log(cute_author)// [ 'Y:)', 'a:)', 'n:)', 'g:)' ]console.log(author)// [ 'Y', 'a', 'n', 'g' ]
但是,我们无法使用 forEach() 函数获取新数组:
const author = ['Y', 'a', 'n', 'g'];const cute_author = author.forEach((c) => c + ':)')console.log(cute_author)// undefinedconsole.log(author)// [ 'Y', 'a', 'n', 'g' ]
使用 for...of... 结构
ES6 是 JavaScript 的一个里程碑。这个版本引入了许多好的功能。“for...of...”方法就是其中之一。
const author = [ 'Y', 'a', 'n', 'g' ];for (let char of author){ console.log(char);}// Y// a// n// g
使用 for...in... 结构
“for...in...”语法也能够实现与我们相同的功能。但我们应该注意“for…in…”和“for…of…”之间的区别。下面的代码片段对此进行了解释:
const author = [ 'Y', 'a', 'n', 'g' ];for (let idx in author){ console.log(author[idx]);}// Y// a// n// g
6. 删除数组重复值的最快方法
ES6 为 JavaScrip 引入了一种新的数据结构——集合。集合是唯一的项目的集合。
由于集合的特性,它使得删除数组的重复值变得更加简单。
const a = [1, 2, 1, 6, 6, 6, 9]const unique_a = [...new Set(a)]console.log(unique_a)// [ 1, 2, 6, 9 ]
如上面的程序所示,我们可以利用展开运算符和 Set() 方法来方便地获取数组的唯一元素。
7. 用一行代码反转字符串
要在 JavaScript 中反转字符串,我们不需要编写 for 循环。
执行此操作需要 3 个步骤:
这 3 个步骤需要利用 3 种不同的内置方法,如下所示:
const author = "Yang Zhou";const reversedAuthor = author.split("").reverse().join("");console.log(reversedAuthor);// uohZ gnaY
这是一句不错的俏皮话,但说实话,JS 反转字符串的方式并不像 Python 那样优雅。由于其优美的切片语法,Python 可以更简洁地完成同样的事情:
author = 'Yang Zhou'reversed_author = author[::-1]print(reversed_author)# uohZ gnaY
顺便说一句,检查 JavaScript 字符串是否为回文的一个简单方法是将字符串与其反转版本进行比较:
const author = 'YangnaY'const isPalindrome = author.split("").reverse().join("") === authorconsole.log(isPalindrome)// true
8.快速计算数组中的元素
如何计算 JavaScript 数组中的每个元素?
使用for循环一项一项地遍历项目并在此过程中对它们进行计数?
这是一个解决方案,但根本不是一个优雅的解决方案。我想说 lodash 是一个超级有用的 JS 库:
const _ = require('lodash');const author = ['Y', 'a', 'a', 'a', 'n', 'n', 'g', 'g', 'g', 'g']console.log(_.countBy(author))// { Y: 1, a: 3, n: 2, g: 4 }
如果你不想使用第三方库,自己实现类似的功能并不难:
const countBy = (arr) => { let count = {}; arr.forEach((e) => { count[e] = (count[e] || 0) + 1; }); return count;}const author = ['Y', 'a', 'a', 'a', 'n', 'n', 'g', 'g', 'g', 'g']const charCount = countBy(author);console.log(charCount);// { Y: 1, a: 3, n: 2, g: 4 }
如果您只需要计算一个特定项目,则 filter() 方法是一个不错的选择:
const author = ['Y', 'a', 'a', 'a', 'n', 'n', 'g', 'g', 'g', 'g']// Filter all elements equal to 'a' and return the length (count)const countOfa = author.filter(x => x === 'a').lengthconsole.log(countOfa)// 3
9. 使用逗号运算符简化代码
由于逗号运算符的语法,逗号在 JavaScript 中更加强大。
逗号 (,) 运算符计算每个表达式(从左到右)并返回最后一个表达式的值。如果你能熟练地利用它的力量,这对简化你的代码将会非常有帮助。
例如,以下程序演示了如何利用逗号运算符通过一行代码实现两个不同的目的:
let sum = 0;const squares = [1, 2, 3, 4, 5].map((x) => (sum += x, x * x));console.log(squares);// [1, 4, 9, 16, 25]console.log(sum);// 15
鉴于逗号运算符始终返回最后一个表达式的结果,我们可以利用此功能来避免编写许多 return 关键字。
例如,以下代码的 get_author() 函数返回更改后的 arr:
const get_author = arr => { arr.push('g') return arr}console.log(get_author(['Y','a','n']))// [ 'Y', 'a', 'n', 'g' ]
这是让逗号运算符炫耀的最佳:场景
const get_author = arr => (arr.push('g'), arr)console.log(get_author(['Y', 'a', 'n']))// [ 'Y', 'a', 'n', 'g' ]
这9个方法,无论你的新晋职场的小白,还是前端大神在实际的开发过程中都非常的实用,希望可以帮带你!