JavaScript 实用技巧与 ES6+ 语法速查

作者:函数茶馆

时间:2026-10-23

浏览:251

本文汇总了 JavaScript 开发中的实用技巧,涵盖 && 和 || 短路运算、函数默认参数、类数组转数组、解构赋值及数组去重扁平化等常见场景。重点解析 ES5 与 ES6+ 的实现差异,提供代码示例与注意事项,帮助开发者编写更简洁高效的代码。

文章目录

JavaScript 实用技巧与 ES6+ 语法速查

前言

本文整理自日常实践、博客阅读及项目源码中的 JavaScript 技巧。内容既包含一些巧妙的编程手法,也涉及 ES6+ 标准中具备高实用价值的新语法特性。

&& 和 || 的妙用

&& 和 || 的妙用

在某些场景下,我们需要判断函数或变量是否为真,若为真则执行后续操作。例如:


const task1 = () => {
 console.log('执行 task1');
 return Math.random() >= 0.5;
}

const task2 = () => console.log('task1 执行成功后执行 task2');
if (task1()) task2();

上述 if 语句逻辑可以通过 && 运算符简化为:


task1() && task2();

若需在 task1 执行失败(返回 false)时执行 task3,可结合使用 && 和 ||:


const task3 = () => console.log('task1 执行失败后执行 task3');
task1() && task2() || task3();

这本质上利用了 && 和 || 的短路求值特性。

此外,使用三元条件运算符也能实现相同逻辑:


task1() ? task2() : task3();

以下展示一个在 React Hooks 项目中实际使用的代码片段:


const ProfileItem = (props) => {
 const { name, value, render } = props;

 return (
  <div className="profile-item">
   <span className="item-name">{name}</span>
   <form action="">
    {/* 根据是否有 render 这个 props 来返回不同的内容 */}
    {render && render(props) || <SimpleProfileItemContent value={value}/>}
   </form>
  </div>
 )
}

函数默认值

函数默认值

ES5 版本

利用短路或运算符设置函数默认值是常见做法,但存在潜在陷阱。如下代码所示,当默认值参数为数字时,若传入 0,仍会回退使用默认值,因此必须对 y 为 0 的情况进行单独判断。


const pow = (x, y) => {
 y = y || 2;
 
 let result = 1;
 for (let i = 0, max = y; i < max; i++) {
  result *= x;
 }

 return result;
}

console.log(pow(2)); // => 4
console.log(pow(2, 3)); // => 8

// 当 y 传值为 0 时, y 取值 2
console.log(pow(2, 0)); // => 4

ES6 版本

ES6 在语法层面提供的默认参数功能更为可靠:


const pow = (x, y=2) => {
 let result = 1;
 for (let i = 0, max = y; i < max; i++) {
  result *= x;
 }

 return result;
}

console.log(pow(2)); // => 4
console.log(pow(2, 3)) // => 8
console.log(pow(2, 0)); // => 1

类数组转数组

类数组转数组

类数组对象(如 arguments 或 jQuery 对象)具备 length 属性且可通过下标访问,外观与数组相似,但并非真正的数组实例。

由于类数组缺少 slice、map 等集合方法,有时需将其转换为数组以使用这些功能。


function func() {
 for (let i = 0, max = arguments.length; i < max; i++) {
  console.log(arguments[i]);
 }

 console.log(Array.isArray(arguments)); // => false
 // 类数组没有 slice, forEach, map 等集合函数
 console.log(arguments.slice === undefined); // => true
}

func('Google', 'facebook', 'Microsoft'); 
// => 
// Google
// facebook
// Microsoft

ES5 中的转换方法

将 Array 原型的 slice 方法绑定到 arguments 对象上调用,且不传参数,从而返回所有元素。


function func() {
 const array = Array.prototype.slice.call(arguments);
 console.log(array.slice(0, 1));
}

func('Google', 'facebook', 'Microsoft'); // => [ 'Google' ]

ES6 中的转换方法

ES6 提供了更多将类数组转换为数组的途径。

使用扩展运算符


function func() {
 console.log([...arguments])
}

func('Google', 'facebook', 'Microsoft'); // [ 'Google', 'facebook', 'Microsoft' ]

