本文论述的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 anymoreapi.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);// 0console.log(ids.next().value);// 1console.log(ids.next().value);// 2// etc...

恒等运算符和相等运算符

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

console.log(0 == '0');// trueconsole.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 << "点个好看吧!" << endl;
Console.WriteLine("点个好看吧!");
fmt.Println("点个好看吧!");
Response.Write("点个好看吧!");
alert("点个好看吧!")
echo "点个好看吧!"

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

喜欢就点击“好看”吧!

程序员必须掌握的 12 个 JavaScript 技能!相关推荐

  1. 程序员最喜爱的12个Android应用开发框架二(转)

    在上一篇程序员最喜爱的12个Android应用开发框架(一)中,我们为大家介绍了前6个Android应用开发框架,主要包括了 Xamarin.Phonegap.Corona SDK等.接下来,小编将继 ...

  2. web 前端 如何分享到instagram_好程序员web前端教程分享前端javascript练习题三

    好程序员web前端教程分享前端javascript练习题三 cookie 一周内免登录 样式代码: 姓名: 密码: 一周内免登陆 js功能代码: var input=document.getEleme ...

  3. 好程序员web前端分享详细了解JavaScript函数

    好程序员web前端分享详细了解JavaScript函数,如果你曾经接触过JavaScript编程,你一定不会陌生如何定义并且调用一个函数.但是你知道在JavaScript中有多少种定义函数的方法吗?如 ...

  4. 程序员面试金典——18.12最大和子矩阵

    程序员面试金典--18.12最大和子矩阵 Solution1: 参考网址: [1]https://www.cnblogs.com/GodA/p/5237061.html 思想讲的很清楚~ [2]htt ...

  5. 程序员面试金典——17.12整数对查找

    程序员面试金典--17.12整数对查找 Solution1:针对重复数字的情况题目未做明确说明,虽然此题仍能AC,但有的重复数字用了1次,有的用了超过1次,要求不清晰.重点是这种前后双指针的方法要会! ...

  6. 程序员被公司辞退12天,前领导要求回公司讲清楚代码,结果蒙了

    程序员被公司辞退12天,前领导要求回公司讲清楚代码,结果蒙了 在大部分情况下,如果一个员工已经离职了,那么与原来公司是没有任何关系.而员工在离职前,只需要做的一件事就是把工作交接清楚,拿到相应的工资就 ...

  7. HTML结业做什么项目好,高薪就业-好程序员HTML5大前端12期毕业典礼!

    时光飞逝,好程序员web前端培训12期的小伙伴们在好程序员紧张的学习生涯即也将结束,迎来了属于他们的毕业典礼.讲师寄语.美食.表演.送锦旗.颁发毕业证书,一个都不能少. 北科校区王校长为大家送上了毕业 ...

  8. 程序员应该学习掌握哪些知识和技能?

    现在做为一名程序员,压力越来越大,各种开发工具越来越庞大.不断推陈出新,各种开发设计工程理念缤纷精彩.需要融入平常的开发当中,还有很多新的知识点在不断开拓中,相比以前,做为一名程序员尤其是合格程序员的 ...

  9. 程序员的自我修养——读《软技能-代码之外的生存指南》笔记

    我记得曾经读过俞甲子的<程序员的自我修养--链接.加载和库>,当时就觉得这个书名起的不太合适,有点不合主题,因为这本书主要讲述链接库的事情,我认为这个是编译器的一部分,是作为程序员要掌握的 ...

最新文章

  1. mfc 开启指定服务器,用MFC实现消息的发送和接收(含服务器)
  2. 如何给邮件添加背景颜色
  3. linux apt-get install 安装指定的版本
  4. 使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案
  5. 是否非要用interface关键字来实现接口?
  6. CentOs7.2编译安装Nginx服务器
  7. C#LeetCode刷题之#581-最短无序连续子数组( Shortest Unsorted Continuous Subarray)
  8. 在XP中轻松获取未使用的局域网IP地址
  9. DB2 SQLCODE: -407, SQLSTATE: 23502
  10. active mq topic消费后删除_【SpringBoot MQ 系列】RabbitListener 消费基本使用姿势介绍
  11. 华硕服务器 u盘安装系统,华硕台式机重装系统详细图解步骤
  12. oracle切割字符串函数,Oracle字符串分割函数
  13. Unity 小程序开发
  14. java读取word目录
  15. Android资源应用与适配标准
  16. SMAP_SSS_ L2c、L3_V04.0.n 文件下载途径(一键同时下载多个数据~)
  17. VXGI体素全局照明
  18. 微信公众号(一键互粉)增粉平台的源码分享
  19. 微信登陆的LOL只有一个服务器,lol微信登录_lol能用微信登录吗_lol微信登录只有一个区-站长之家...
  20. Word Vector的综述

热门文章

  1. html中单选怎么写,在HTML中select标签怎样实现单选和多选
  2. python爬虫网页中的图片_Python爬虫爬取一个网页上的图片地址实例代码
  3. conda创建环境及激活环境失败问题
  4. leetcode python3 简单题171. Excel Sheet Column Number
  5. java jsp动作_Java中级—JSP九大内置对象和动作
  6. leetcode题库1277-- 统计全为 1 的正方形子矩阵
  7. Linux Shell编程笔记8 进程
  8. Ubuntu18.04 + CUDA10.0 + tensorflow-gpu 安装过程
  9. Linux的dup与dup2函数
  10. Flutter基础—常用控件之容器