JavaScript中的一些实用小技巧总结


Posted in Javascript onApril 07, 2019

前言

这篇文章主要记录一下平时自己实践得到的, 博客中学习的以及在一些项目源码中看到的 javascript 技巧。有些东西可以说是奇淫技巧,有些可能是 ES6+ 中一些比较具有实用性的新语法。

&& 和 || 的妙用

有时候我们需要在某个函数或变量为 true 时执行另外一个函数。例如:

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

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

上面的 if 语句可以使用 && 直接简写为:

task1() && task2();

如果还要在 task1 失败(也就是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 的操作...
}

立即执行函数

立即执行函数可以让我们的代码中的变量不污染外部变量,常见的使用方式是像下面这样的。

// 使用括号将函数括起来调用
(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 最新的 LTS 10.15.3 还不支持, node 最新开发版 11.13.0 是支持的。这里贴一个在掘金一个兄弟面经里面看到的比较 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 ]

最近面试腾讯,阿里前端实习岗真的是一言难尽,后面打算整篇文章聊聊我最近的面试经历。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
js判断生效时间不得大于失效时间的思路及代码
Apr 23 Javascript
Javascript动态引用CSS文件的2种方法介绍
Jun 06 Javascript
基于Jquery制作图片文字排版预览效果附源码下载
Nov 18 Javascript
JavaScript小技巧整理篇(非常全)
Jan 26 Javascript
jquery ztree实现树的搜索功能
Feb 25 Javascript
AngularJS基础 ng-copy 指令实例代码
Aug 01 Javascript
JS中传递参数的几种不同方法比较
Jan 20 Javascript
require.js与bootstrap结合实现简单的页面登录和页面跳转功能
May 12 Javascript
Node.js搭建WEB服务器的示例代码
Aug 15 Javascript
解决vue axios的封装 请求状态的错误提示问题
Sep 25 Javascript
django中使用vue.js的要点总结
Jul 07 Javascript
微信小程序自定义弹窗滚动与页面滚动冲突的解决方法
Jul 16 Javascript
详解vue 不同环境配置不同的打包命令
Apr 07 #Javascript
JavaScript数组去重的几种方法
Apr 07 #Javascript
vue表单验证你真的会了吗?vue表单验证(form)validate
Apr 07 #Javascript
js中Generator函数的深入讲解
Apr 07 #Javascript
巧妙运用v-model实现父子组件传值的方法示例
Apr 07 #Javascript
vue路由导航守卫和请求拦截以及基于node的token认证的方法
Apr 07 #Javascript
vue自定义指令directive的使用方法
Apr 07 #Javascript
You might like
PHP-MySQL教程归纳总结
2008/06/07 PHP
Session保存到数据库的php类分享
2011/10/24 PHP
如何在PHP中使用正则表达式进行查找替换
2013/06/13 PHP
浅析linux下apache服务器的配置和管理
2013/08/10 PHP
thinkphp数据查询和遍历数组实例
2014/11/28 PHP
php树型类实例
2014/12/05 PHP
浅谈PHP中output_buffering
2015/07/13 PHP
THinkPHP获取客户端IP与IP地址查询的方法
2016/11/14 PHP
PHP基于phpqrcode类生成二维码的方法详解
2018/03/14 PHP
Laravel框架FormRequest中重写错误处理的方法
2019/02/18 PHP
javascript 进阶篇2 CSS XML学习
2012/03/14 Javascript
js 判断一个元素是否在页面中存在
2012/12/27 Javascript
JSON辅助格式化处理方法
2013/03/26 Javascript
使用JavaScript的ActiveXObject对象检测应用程序是否安装的方法
2014/04/15 Javascript
JS实现图片无间断滚动代码汇总
2014/07/30 Javascript
jquery地址栏链接与a标签链接匹配之特效代码总结
2015/08/24 Javascript
JavaScript数据存储 Cookie篇
2016/07/02 Javascript
Extjs表单输入框异步校验的插件实现方法
2017/03/20 Javascript
axios取消请求的实践记录分享
2018/09/26 Javascript
Vue-router 报错NavigationDuplicated的解决方法
2020/03/31 Javascript
JavaScript变量Dom对象的所有属性
2020/04/30 Javascript
js动态生成表格(节点操作)
2021/01/12 Javascript
深入讲解Java编程中类的生命周期
2016/02/05 Python
Python编写登陆接口的方法
2017/07/10 Python
python 将大文件切分为多个小文件的实例
2019/01/14 Python
使用Python进行中文繁简转换的实现代码
2019/10/18 Python
pytorch标签转onehot形式实例
2020/01/02 Python
Python基于traceback模块获取异常信息
2020/07/23 Python
详解python内置模块urllib
2020/09/09 Python
windows下python 3.9 Numpy scipy和matlabplot的安装教程详解
2020/11/28 Python
Nuts.com:优质散装,批发坚果、干果和巧克力等
2017/03/21 全球购物
Lulu Guinness露露·吉尼斯官网:红唇包
2019/02/03 全球购物
面试后感谢信
2014/02/01 职场文书
购房协议书
2014/04/11 职场文书
活动简报范文
2015/07/22 职场文书
通过Python把学姐照片做成拼图游戏
2022/02/15 Python