4月10-12日 QCon 北京,与全球 140+ 顶尖工程师共同解构 AI 时代的技术浪潮! 了解详情
写点什么

学会这些 JS 小技巧,提升编码幸福度

  • 2019-09-05
  • 本文字数:5647 字

    阅读完需:约 19 分钟

学会这些JS小技巧,提升编码幸福度

本文主要介绍一些 JS 中用到的小技巧,可以在日常 Coding 中提升幸福度~

类型强制转换

string 强制转换为数字

可以用*1来转化为数字(实际上是调用.valueOf方法)


然后使用Number.isNaN来判断是否为NaN,或者使用 a !== a 来判断是否为NaN,因为 NaN !== NaN


'32' * 1            // 32'ds' * 1            // NaNnull * 1            // 0undefined * 1    // NaN1  * { valueOf: ()=>'3' }        // 3
复制代码


常用:也可以使用+来转化字符串为数字


+ '123'            // 123+ 'ds'               // NaN+ ''                    // 0+ null              // 0+ undefined    // NaN+ { valueOf: ()=>'3' }    // 3
复制代码

object 强制转化为 string

可以使用 字符串+Object 的方式来转化对象为字符串(实际上是调用 .toString() 方法)


'the Math object:' + Math                // "the Math object:[object Math]"'the JSON object:' + JSON              // "the JSON object:[object JSON]"
复制代码


当然也可以覆盖对象的toStringvalueOf方法来自定义对象的类型转换:


2  * { valueOf: ()=>'3' }                // 6'J' + { toString: ()=>'S' }                // "JS"
复制代码


《Effective JavaScript》P11:当+用在连接字符串时,当一个对象既有toString方法又有valueOf方法时候,JS 通过盲目使用valueOf方法来解决这种含糊。


对象通过valueOf方法强制转换为数字,通过toString方法强制转换为字符串


'' + {toString:()=>'S',valueOf:()=>'J'}                // J
复制代码

使用 Boolean 过滤数组中的所有假值

我们知道 JS 中有一些假值:falsenull0""undefinedNaN,怎样把数组中的假值快速过滤呢,可以使用 Boolean 构造函数来进行一次转换


const compact = arr => arr.filter(Boolean)compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34])             // [ 1, 2, 3, 'a', 's', 34 ]
复制代码

双位运算符 ~~

可以使用双位操作符来替代 Math.floor( )。双否定位操作符的优势在于它执行相同的操作运行速度更快。


Math.floor(4.9) === 4      //true// 简写为:~~4.9 === 4      //true
复制代码


不过要注意,对整数来说 ~~ 运算结果与 Math.floor( ) 运算结果相同,而对于负数来说不相同:


~~4.5            // 4Math.floor(4.5)        // 4~~-4.5        // -4Math.floor(-4.5)        // -5
复制代码

短路运算符

我们知道逻辑与&&与逻辑或||是短路运算符,短路运算符就是从左到右的运算中前者满足要求,就不再执行后者了;


可以理解为:


  • &&为取假运算,从左到右依次判断,如果遇到一个假值,就返回假值,以后不再执行,否则返回最后一个真值。

  • ||为取真运算,从左到右依次判断,如果遇到一个真值,就返回真值,以后不再执行,否则返回最后一个假值。


let param1 = expr1 && expr2let param2 = expr1 || expr2
复制代码



因此可以用来做很多有意思的事,比如给变量赋初值:


let variable1let variable2 = variable1  || 'foo'    
复制代码


如果 variable1 是真值就直接返回了,后面短路就不会被返回了,如果为假值,则会返回后面的foo


也可以用来进行简单的判断,取代冗长的if语句:


let variable = param && param.prop
复制代码


如果param如果为真值则返回param.prop属性,否则返回param这个假值,这样在某些地方防止paramundefined的时候还取其属性造成报错。

取整 | 0

对一个数字| 0可以取整,负数也同样适用,num | 0


1.3 | 0         // 1-1.9 | 0        // -1
复制代码

判断奇偶数 & 1

对一个数字& 1可以判断奇偶数,负数也同样适用,num & 1


const num=3;!!(num & 1)          // true!!(num % 2)          // true
复制代码

函数

函数默认值

func = (l, m = 3, n = 4 ) => (l * m * n);func(2)             //output: 24
复制代码


注意,传入参数为undefined或者不传入的时候会使用默认参数,但是传入null还是会覆盖默认参数。

强制参数

默认情况下,如果不向函数参数传值,那么 JS 会将函数参数设置为undefined。其它一些语言则会发出警告或错误。要执行参数分配,可以使用if语句抛出未定义的错误,或者可以利用强制参数


