12个提高JavaScript技能的概念(小结)
JavaScript是一种复杂的语言。如果是你是高级或者初级JavaScript开发人员,了解它的基本概念非常重要。本文介绍JavaScript至关重要的12个概念,但绝对不是说JavaScript开发人员只需要知道这些就可以了。
1.变量赋值(值vs引用)
理解JavaScript如何给变量赋值可以帮助我们减少一些不必要的bug。如果你不理解这一点,可能很容易地编写被无意中更改值的代码。
JavaScript总是按照值来给变量赋值。这一部分非常重要:当指定的值是JavaScript的五种基本类型之一(即Boolean,null,undefined,String和Number)时,将分配实际值。但是,当指定的值是Array,Function或Object时,将分配对内存中对象的引用给变量。
在以下代码段中,使用var1对var2进行赋值。由于var1是基本类型(String),因此var2的值等于var1的String值,并且可以认为此时与var1完全不同。因此,重新赋值var2对var1没有影响。
letvar1='Mystring'; letvar2=var1; var2='Mynewstring'; console.log(var1); //'Mystring' console.log(var2); //'Mynewstring'
接着,与对象赋值进行比较。
letvar1={name:'Jim'} letvar2=var1; var2.name='John'; console.log(var1); //{name:'John'} console.log(var2); //{name:'John'}
如果你期望它会像原始类型赋值那样,很可能会出问题!如果你创建了一个无意中会改变对象的函数,就会出现一些非预期的行为。
2.闭包
闭包是一个重要的JavaScript模式,可以私有访问变量。在本例中,createGreeter返回一个匿名函数,这个函数可以访问参数greeting(在这里是“Hello”)。在后续的调用中,sayHello将有权访问这个greeting!
functioncreateGreeter(greeting){ returnfunction(name){ console.log(greeting+','+name); } } constsayHello=createGreeter('Hello'); sayHello('Joe'); //Hello,Joe
在更真实的场景中,你可以设想一个初始函数apiConnect(apiKey),它返回一些使用APIkey的方法。在这种情况下,apiKey只需要提供一次即可。
functionapiConnect(apiKey){ functionget(route){ returnfetch(`${route}?key=${apiKey}`); } functionpost(route,params){ returnfetch(route,{ method:'POST', body:JSON.stringify(params), headers:{ 'Authorization':`Bearer${apiKey}` } }) } return{get,post} } constapi=apiConnect('my-secret-key'); //NoneedtoincludetheapiKeyanymore api.get('http://www.example.com/get-endpoint'); api.post('http://www.example.com/post-endpoint',{name:'Joe'});
3.解构
JavaScript参数解构可以从对象中干中提取所需属性的常用方法。
constobj={ name:'Joe', food:'cake' } const{name,food}=obj; console.log(name,food); //'Joe''cake'
如果要以其他名称提取属性,可以使用如下方式:
constobj={ name:'Joe', food:'cake' } const{name:myName,food:myFood}=obj; console.log(myName,myFood); //'Joe''cake'
解构经常也用于直接用于提取传给函数的参数。如果你熟悉React,可能已经见过这个:
constperson={ name:'Eddie', age:24 } functionintroduce({name,age}){ console.log(`I'm${name}andI'm${age}yearsold!`); } console.log(introduce(person)); //"I'mEddieandI'm24yearsold!"
4.展开运算
ES6的一个常用之一的特性就是展开(...)运算符了,在下面的例子中,Math.max不能应用于arr数组,因为它不将数组作为参数,但它可以将各个元素作为参数传入。展开运算符...可用于提取数组的各个元素。
constarr=[4,6,-1,3,10,4]; constmax=Math.max(...arr); console.log(max); //10
5.剩余参数
剩余参数语法和展开语法看起来的一样的,不同的是展开语法是为了结构数组和对象;而剩余参数和展开运算符是相反的,剩余参数收集多个元素合成一个数组。
functionmyFunc(...args){ console.log(args[0]+args[1]); } myFunc(1,2,3,4); //3
restparameters和arguments的区别
- arguments是伪数组,包含所有的实参
- 剩余参数是标准的数组,可以使用数组的方法
6.数组方法
JavaScript数组方法通常可以提供令人难以置信的、优雅的方法来执行所需的数据转换。作为StackOverflow的贡献者,我经常看到关于如何以某种方式操纵对象数组的问题,这往往也是数组方法的完美用例。
map、filter、reduce
JavaScript数组方法map、filter和reduce容易混淆,这些都是转换数组或返回聚合值的有用方法。
map:返回一个数组,其中每个元素都使用指定函数进行过转换。
constarr=[1,2,3,4,5,6]; constmapped=arr.map(el=>el+20); console.log(mapped); //[21,22,23,24,25,26]
filter:返回一个数组,只有当指定函数返回true时,相应的元素才会被包含在这个数组中。
constarr=[1,2,3,4,5,6]; constfiltered=arr.filter(el=>el===2||el===4); console.log(filtered); //[2,4]
reduce:按函数中指定的值累加
constarr=[1,2,3,4,5,6]; constreduced=arr.reduce((total,current)=>total+current); console.log(reduced); //21
find,findIndex,indexOf
find:返回与指定条件匹配的第一个实例,如果查到不会继续查找其他匹配的实例。
constarr=[1,2,3,4,5,6,7,8,9,10]; constfound=arr.find(el=>el>5); console.log(found); //6
再次注意,虽然5之后的所有元素都满足条件,但是只返回第一个匹配的元素。当你发现匹配项时,通常会中断for循环,在这种情况下,这实际上非常有用。
findIndex:这与find几乎完全相同,但不是返回第一个匹配元素,而是返回第一个匹配元素的索引。
constarr=['Nick','Frank','Joe','Frank']; constfoundIndex=arr.findIndex(el=>el==='Frank'); console.log(foundIndex); //1
indexOf:与findIndex几乎完全相同,但它不是将函数作为参数,而是采用一个简单的值。当w你需要更简单的逻辑并且不需要使用函数来检查是否存在匹配时,可以使用此方法。
constarr=['Nick','Frank','Joe','Frank']; constfoundIndex=arr.indexOf('Frank'); console.log(foundIndex); //1
push,pop,shift,unshift
push:这是一个相对简单的方法,它将一个项添加到数组的末尾。它就地修改数组,函数本身会返回添加到数组中的项。
letarr=[1,2,3,4]; constpushed=arr.push(5); console.log(arr); //[1,2,3,4,5] console.log(pushed); //5
pop:这将从数组中删除最后一项。同样,它在适当的位置修改数组,函数本身返回从数组中删除的项。
letarr=[1,2,3,4]; constpopped=arr.pop(); console.log(arr); //[1,2,3] console.log(popped); //4
shift:从数组中删除第一项。同样,它在适当的位置修改数组。函数本身返回从数组中删除的项。
letarr=[1,2,3,4]; constshifted=arr.shift(); console.log(arr); //[2,3,4] console.log(shifted); //1
unshift:将一个或多个元素添加到数组的开头。同样,它在适当的位置修改数组。与许多其他方法不同,函数本身返回数组的新长度。
letarr=[1,2,3,4]; constunshifted=arr.unshift(5,6,7); console.log(arr); //[5,6,7,1,2,3,4] console.log(unshifted); //7
splice,slice
splice:通过删除或替换现有元素和/或添加新元素来更改数组的内容,此方法会修改了数组本身。
下面的代码示例的意思是:在数组的位置1上删除0个元素,并插入b。
letarr=['a','c','d','e']; arr.splice(1,0,'b')
slice:从指定的起始位置和指定的结束位置之前返回数组的浅拷贝。如果未指定结束位置,则返回数组的其余部分。重要的是,此方法不会修改数组,而是返回所需的子集。
letarr=['a','b','c','d','e']; constsliced=arr.slice(2,4); console.log(sliced); //['c','d'] console.log(arr); //['a','b','c','d','e']
sort
sort:根据提供的函数对数组进行排序。这个方法就地修改数组。如果函数返回负数或0,则顺序保持不变。如果返回正数,则交换元素顺序。
letarr=[1,7,3,-1,5,7,2]; constsorter=(firstEl,secondEl)=>firstEl-secondEl; arr.sort(sorter); console.log(arr); //[-1,1,2,3,5,7,7]
7.Generators(生成器)
生成器是一种特殊的行为,实际上是一种设计模式,我们通过调用next()方法来遍历一组有序的值。想象一下,例如使用遍历器对数组[1,2,3,4,5]进行遍历。第一次调用next()方法返回1,第二次调用next()方法返回2,以此类推。当数组中的所有值都返回后,调用next()方法将返回null或false或其它可能的值用来表示数组中的所有元素都已遍历完毕。
function*greeter(){ yield'Hi'; yield'Howareyou?'; yield'Bye'; } constgreet=greeter(); console.log(greet.next().value); //'Hi' console.log(greet.next().value); //'Howareyou?' console.log(greet.next().value); //'Bye' console.log(greet.next().value); //undefined
使用生成器生成无限个值:
function*idCreator(){ leti=0; while(true) yieldi++; } constids=idCreator(); console.log(ids.next().value); //0 console.log(ids.next().value); //1 console.log(ids.next().value); //2 //etc...
8.恒等运算符(===)与相等运算符(==)
大家一定要知道JavaScript中的恒等运算符(===)和相等运算符(==)之间的区别!==运算符在比较值之前会进行类型转换,而===运算符在比较之前不会进行任何类型转换。
console.log(0=='0'); //true console.log(0==='0'); //false
9.对象比较
我看到JavaScript新手所犯的错误是直接比较对象。变量指向内存中对象的引用,而不是对象本身!实际比较它们的一种方法是将对象转换为JSON字符串。这有一个缺点:对象属性顺序不能保证!比较对象的一种更安全的方法是引入专门进行深度对象比较的库(例如,lodash的isEqual)。
下面的对象看起来是相等的,但实际上它们指向不同的引用。
constjoe1={name:'Joe'}; constjoe2={name:'Joe'}; console.log(joe1===joe2); //false
相反,下面的计算结果为true,因为一个对象被设置为与另一个对象相等,因此指向相同的引用(内存中只有一个对象)。
constjoe1={name:'Joe'}; constjoe2=joe1; console.log(joe1===joe2); //true
相反,以下计算结果为true,因为一个对象设置为等于另一个对象,因此指向相同的引用(内存中只有一个对象)。
constjoe1={name:'Joe'}; constjoe2=joe1; console.log(joe1===joe2); //true
10.回调函数
很多人都被JavaScript回调函数吓倒了!他们很简单,举个例子。console.log函数作为回调传递给myFunc。它在setTimeout完成时执行。
functionmyFunc(text,callback){ setTimeout(function(){ callback(text); },2000); } myFunc('Helloworld!',console.log); //'Helloworld!'
11.Promises
一旦你理解了JavaScript回调,很快就会发现自己陷入了“回调地狱”中。这个时候可以使用promise,将异步逻辑包装在promise中,成功时resolve或在失败时reject使用“then”来处理成功的情况,使用catch来处理异常。
constmyPromise=newPromise(function(res,rej){ setTimeout(function(){ if(Math.random()<0.9){ returnres('Hooray!'); } returnrej('Ohno!'); },1000); }); myPromise .then(function(data){ console.log('Success:'+data); }) .catch(function(err){ console.log('Error:'+err); }); //IfMath.random()returnslessthan0.9thefollowingislogged: //"Success:Hooray!" //IfMath.random()returns0.9orgreaterthefollowingislogged: //"Error:Onno!"
12.Async/Await
在掌握了promise的用法后,你可能也会喜欢asyncawait,它只是一种基于promise的“语法糖”。在下面的示例中,我们创建了一个async函数,并awaitgreeterpromise。
constgreeter=newPromise((res,rej)=>{ setTimeout(()=>res('Helloworld!'),2000); }) asyncfunctionmyFunc(){ constgreeting=awaitgreeter; console.log(greeting); } myFunc(); //'Helloworld!'
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。