使用 Array.from


function func() {
 console.log(Array.from(arguments))
}

func('Google', 'facebook', 'Microsoft'); // [ 'Google', 'facebook', 'Microsoft' ]

构造连续整数数组

构造一个连续整数的数组

这里直接展示我认为较为简洁的方法:


// 输出 2 开始连续的8个整数
const array = Array.from({ length: 8}).map((ele, index) => index + 2);
console.log(array); // => [ 2, 3, 4, 5, 6, 7, 8, 9 ] 
// 评论区指出有更简洁的版本, Array.from 自带的映射函数
const array = Array.from({ length: 8}, (ele, index) => index + 2);
console.log(array); // => [ 2, 3, 4, 5, 6, 7, 8, 9 ] 

函数参数解构赋值

函数参数使用解构赋值

当函数参数较多时,通常接受一个配置对象。但直接使用对象参数无法便捷地设置默认值,且在函数体内通过对象属性访问参数较为繁琐,尤其是嵌套较深或访问次数较多时。在函数参数中使用解构赋值可有效解决上述问题。


// 必须给对象参数设置默认值, 不然传参数时因为没有解构对象会报错
const getUsers = ({
 offset=0,
 limit=1,
 orderBy="salary"
}={}) => {
 // 根据条件查询数据库返回用户数据
 console.log({ offset, limit, orderBy });
}

getUsers({ offset: 10, limit: 20,orderBy: 'age' }); // => { offset: 10, limit: 20, orderBy: 'age' }
getUsers();// => { offset: 0, limit: 1, orderBy: 'salary' }

类型转换与对象处理

使用 !! 将其它类型转换成 bool 型


console.log(!!{}); // true
console.log(!!0); // false
console.log(!![]); // true
console.log(!!undefined); // false

const httpGet = (url, retry) => {
 if (!!retry) {
  // 超时重发
 }
}

JSON.stringify

深度克隆

通过先序列化再反序列化的方式克隆对象通常很方便,但其缺点是无法克隆函数及继承的属性。若需克隆函数属性,建议使用 lodash 的 cloneDeep 方法。


const me = {
  name: 'lyreal666',
  age: 23,
  speak() {
    console.log(`Hello, I'm ly!`);
  }
}

const clonedMe = JSON.parse(JSON.stringify(me));
console.log(clonedMe); // => { name: 'lyreal666', age: 23 }
console.log(clonedMe.speak === undefined); // => true

使用第二个和第三参数

JSON.stringify 的第二个参数用于处理属性值,第三个参数指定输出 JSON 字符串的缩进长度,可传入数字或字符串。


const me = {
  name: 'lyreal666',
  age: 23,
  speak() {
    console.log(`Hello, I'm ly!`);
  }
}

const jsonStr = JSON.stringify(me, (key, value) => key === 'name' ? '老余' : value, 2);

console.log(jsonStr);
/* =>
{
 "name": "老余",
 "age": 23
}
*/

优雅的遍历对像

结合解构赋值与 Object.entries 进行遍历。


const me = {
  name: 'lyreal666',
  age: 23,
  speak() {
    console.log(`Hello, I'm ly!`);
  }
}

for (const [key, value] of Object.entries(me)) {
  console.log(`${key}: ${value}`);
}

/* =>
name: lyreal666
age: 23
speak: speak() {
    console.log(`Hello, I'm ly!`);
  }
*/

数组操作技巧

清空数组的最快方法

评论区指出直接修改 length 的做法可能存在争议,此前我也关注过相关讨论。但在一般应用场景下,这种方法简单且无需为新数组重新分配内存,通常是可以接受的。


const array = [1, 2, 3, 4];
array.length = 0;
console.log(array); // => []

// 网友指出可以更好的方式是直接赋值空数组
let array = [1, 2, 3, 4];
array = [];

其他实用技巧

判断一个整数是否是 -1


// ~ 操作符的运算规律可以简单记作将加一的结果取反
console.log(~1); // => -2
console.log(~0); // => -1
console.log(~(-3)); // => 2
console.log(~(-1)); // => 0

const number = -2;

