程序员必须掌握的 12 个 JavaScript 技能!

百家 作者:CSDN 2019-03-07 09:53:06

本文论述的12个概念,对于 JavaScript 开发者来说都是非常重要的。

作者 |?Nick Scialli

译者 | 谭开朗

责编 |?屠敏

出品 |?CSDN(ID:CSDNNews)

以下为译文:

JavaScript 是一种复杂的编程语言。无论你的开发水平如何,理解 JavaScript 的基础概念都尤为重要。

本文将与大家分享 12 个实用的 JavaScript 技能,未来我也将在 Github 的 JS Tips&Tidbits 仓库(https://github.com/nas5w/javascript-tips-and-tidbits)中持续更新此概念列表。


值和引用变量的赋值


理解 JavaScript 的赋值原理是正确编码的基础,否则,很容易在编码过程中无意篡改值。

JavaScript 通常是通过值来赋值变量。但需要注意一点:JavaScript 基本数据类型(布尔值、null、undefined、字符串和数字)的赋值是拷贝赋值,而数组,函数或对象的赋值是引用赋值。

举个例子。下面的代码,把 var1 赋值给 var2。因为 var1 是基本数据类型(字符串),所以 var2 等于 var1 的字符串值,或者说 var2 全等于 var1。因此,给 var2 重新赋值不会影响到 var1。

let?var1?=?'My?string';
let?var2?=?var1;
var2?=?'My?new?string';
console.log(var1);
//?'My?string'
console.log(var2);
//?'My?new?string'

让我们对比一下给对象赋值。

let?var1?=?{?name:?'Jim'?}
let?var2?=?var1;
var2.name?=?'John';
console.log(var1);
//?{?name:?'John'?}
console.log(var2);
//?{?name:?'John'?}

如果还像上面那样原始赋值,就出现问题了。如果创建了一个会篡改值的函数,那情况将会更糟糕。


闭包


闭包是一种重要的 JavaScript 模式,用于对变量进行私有访问。如下例子中,createGreeter 函数返回一个能访问 greeting 值为“Hello”的匿名函数。往后每引用一次 sayHello 变量都会访问该 greeting 值。

function?createGreeter(greeting)?{
??return?function(name)?{
????console.log(greeting?+?',?'?+?name);
??}
}
const?sayHello?=?createGreeter('Hello');
sayHello('Joe');
//?Hello,?Joe

在更真实的案例场景中,假设一个初始函数 apiConnect(apiKey) 返回一些引用 API key 的方法。在这种情况下,初始函数的内部参数 apiKey 只需赋值一次,往后无需重新赋值。

function?apiConnect(apiKey)?{
??function?get(route)?{
????return?fetch(`${route}?key=${apiKey}`);
??}
??function?post(route,?params)?{
????return?fetch(route,?{
??????method:?'POST',
??????body:?JSON.stringify(params),
????????headers:?{
??????????'Authorization':?`Bearer?${apiKey}`
????????}
??????})
??}
??return?{?get,?post?}
}
const?api?=?apiConnect('my-secret-key');
//?No?need?to?include?the?apiKey?anymore
api.get('http://www.example.com/get-endpoint');
api.post('http://www.example.com/post-endpoint',?{?name:?'Joe'?});


解构


别忘了学习 JavaScript 参数的解构赋值。这是一种简洁地提取对象属性的通用方法。

const?obj?=?{
??name:?'Joe',
??food:?'cake'
}
const?{?name,?food?}?=?obj;
console.log(name,?food);
//?'Joe'?'cake'

要想提取不同名的属性,可以使用以下格式来指定它们。

const?obj?=?{
??name:?'Joe',
??food:?'cake'
}
const?{?name:?myName,?food:?myFood?}?=?obj;
console.log(myName,?myFood);
//?'Joe'?'cake'

在下面的例子中,解构简洁地向 introduce 函数传递 person 对象参数。换言之,解构可以(通常用来)直接提取参数同时传递给一个函数。如果你熟悉 React,那很可能在之前就见到过此用法。

const?person?=?{
??name:?'Eddie',
??age:?24
}
function?introduce({?name,?age?})?{
??console.log(`I'm?${name}?and?I'm?${age}?years?old!`);
}
console.log(introduce(person));
//?"I'm?Eddie?and?I'm?24?years?old!"


展开语法


展开语法是个比较难理解的 JavaScript 概念,叫扩展运算符会相对容易理解。在下面的例子中,Math.max 方法不会作用于数组 arr,因为 Math.max 方法的参数只能是单个元素而不能是一个数组。扩展运算符可以从数组中提取出单个元素。

const?arr?=?[4,?6,?-1,?3,?10,?4];
const?max?=?Math.max(...arr);
console.log(max);
//?10


剩余语法


让我们来谈谈 JavaScript 的剩余语法。剩余语法可以通过一个函数将任意数量的参数收集到一个数组中。

function?myFunc(...args)?{
??console.log(args[0]?+?args[1]);
}
myFunc(1,?2,?3,?4);
//?3


数组方法


JavaScript 数组方法可以神奇且优雅的进行数据转换。作为 StackOverflow 的贡献者之一,我经常看到关于如何用某种方法操作数组对象的问题。这往往是数组方法的最佳用例。

我将在这里用类似的表达方式(某些已合并在内)来介绍多种不同的数组方法。以下罗列的方法并不全面:你也可以参考 MDN(我喜爱的 JavaScript 参考资料:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#)回顾和实践所有方法。

map, filter, reduce

JavaScript 的 map、filter、reduce 数组方法很容易被混淆。这些方法是用来转换数组或者返回值的集合。

  • map:将调用的数组的每个元素传递给指定的函数,并返回一个数组。

const?arr?=?[1,?2,?3,?4,?5,?6];
const?mapped?=?arr.map(el?=>?el?+?20);
console.log(mapped);
//?[21,?22,?23,?24,?25,?26]
  • filter:返回的数组元素是函数逻辑为真的一个子集。

const?arr?=?[1,?2,?3,?4,?5,?6];
const?filtered?=?arr.filter(el?=>?el?===?2?||?el?===?4);
console.log(filtered);
//?[2,?4]
  • reduce:按函数方法计算值。

const?arr?=?[1,?2,?3,?4,?5,?6];
const?reduced?=?arr.reduce((total,?current)?=>?total?+?current);
console.log(reduced);
//?21

find, findIndex, indexOf

JavaScript 的 find, findIndex, indexOf 数组方法通常被合并。用法如下。

  • find:返回与指定条件匹配的第一个元素,不再往后匹配。

const?arr?=?[1,?2,?3,?4,?5,?6,?7,?8,?9,?10];
const?found?=?arr.find(el?=>?el?>?5);
console.log(found);
//?6

再次注意,(上面的例子中)虽然5以后的所有值都符合条件,但也只返回第一个匹配的元素。这在 for 循环中匹配某个条件后便跳出循环的情况下非常有用!

  • findIndex:和 find 用法相同,不过不是返回第一个匹配的元素,而是返回该匹配元素的索引值。为清晰可见,以下面的名字数组为例,而不是数字数组。

const?arr?=?['Nick',?'Frank',?'Joe',?'Frank'];
const?foundIndex?=?arr.findIndex(el?=>?el?===?'Frank');
console.log(foundIndex);
//?1
  • indexOf:和 findIdex 用法相同,但它不以函数作为参数,它的参数是一个简单的值。适用于简单逻辑或不需要要函数进行判定的情况。

const?arr?=?['Nick',?'Frank',?'Joe',?'Frank'];
const?foundIndex?=?arr.indexOf('Frank');
console.log(foundIndex);
//?1

push, pop, shift, unshift

有很多很棒的数组方法可以给目标数组添加或删除元素。

  • push:这是一种将元素添加至数组末尾的相对简单的方法。它修改数组值,函数本身返回压入数组的值。

let?arr?=?[1,?2,?3,?4];
const?pushed?=?arr.push(5);
console.log(arr);
//?[1,?2,?3,?4,?5]
console.log(pushed);
//?5
  • pop:删除数组的最后一个元素。同样,它改变了原数组。函数本身返回被删除元素的值。

let?arr?=?[1,?2,?3,?4];
const?popped?=?arr.pop();
console.log(arr);
//?[1,?2,?3]
console.log(popped);
//?4
  • shift:从数组头部删除一个元素。同样,它改变了原数组。函数本身返回被删除元素的值。

let?arr?=?[1,?2,?3,?4];
const?shifted?=?arr.shift();
console.log(arr);
//?[2,?3,?4]
console.log(shifted);
//?1
  • unshift:在数组头部添加一个或多个元素。同样,它改变了原数组。与其他很多方法不同的是,函数本身返回数组新的长度。

let?arr?=?[1,?2,?3,?4];
const?unshifted?=?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 值。

let?arr?=?['a',?'c',?'d',?'e'];
arr.splice(1,?0,?'b')
  • slice:返回从指定的起始位置到结束位置的浅拷贝数组。如果没有指定结束位置,则会返回到原数组的最后部分。重要的是,该方法不会改变原数组的值,而是返回对应的子集。

let?arr?=?['a',?'b',?'c',?'d',?'e'];
const?sliced?=?arr.slice(2,?4);
console.log(sliced);
//?['c',?'d']
console.log(arr);
//?['a',?'b',?'c',?'d',?'e']

sort

  • sort:根据带两个参数的函数对数组进行排序。改变原数组的值。当函数返回负数或0,则数组元素顺序不改变。当函数返回正数,数组元素会重新排序。

let?arr?=?[1,?7,?3,?-1,?5,?7,?2];
const?sorter?=?(firstEl,?secondEl)?=>?firstEl?-?secondEl;
arr.sort(sorter);
console.log(arr);
//?[-1,?1,?2,?3,?5,?7,?7]

嘿,以上方法你都掌握了吗?或许我们都没有。实际上,在我写本篇文章时夜不得不时常查阅 MDN 文档,这很正常。只要知道有什么数组方法就达到 95% 的效果啦。


Generators 函数


不要害怕使用 * 函数。Generator 函数指定了下一次调用 next() 生成的 value 值。在有限个生成值或无限次循环下使用,最后 next() 会返回一个 undefined 值。

function*?greeter()?{
??yield?'Hi';
??yield?'How?are?you?';
??yield?'Bye';
}
const?greet?=?greeter();
console.log(greet.next().value);
//?'Hi'
console.log(greet.next().value);
//?'How?are?you?'
console.log(greet.next().value);
//?'Bye'
console.log(greet.next().value);
//?undefined

下面是一个无限值的 Generator 函数:

function*?idCreator()?{
??let?i?=?0;
??while?(true)
????yield?i++;
}
const?ids?=?idCreator();
console.log(ids.next().value);
//?0
console.log(ids.next().value);
//?1
console.log(ids.next().value);
//?2
//?etc...


恒等运算符和相等运算符


一定要知道 JavaScript 中恒等运算符和相等运算符之间的区别!相等运算符在比较值前允许进行类型转换,而恒等运算符则不允许类型转换。

console.log(0?==?'0');
//?true
console.log(0?===?'0');
//?false


比较对象


JavaScript 新手常犯的错误之一是对对象直接进行比较。变量是指向内存中的引用对象,而不是对象本身!实际比较它们的一种方法是将对象转换成 JSON 字符串。这种方法有个缺点:不能保证对象属性的顺序!比较对象更安全的方法是使用专门用于深度对象比较的库(比如 lodash 库的 isEqual 方法)。

下面这两个对象看起来相等,但实际上它们指向不同的引用对象。

const?joe1?=?{?name:?'Joe'?};
const?joe2?=?{?name:?'Joe'?};
console.log(joe1?===?joe2);
//?false

相反地,下面的计算结果返回 true,因为把一个对象赋值给另一个对象,它们的指向相同(内存中只有一个对象)。

const?joe1?=?{?name:?'Joe'?};
const?joe2?=?joe1;
console.log(joe1?===?joe2);
//?true

请回顾上面的第一个概念:值和引用变量的赋值,将有助于更系统的理解将指向内存的一个对象变量赋值给另一个变量的结果。


回调函数


太多人被 JavaScript 的回调函数吓倒了。回调函数很简单,举下面这个例子。将 console.log 作为回调函数传给 myFunc 函数,它在计时器 setTimeout 完成时执行。这就是回调函数的全部!

function?myFunc(text,?callback)?{
??setTimeout(function()?{
????callback(text);
??},?2000);
}
myFunc('Hello?world!',?console.log);
//?'Hello?world!'


Promises


一旦你理解了 JavaScript 的回调函数,你很快就发现自己陷入了嵌套的“回调地狱”。这就是 Promises 的用途。将异步逻辑封装在 Promise 中,异步操作执行成功则运行 resolve 回调函数,否则则运行 reject 回调函数。Promise 运行成功进入 then 回调函数,失败则进入 catch 回调。

const?myPromise?=?new?Promise(function(res,?rej)?{
??setTimeout(function(){
????if?(Math.random()?< ?0.9)?{
??????return?res('Hooray!');
????}
????return?rej('Oh?no!');
??},?1000);
});
myPromise
??.then(function(data)?{
????console.log('Success:?'?+?data);
???})
???.catch(function(err)?{
????console.log('Error:?'?+?err);
???});

//?If?Math.random()?returns?less?than?0.9?the?following?is?logged:
//?"Success:?Hooray!"
//?If?Math.random()?returns?0.9?or?greater?the?following?is?logged:
//?"Error:?On?no!"


Async Await


一旦掌握了 JavaScript Promises 的秘诀,你可能会喜欢 async await,这是基于 Promises 的“语法糖”。在下面的例子中,我们创建一个异步函数 myFunc,并在函数中等待执行 greeter 这一 promise。

const?greeter?=?new?Promise((res,?rej)?=>?{
??setTimeout(()?=>?res('Hello?world!'),?2000);
})
async?function?myFunc()?{
??const?greeting?=?await?greeter;
??console.log(greeting);
}
myFunc();
//?'Hello?world!'


结论


如果你以前完全不知道以上这 12 个概念,那么现在,你的 JavaScript 知识多少有些增长了!如果你知道所有这些,那么希望这是巩固和增长知识的机会。

原文:https://hackernoon.com/12-javascript-concepts-that-will-level-up-your-development-skills-b37d16ad7104

本文为?CSDN 翻译,如需转载,请注明来源出处。


?热 文?推 荐?

??调查全球 98,000 名程序员发现,PHP 遭厌弃,前端岗已饱和!

??马化腾谈滴滴;苹果供应商研发柔性玻璃;丁磊谈沉迷手机 | 极客头条

??除了写代码,程序员还能做哪些副业?

? 女神节该送程序媛什么礼物?保命指南来了!| 程序员有话说

??小学生手写 Python 程序解魔方!这是高手,这绝对是高手!

? 小团队的微服务之路

? 首发 | 旷视14篇CVPR 2019论文,都有哪些亮点?

?两会第一天, 大佬们关于区块链的探讨, 你要了解的都在这了

??神操作!这段代码让程序员躺赚200万?给力!

print_r('点个好看吧!');
var_dump('点个好看吧!');
NSLog(@"点个好看吧!");
System.out.println("点个好看吧!");
console.log("点个好看吧!");
print("点个好看吧!");
printf("点个好看吧!n");
cout?< "点个好看吧!"?< Console.WriteLine("点个好看吧!");
fmt.Println("点个好看吧!");
Response.Write("点个好看吧!");
alert("点个好看吧!")
echo "点个好看吧!"

点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。

喜欢就点击“好看”吧!

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接