您应该知道的11个JavaScript和TypeScript速记
2020-09-17 17:34:19 作者: 匿名 浏览量:492次
英文 | https://blog.bitsrc.io/11-javascript-and-typescript-shorthands-you-should-know-690a002674e0
在编写简洁高效的代码与编写仅可读的代码之间有一条很好的界限。最糟糕的是,这条线不是通用的,有些人会比其他人画得更远,因此,在确定一段代码是否对每个人都足够清晰时,我们倾向于避免使用许多速记(如三元运算符) ,在线箭头功能等。
但是,丑陋的事实是:有时,这些速记法非常方便并且非常容易,以至于对阅读我们的代码有足够兴趣的任何人都可以并且将会理解它们。
因此,在本文中,我想介绍一些非常有用的(有时是晦涩的)速记,您可以在JavaScript和TypeScript中找到它们,以便您可以自己使用它们,或者至少可以使用它们,以防万一您编写代码的人重新阅读已使用它们。
1.空位合并运算符
有了这样的名字,很难相信它不是该语言中最受欢迎的运算符之一,对吗?
此运算符的要点是,如果所计算的表达式为null
或undefined
,返回的值不完全是该名称所隐含的含义,但是很好。这是您的使用方式:
function myFn(variable1, variable2) {
let var2 = variable2 ?? "default value"
return variable1 + var2
}
myFn("this has", " no default value") //returns "this has no default value"
myFn("this has no") //returns "this has no default value"
myFn("this has no", 0) //returns "this has no 0"
几乎像|| 运算符,其背后的逻辑是相同的:如果表达式的左侧计算为null或undefined它将返回右侧,否则将返回左侧。因此,对于通用默认值,可以在其中分配任何类型的值,并希望确保不必处理undefined或null那么这就是方法。
2.逻辑无效分配
这是前一个的扩展,可以同时使用??=运算符同时执行这两个操作:检查空合并值并将其分配为1。
让我们在相同的代码示例中进行另外的破解:
function myFn(variable1, variable2) {
variable2 ??= "default value"
return variable1 + var2
}
myFn("this has", " no default value") //returns "this has no default value"
myFn("this has no") //returns "this has no default value"
myFn("this has no", 0) //returns "this has no 0"
赋值运算符允许我们检查variable2的值,如果它的值为null或undefined则赋值将通过,否则将永远不会发生。
警告:此语法可能会使其他对该运算符不熟悉的人感到困惑,因此,如果您使用它,则最好在一行注释中说明正在发生的事情。
3. TypeScript的构造函数速记
这是TypeScript特有的,如果您是JavaScript的纯粹主义者,那您就错了!(不,只是在开玩笑,但是您不能使用普通JS来做到这一点)。
您知道在定义类时通常如何列出所有属性及其相应的可见性,然后在构造函数中分配它们的值吗?好吧,对于那些您的构造函数非常简单并且您只是将接收到的值分配为参数的情况,这是一个简写。
让我给你演示:
//Old way...
class Person {
private first_name: string;
private last_name: string;
private age: number;
private is_married: boolean;
constructor(fname:string, lname:string, age:number, married:boolean) {
this.first_name = fname;
this.last_name = lname;
this.age = age;
this.is_married = married;
}
}
//New, shorter way...
class Person {
constructor( private first_name: string,
private last_name: string,
private age: number,
private is_married: boolean){}
}
这绝对是一个节省时间的程序,特别是如果您有一个具有很多属性的类。
本质上,您要确保的是,不要忘了在构造函数之后添加{} ,因为这是函数的主体。就是这样,其余的工作由编译器完成,了解了我们要实现的目标,它将把两个版本的代码转换为相同JavaScript代码段。
提示:使用Bit ( Github )在项目之间共享可重复使用的模块/ 组件 。Bit使共享,记录和组织来自任何项目的独立组件变得简单。
使用它可以最大程度地重复使用代码,在独立组件上进行协作并构建可扩展的应用程序。
4.三元运算符
该代码相对易于阅读,并且倾向于代替单行的IF..ELSE语句使用,因为它删除了许多不需要的字符并将四行变为一。
// Original IF..ELSE statement
let isOdd = ""
if(variable % 2 == 0) {
isOdd = "yes"
} else {
isOdd = "no"
}
//The ternary approach
let isOdd = (variable % 2 == 0) ? "yes" : "no"
您可以看到三元运算符的结构首先具有布尔表达式,然后是在表达式为true的情况下的“ return”语句和表达式为false的情况的“ return”语句。尽管最好在赋值的右侧使用(如示例中所示),但也可以将其单独用作执行函数调用的方式,具体取决于布尔表达式的值。
let variable = true;
(variable) ? console.log("It s TRUE") : console.log("It s FALSE")
请注意,格式是相同的,这里的问题是,如果将来您需要在此处扩展其中一个部分(对于表达式为true或false的情况),则必须将其变成完整的IF..ELSE语句。
5.利用OR的惰性评估
在JavaScript(以及TypeScript)中, OR逻辑运算符遵循一个惰性评估模型,这意味着它将返回第一个返回true的表达式,而不会继续检查其余表达式。
这意味着,如果您具有以下IF语句,则仅对前两个表达式进行求值:
if( expression1 || expression2 || expression3 || expression4)
假设expression1是falsy (即它返回一个评估为false的值),而expression2是truthy (即它返回一个评估为true的值),则评估将在那里停止。
我们可以利用这种惰性计算,而不是在IF语句中使用它,而可以将其用作分配的一部分,以便在表达式失败或undefined情况下提供默认值:
function myFn(variable1, variable2) {
let var2 = variable2 || "default value"
return variable1 + var2
}
myFn("this has", " no default value") //returns "this has no default value"
myFn("this has no") //returns "this has no default value"
上面的示例显示了如何使用OR运算符为函数的第二个参数设置默认值。现在,如果您仔细观察,将会发现这种方法存在一个小问题:如果variable2的值为0或一个空字符串,则将在var2上设置默认值,因为它们的值都为false 。
因此,如果您的用例也允许将falsy值设为有效值,那么您可能希望查看一个鲜为人知的操作数,称为“空值合并运算符”。
6.双按位NOT运算符
逐位运算符是我们倾向于远离的运算符,因为老实说,如今谁需要考虑位呢?事实是,由于它们直接在数字的位上工作的方式,因此它们执行操作的速度比正常方法调用快得多。
在这种情况下,按位NOT运算符(即〜)将获取您的数字,将其转换为32位整数(丢弃任何多余的位),然后将其所有位求反,实际上将值x任何整数转换为-(x+1) 。为什么我们关心此运算符?因为如果在相同的值上使用两次,则得到的结果与Math.floor方法相同。
let x = 3.8
let y = ~x //this turns x into -(3 + 1), remember, the number gets turned into an integer
let z = ~y //this turns y (which is -4) into -(-4 + 1) which is 3
//So you can do:
let flooredX = ~~x //and this performs both steps from above at the same time
请注意最后一行上的double〜,虽然看起来很奇怪,但是如果您不得不处理将多个浮点数转换为整数的情况,那么这对您来说可能是一个很好的速记。
7.对象属性分配
在为属性分配值时,ES6简化了对象创建的过程。如果将值分配给与对象属性完全一样命名的变量,则不再需要像以前一样重复名称:
let name:string = "Fernando";
let age:number = 36;
let id:number = 1;
type User = {
name: string,
age: number,
id: number
}
//Old way
let myUser: User = {
name: name,
age: age,
id: id
}
//new way
let myNewUser: User = {
name,
age,
id
}
如您所见,新方法肯定更短,更容易编写,同时又不难阅读(不同于本文中显示的其他速记技巧)。
8.箭头函数的隐式返回
您是否知道只有一行长的箭头函数也会返回该行代码的结果?
本质上,此技巧使您可以保存冗余的return语句。查找正在使用的这些速记类型的一个非常普遍的情况是在数组方法上,例如filter或map ,如下所示:
let myArr:number[] = [1,2,3,4,5,6,7,8,9,10]
//Long way of doing it:
let oddNumbers:number[] = myArr.filter( (n:number) => {
return n % 2 == 0
})
let double:number[] = myArr.map( (n:number) => {
return n * 2;
})
//Shorter way:
let oddNumbers2:number[] = myArr.filter( (n:number) => n % 2 == 0 )
let double2:number[] = myArr.map( (n:number) => n * 2 )
这一代码不必增加代码的复杂性,它是清理语法的一种好方法,可以消除不必要的空格和行。当然,这里的缺点是,如果您需要在这些行中添加额外的逻辑,则必须重新添加大括号。
这里唯一需要注意的是,无论您试图在单行函数上执行什么,都必须是一个表达式(即可以返回的内容),否则它将不起作用。例如,您不能拥有像这样的单线:
const m = _ => if(2) console.log("true") else console.log("false")
在下一个示例中,您将看到另一个需要大括号的单线示例,让我们继续。
9.默认功能参数
感谢ES6,您现在可以在函数参数上指定默认值。在以前JavaScript版本中,这是不可能的,因此您必须诉诸于使用OR的惰性评估之类的方法。
但是现在它就像编写代码一样简单:
//We can function without the last 2 parameter because a default value
//can be assigned to them
function myFunc(a, b, c = 2, d = "") {
//your logic goes here...
}
很简单,不是吗?好吧,它实际上会更有趣,因为该值可以是任何值,包括一个函数调用,如果您不使用自己的值覆盖它,该调用将被执行,从而使您也可以轻松实现强制性的函数参数模式。看看这个:
const mandatory = _ => {
throw new Error("This parameter is mandatory, don t ignore it!")
}
function myFunc(a, b, c = 2, d = mandatory()) {
//your logic goes here...
}
//Works great!
myFunc(1,2,3,4)
//Throws an error
myFunc(1,2,3)
就像我说的那样,单行mandatory需要使用大括号,因为它使用的是throw ,它是语句而不是表达式。但是,您仍然可以很轻松地获得很酷的强制参数行为。
10.用!!将任何值转换为布尔值!
在类似于双按位NOT运算符的注释中,可以使用双逻辑NOT运算符将任何值转换为布尔值。
!!23 // TRUE
!!"" // FALSE
!!0 // FALSE
!!{} // TRUE
单个逻辑非将已经为您完成此操作,它将强制将值转换为布尔值,然后将其取反,因此第二个逻辑非将负责再次对其求反,从而将其返回给它。原义,同时将其保留为布尔型。
这是速记在那些情况下,你要么必须确保你分配一个实际的布尔有用(如类型的打字稿变量boolean ),或在做对了严格的比较,无论是true或false (与=== )。
11.解构和传播运营商
关于这两个主题,有很多要说的地方,只要正确使用它们,它们都可以产生非常有趣的结果。但是对于本文,让我快速向您展示如何利用两者来简化某些任务。
将对象分解为多个变量
您是否曾经将一堆不同的对象属性分配给各个变量?例如,如果您需要在不影响原始对象的情况下单独处理这些值(例如,通过修改它们),这实际上很常见。
解构可以帮助您在一行代码中做到这一点:
const myObj = {
name: "Fernando",
age: 37,
country: "Spain"
}
//Old way of doing this:
const name = myObj.name;
const age = myObj.age;
const country = myObj.country;
//Using destructuring
const {name, age, country} = myObj;
如果您以前使用过TypeScript,那么此语法也可以视为import语句的一部分,因为它使您可以单独导入某些导出的方法库,而不必将名称空间与许多不需要的功能相提并论。
const { get } from lodash
例如,上面的那一行仅允许您将lodash库中的get方法添加到您的命名空间中,而无需添加该库的其余部分,该库中还有很多其他方法。
传播合并
使用spread运算符,您可以简化将数组和对象合并为一行代码的任务,而无需调用任何其他方法:
const arr1 = [1,2,3,4]
const arr2 = [5,6,7]
const finalArr = [...arr1, ...arr2] // [1,2,3,4,5,6,7]
const partialObj1 = {
name: "fernando"
}
const partialObj2 = {
age:37
}
const fullObj = { ...partialObj1, ...partialObj2 } // {name: "fernando", age: 37}
请注意,如果对象名称相同,则合并此类对象将导致属性被覆盖。但是,数组不会发生相同的情况,将添加重复的值,如果您也想避免这种情况,则必须使用Set 。
两者结合
您甚至可以结合使用解构和散布运算符来获得有趣的结果,例如删除数组的第一个元素,而其余元素保持不变(即常见的头尾示例以及可以在Python和其他语言中找到的列表)。甚至从对象中提取一些属性,其余部分保持不变,如下所示:
const myList = [1,2,3,4,5,6,7]
const myObj = {
name: "Fernando",
age: 37,
country: "Spain",
gender: "M"
}
const [head, ...tail] = myList
const {name, age, ...others} = myObj
console.log(head) //1
console.log(tail) //[2,3,4,5,6,7]
console.log(name) //Fernando
console.log(age) //37
console.log(others) //{country: "Spain", gender: "M"}
请注意,分配左侧的扩展运算符必须用作最后一项。您不能先使用价差然后添加如下所示的单个变量:
const [...values, lastItem] = [1,2,3,4]
上面的示例将失败。
结论
速记有很多,但是请记住,保存的代码越多,对于不习惯这些速记的其他人来说,可读性就越差。这并不是要压缩代码,也不是隐式地假设更少的代码行将导致性能更高的代码。这只是为了从语法中删除多余或不必要的构造,以简化阅读任务。
因此,请尝试在速记和可读代码之间保持健康的平衡,以使每个人都满意(请记住,您不是唯一阅读代码的人!)。