// 判断一个数是否为 -1
if (!~number) {
  // 当 number 是 -1 的操作...
}

立即执行函数

立即执行函数(IIFE)可使变量不污染外部作用域,常见用法如下:


// 使用括号将函数括起来调用
(function(window, $) {
  // 内部代码
}) (window, jQuery)

更优雅的方式如下,事实上,其他算术运算符如 +、-、*、~ 等也可实现类似效果:


! function(window, $) {
  // 内部代码
} (window, jQuery)

// 还可以使用 +, -, * 等
+ function(window, $) {
  // 内部代码
} (window, jQuery)

// 更神奇的是还可以用 new, typeof 等操作符
new function(window, $) {
  // 内部代码
} (window, jQuery);

使用 set 来对数组去重复


console.log([...new Set([1, 3, 1, 2, 2, 1])]); // => [ 1, 3, 2 ]

使用 reduce 连乘或连加


const array = [ 1, 2, 3, 4];
// 连加
console.log(array.reduce((p, c) => p + c)); // => 10
// 连乘
console.log(array.reduce((p, c) => p * c)); // => 24

取整

Math 对象提供了多种取整函数,此处主要介绍一些巧妙的取整技巧。


console.log(~~3.14); // => 3
console.log(~~(-2.5)); // => -2

console.log(6.18 | 0); // => 6
console.log(-3.6 | 0); // => -3

console.log(9.9 >> 0); // => 9
console.log(-2.1 >> 0); // => -2

// superagent 是一个很实用的发送 http 请求的 node 模块,它对返回码的处理就用到了 |
var type = status / 100 | 0;

// status / class
res.status = status;
res.statusType = type;

// basics
res.info = 1 == type;
res.ok = 2 == type;
res.clientError = 4 == type;
res.serverError = 5 == type;
res.error = 4 == type || 5 == type;

使用 + 将其它类型转换成 number 类型


console.log(+'3.14'); // => 3.14
console.log(typeof +'3.14') // => number

const sleep = (milliseconds) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(), milliseconds);
    });
}

// 当然这里可以考虑使用 console.time 来测试
! async function main() {
  const start = +new Date();
  await sleep(3000);
  const end = +new Date();
  console.log(`执行了${end - start}`); // 执行了 3002
}();

使用科学计数法表示大数字


const str1 = 'hello';
const str2 = ' world'

console.time('测试 + 拼接字符串');
for (let i = 0; i < 200000000; i++) {
  const joinedStr = str1 + str2;
}
console.timeEnd('测试 + 拼接字符串');

console.time('测试模板字符串拼接字符串');
// 使用科学计数法比打 8 个 0 方便不少
for (let i = 0; i < 2E8; i++) {
  const joinedStr =`${str1}${str2}`;
}
console.timeEnd('测试模板字符串拼接字符串')

/* =>
测试 + 拼接字符串: 3238.037ms
测试模板字符串拼接字符串: 3680.225ms
*/

交换变量值

直接利用解构赋值


let a = 666;
let b = 999;
[a, b] = [b, a];
console.log({ a, b }); // => { a: 999, b: 666 }

获取随机字符串

截取子串从索引 2 开始是为了去除 Math.random() 生成的小数部分中的“0.”字符。使用 36 进制可生成字符种类更丰富的随机字符串。


console.log(Math.random().toString(16).substring(2)); // 13位 => 45d9d0bb10b31
console.log(Math.random().toString(36).substring(2)); // 11位 => zwcx1yewjvj

扁平化数组

ES 2019 新增了 Array.prototype.flat,Chrome 73.0.3683.103 及 Node 11.13.0 已支持,但 Node LTS 10.15.3 尚不支持。此处引用自某面经的较 hack 方法,需注意根据情况处理类型转换。


const array = [1, [2, [3, 4], 5], 6, 7];
console.log(array.toString().split(',').map(ele => Number.parseInt(ele))); // => [ 1, 2, 3, 4, 5, 6, 7 ]

近期在腾讯、阿里前端实习岗位的面试经历颇具代表性,后续计划另文详述。

总结

总结

以上为本文全部内容,希望对您的学习或工作有所参考,感谢支持。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章