mandatory = ( ) => {  throw new Error('Missing parameter!');}foo = (bar = mandatory( )) => {            // 这里如果不传入参数,就会执行manadatory函数报出错误  return bar;}
复制代码

隐式返回值

返回值是我们通常用来返回函数最终结果的关键字。只有一个语句的箭头函数,可以隐式返回结果(函数必须省略大括号{ },以便省略返回关键字)。


要返回多行语句(例如对象文本),需要使用( )而不是{ }来包裹函数体。这样可以确保代码以单个语句的形式进行求值。


function calcCircumference(diameter) {  return Math.PI * diameter}// 简写为:calcCircumference = diameter => (  Math.PI * diameter;)
复制代码

惰性载入函数

在某个场景下我们的函数中有判断语句,这个判断依据在整个项目运行期间一般不会变化,所以判断分支在整个项目运行期间只会运行某个特定分支,那么就可以考虑惰性载入函数


function foo(){    if(a !== b){        console.log('aaa')    }else{        console.log('bbb')    }}
// 优化后function foo(){ if(a != b){ foo = function(){ console.log('aaa') } }else{ foo = function(){ console.log('bbb') } } return foo();}
复制代码


那么第一次运行之后就会覆写这个方法,下一次再运行的时候就不会执行判断了。当然现在只有一个判断,如果判断很多,分支比较复杂,那么节约的资源还是可观的。

一次性函数

跟上面的惰性载入函数同理,可以在函数体里覆写当前函数,那么可以创建一个一次性的函数,重新赋值之前的代码相当于只运行了一次,适用于运行一些只需要执行一次的初始化代码


var sca = function() {    console.log('msg')    sca = function() {        console.log('foo')    }}sca()        // msgsca()        // foosca()        // foo
复制代码

代码复用

Object [key]

虽然将 foo.bar 写成 foo ['bar'] 是一种常见的做法,但是这种做法构成了编写可重用代码的基础。许多框架使用了这种方法,比如 element 的表单验证


请考虑下面这个验证函数的简化示例:


function validate(values) {  if(!values.first)    return false;  if(!values.last)    return false;  return true;}console.log(validate({first:'Bruce',last:'Wayne'})); // true
复制代码


上面的函数完美的完成验证工作。但是当有很多表单,则需要应用验证,此时会有不同的字段和规则。如果可以构建一个在运行时配置的通用验证函数,会是一个好选择。


// object validation rulesconst schema = {  first: {    required:true  },  last: {    required:true  }}
// universal validation functionconst validate = (schema, values) => { for(field in schema) { if(schema[field].required) { if(!values[field]) { return false; } } } return true;}console.log(validate(schema, {first:'Bruce'})); // falseconsole.log(validate(schema, {first:'Bruce',last:'Wayne'})); // true
复制代码


现在有了这个验证函数,我们就可以在所有窗体中重用,而无需为每个窗体编写自定义验证函数。

数字字面量

不同进制表示法

ES6 中新增了不同进制的书写格式,在后台传参的时候要注意这一点。


29            // 10进制035            // 8进制29      原来的方式0o35            // 8进制29      ES6的方式0x1d            // 16进制290b11101            // 2进制29
复制代码

精确到指定位数的小数

将数字四舍五入到指定的小数位数。使用 Math.round() 和模板字面量将数字四舍五入为指定的小数位数。 省略第二个参数 decimals ,数字将被四舍五入到一个整数。


const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`)round(1.345, 2)         // 1.35round(1.345, 1)         // 1.3
复制代码

数字补 0 操作

感谢网友 @JserWang @vczhan 提供 这个小技巧


有时候比如显示时间的时候有时候会需要把一位数字显示成两位,这时候就需要补 0 操作,可以使用slice和 string 的padStart方法


const addZero1 = (num, len = 2) => (`0${num}`).slice(-len)const addZero2 = (num, len = 2) => (`${num}`).padStart( len   , '0')addZero1(3) // 03 addZero2(32,4)  // 0032
复制代码

数组

reduce 方法同时实现 map 和 filter

假设现在有一个数列,你希望更新它的每一项(map 的功能)然后筛选出一部分(filter 的功能)。如果是先使用 map 然后 filter 的话,你需要遍历这个数组两次。


在下面的代码中,我们将数列中的值翻倍,然后挑选出那些大于 50 的数。


const numbers = [10, 20, 30, 40];const doubledOver50 = numbers.reduce((finalList, num) => {  num = num * 2;  if (num > 50) {    finalList.push(num);  }  return finalList;}, []);doubledOver50;            // [60, 80]
复制代码

统计数组中相同项的个数

很多时候,你希望统计数组中重复出现项的个数然后用一个对象表示。那么你可以使用 reduce 方法处理这个数组。


下面的代码将统计每一种车的数目然后把总数用一个对象表示。


var cars = ['BMW','Benz', 'Benz', 'Tesla', 'BMW', 'Toyota'];var carsObj = cars.reduce(function (obj, name) {  obj[name] = obj[name] ? ++obj[name] : 1;  return obj;}, {});carsObj; // => { BMW: 2, Benz: 2, Tesla: 1, Toyota: 1 }
复制代码

使用解构来交换参数数值

有时候你会将函数返回的多个值放在一个数组里。我们可以使用数组解构来获取其中每一个值。


let param1 = 1;let param2 = 2;[param1, param2] = [param2, param1];console.log(param1) // 2console.log(param2) // 1
复制代码


当然我们关于交换数值有不少其他办法:


var temp = a; a = b; b = temp                b = [a, a = b][0]                         a = a + b; b = a - b; a = a - b      
复制代码

接收函数返回的多个结果

在下面的代码中,我们从/post 中获取一个帖子,然后在/comments 中获取相关评论。由于我们使用的是 async/await,函数把返回值放在一个数组中。而我们使用数组解构后就可以把返回值直接赋给相应的变量。


async function getFullPost(){  return await Promise.all([     fetch('/post'),     fetch('/comments')  ]);}const [post, comments] = getFullPost();
复制代码

将数组平铺到指定深度

使用递归,为每个深度级别 depth 递减 1 。 使用 Array.reduce()Array.concat() 来合并元素或数组。 基本情况下,depth 等于 1 停止递归。 省略第二个参数,depth 只能平铺到 1 (单层平铺) 的深度。


const flatten = (arr, depth = 1) =>  depth != 1    ? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flatten(v, depth - 1) : v), [])    : arr.reduce((a, v) => a.concat(v), []);flatten([1, [2], 3, 4]);                         // [1, 2, 3, 4]flatten([1, [2, [3, [4, 5], 6], 7], 8], 2);           // [1, 2, 3, [4, 5], 6, 7, 8]
复制代码

数组的对象解构

数组也可以对象解构,可以方便的获取数组的第 n 个值


const csvFileLine = '1997,John Doe,US,john@doe.com,New York';const { 2: country, 4: state } = csvFileLine.split(',');
country // USstate // New Yourk
复制代码

对象

使用解构删除不必要属性

有时候你不希望保留某些对象属性,也许是因为它们包含敏感信息或仅仅是太大了(just too big)。你可能会枚举整个对象然后删除它们,但实际上只需要简单的将这些无用属性赋值给变量,然后把想要保留的有用部分作为剩余参数就可以了。


下面的代码里,我们希望删除_internal 和 tooBig 参数。我们可以把它们赋值给 internal 和 tooBig 变量,然后在 cleanObject 中存储剩下的属性以备后用。


let {_internal, tooBig, ...cleanObject} = {el1: '1', _internal:"secret", tooBig:{}, el2: '2', el3: '3'};
console.log(cleanObject); // {el1: '1', el2: '2', el3: '3'}
复制代码

在函数参数中解构嵌套对象

在下面的代码中,engine 是对象 car 中嵌套的一个对象。如果我们对 engine 的 vin 属性感兴趣,使用解构赋值可以很轻松地得到它。


var car = {  model: 'bmw 2018',  engine: {    v6: true,    turbo: true,    vin: 12345  }}const modelAndVIN = ({model, engine: {vin}}) => {  console.log(`model: ${model} vin: ${vin}`);}modelAndVIN(car); // => model: bmw 2018  vin: 12345
复制代码

代码复用

Object [key]

虽然将 foo.bar 写成 foo ['bar'] 是一种常见的做法,但是这种做法构成了编写可重用代码的基础。许多框架使用了这种方法,比如 element 的表单验证


请考虑下面这个验证函数的简化示例:


function validate(values) {  if(!values.first)    return false;  if(!values.last)    return false;  return true;}console.log(validate({first:'Bruce',last:'Wayne'})); // true
复制代码


上面的函数完美的完成验证工作。但是当有很多表单,则需要应用验证,此时会有不同的字段和规则。如果可以构建一个在运行时配置的通用验证函数,会是一个好选择。


// object validation rulesconst schema = {  first: {    required:true  },  last: {    required:true  }} // universal validation functionconst validate = (schema, values) => {  for(field in schema) {    if(schema[field].required) {      if(!values[field]) {        return false;      }    }  }  return true;}console.log(validate(schema, {first:'Bruce'})); // falseconsole.log(validate(schema, {first:'Bruce',last:'Wayne'})); // true
复制代码


现在有了这个验证函数,我们就可以在所有窗体中重用,而无需为每个窗体编写自定义验证函数。


网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~

参考资料


推介阅读:

ES6演示小网站


2019-09-05 13:007504

评论 3 条评论

发布
用户头像
async function getFullPost(){
return await Promise.all([
fetch('/post'),
fetch('/comments')
]);
}
为什么不写成
function getFullPost(){
return Promise.all([
fetch('/post'),
fetch('/comments')
]);
}
展开
2019-09-06 17:02
回复
用户头像
可读性是最重要的,很多团队并不欢迎这些技巧
2019-09-06 09:46
回复
用户头像
你好,请问我可以转载到我的博客上吗?不做商业用途,仅交流学习使用。
2019-09-06 09:27
回复
没有更多了
发现更多内容

安卓??传奇!!探寻世界最大操作系统的崛起之路

博文视点Broadview

大数据分析如何进行?瓴羊Quick BI成为了很重要的工具

小偏执o

Centos7安装Mysql5.7(超详细版)

A-刘晨阳

MySQL Linux 运维 11月月更

【Linux】之【CPU】相关的命令及解析[lscpu、mpstat]

A-刘晨阳

Linux 运维 cpu 命令 11月月更

炎凰数据完成超亿元 A1 和 A1+ 轮融资,推出异构数据即时分析平台

晨山资本

大数据 大数据处理 大数据分析

自动驾驶的「数据引擎」,该如何“降本”、“增效”和“精准化”?

澳鹏Appen

人工智能 自动驾驶 无人驾驶 智能驾驶 数据标注

SAP ERP系统里的那些核心主数据

SAP虾客

混合开发Hybrid App的优劣

Onegun

ios 混合应用开发 移动端 andiod 混合开发

开源共建 | 中国移动冯江涛:ChunJun(原FlinkX)在数据入湖中的应用

袋鼠云数栈

flink 开源

【昇思生态城市行】南京站圆满举办, 昇腾携手伙伴见证多项重磅发布!

Geek_2d6073

那些适用于跨境电商的ERP系统

SAP虾客

学会二阶思维,你就能像巴菲特一样思考了

华为云开发者联盟

软件开发 开发 华为云 二阶思维

室内高清led电子显示屏的定义

Dylan

LED显示屏 全彩LED显示屏 led显示屏厂家

Karmada跨集群优雅故障迁移特性解析

华为云开发者联盟

云原生 后端 华为云

centos安装python3/pip3项目所需的第三方模块(在线安装&&离线安装)

A-刘晨阳

Linux 运维 Python3 11月月更 pip3

与时俱进「风险系统保障质量之路」非同寻常

京东科技开发者

自动化 风险识别 风险控制 预警监控 风险系统

你可见过如此细致的延时任务详解

骑牛上青山

Java redis kafka 延时队列

ERP系统能给企业带来的那些好处

SAP虾客

5种典型 API 攻击及预防建议

SEAL安全

API API安全

算法基础:区间合并算法及模板应用

timerring

11月月更 区间合并 算法学习

桌面端软件的开发框架如何选型

Onegun

macos windows 桌面端 桌面应用

云小课|云小课带您快速了解LTS可视化查看

华为云开发者联盟

云计算 后端 华为云

聊聊Mybatis的数据源之PooledDataSource

急需上岸的小谢

11月月更

动手实践丨基于ModelAtrs使用A2C算法制作登月器着陆小游戏

华为云开发者联盟

人工智能 华为云 A2C算法

高并发下丢失更新的解决方案

京东科技开发者

幻读 脏读 不可重复读 更新丢失

聊聊Mybatis的数据源之工厂模式

急需上岸的小谢

11月月更

百度架构师手写万字Spring Security实战笔记,一篇就搞懂

小小怪下士

Java spring springsecurity

PCB layout有DRC,为什么还要用CAM和DFM检查?

华秋PCB

PCB PCB设计 PCB工具

江西省四家等保测评机构名单详解

行云管家

江西 等保测评 等保测评机构

ERP 实施,甲方公司前期应该准备什么?

SAP虾客

主流BI软件,哪一个软件使用效果更好?

夏日星河

学会这些JS小技巧,提升编码幸福度_语言 & 开发_HERlocked93_InfoQ精选文章