写点什么

了解这 12 个概念,让你的 JavaScript 水平更上一层楼

  • 2019 年 3 月 06 日
  • 本文字数:4634 字

    阅读完需:约 15 分钟

了解这12个概念,让你的JavaScript水平更上一层楼

JavaScript 是一门复杂的语言。如果你是一名 JavaScript 开发人员,不管处于什么样的水平,都有必要了解 JavaScript 的基本概念。本文介绍了 12 个非常重要的 JavaScript 概念,但绝对不是说 JavaScript 开发人员只需要知道这些就可以了。


1. 变量赋值(值与引用)

JavaScript 总是按照值来给变量赋值。当指定的值是 JavaScript 的五种原始类型之一(即 Boolean、null、undefined、String 和 Number)时,将为变量分配实际的值。但是,当指定的值是 Array、Function 或 Object 时,将为变量分配内存的对象引用。


在下面的代码段中,使用 var1 对 var2 赋值。由于 var1 是基本类型(String),因此 var2 的值等于 var1 的 String 值,但这个时候可以认为 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' }
复制代码


如果你期望它会像原始类型赋值那样,很可能会出问题!如果你创建了一个无意中会改变对象的函数,就会出现一些非预期的行为。


2. 闭包

闭包是一种重要的 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​​密钥的方法。在这种情况下,只需要提供一次 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' });
复制代码


3. 解构

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'
复制代码


在下面的示例中,解构被用来将 person 对象传给 introduce 函数。换句话说,解构可以(并且经常)直接用于提取传给函数的参数。如果你熟悉 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!"
复制代码


4. 展开(spread)语法

在下面的示例中,Math.max 不能直接接受 arr 数组,因为它的参数不是数组类型,但可以以数组中的各个元素作为参数。展开运算符…可用于提取数组的各个元素。


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


5. 变长参数(rest)语法

你可以用它将传给函数的任意数量的参数放入数组中!


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


6. 数组方法

JavaScript 数组方法通常可以为你提供令人难以置信的优雅方式来执行所需的数据转换。作为 StackOverflow 的贡献者,我经常看到有关如何以这种或那种方式操纵对象数组的问题。这往往是数组方法的完美用例。


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:返回一个数组,只有当指定函数返回 true 时,相应的元素才会被包含在这个数组中。


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

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 之后的元素都符合条件,但只返回第一个匹配的元素。


findIndex:这与 find 几乎完全相同,但不返回第一个匹配的元素,而是返回第一个匹配元素的索引。


const arr = ['Nick', 'Frank', 'Joe', 'Frank'];const foundIndex = arr.findIndex(el => el === 'Frank');console.log(foundIndex);// 1
复制代码


indexOf:与 findIndex 几乎完全相同,但它的参数不是一个函数,而是一个简单的值。


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]
复制代码


7. 生成器

看到*不要害怕。生成器函数指定下一次调用 next()时会生成什么 value。既可以生成有限数量的 value(最后调用 next()会返回 undefined),也可以使用循环生成无限数量的 value。


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
复制代码


使用生成器生成无限个值:


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...
复制代码


8. ===与==

一定要知道 JavaScript 中===运算符和==运算符之间的区别!==运算符在比较之前会进行类型转换,而===运算符在比较之前不会进行类型转换。


console.log(0 == '0');// trueconsole.log(0 === '0');// false
复制代码


9. 对象比较

JavaScript 新手容易犯的一个错误是直接比较对象。变量一般是指向内存中对象的引用,而不是对象本身!比较对象的一种方法是将它们转换成 JSON 字符串。但这样做有一个缺点:无法保证对象属性的顺序!一种更安全的方法是使用专门进行深度对象比较的库(例如 lodash 的 isEqual,https://lodash.com/docs#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
复制代码


10. 回调函数

很多人都被 JavaScript 回调函数吓倒了!它们其实很简单,请看下面的例子。console.log 函数作为回调传给 myFunc,并在 setTimeout 完成时执行。


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


11. promise

一旦你理解了 JavaScript 回调,很快就会发现自己陷入了“回调地狱”中。这个时候可以使用 promise!将异步逻辑包装在 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!"
复制代码


12. Async/Await

在掌握了 promise 的用法后,你可能也会喜欢 async await,它只是一种基于 promise 的“语法糖”。在下面的示例中,我们创建了一个 async 函数,并 await 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!'
复制代码


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


更多内容,请关注前端之巅。



2019 年 3 月 06 日 07:507090
用户头像

发布了 731 篇内容, 共 394.6 次阅读, 收获喜欢 1937 次。

关注

评论 1 条评论

发布
用户头像
数组push返回的是数组长度
2019 年 03 月 07 日 16:05
回复
没有更多了
发现更多内容

缓存数据的淘汰之路(下)

卢卡多多

缓存 28天写作 签约计划第二季 12月日更

ONES X 蓝城兄弟|有机的研发管理改进是最健康的颠覆

万事ONES

项目管理 ONES 项目管理工具

第一周作业

lv

博睿数据携手火山引擎,共建新云新未来

博睿数据

架构实战营模块一作业

Evan

架构实战营

使用 HTML、CSS 和 JavaScript 制作的随机密码生成器

海拥(haiyong.site)

JavaScript 密码 28天写作 签约计划第二季 12月日更

你以为Vue3封装一个弹框组件很简单?

CRMEB

基于云的技术架构设计实践-第5篇

hackstoic

数据分析 云原生 数据可视化 业务分析 签约计划第二季

从实习到秋招成为一名安全工程师,我经历了什么

网络安全学海

面试 网络安全 信息安全 渗透测试 WEB安全

作业:架构实战营模块1

Poplar89

「架构实战营」

Gartner预测全球人工智能软件市场将在2022年达到620亿美元规模

WorkPlus Lite

架构实战营模块一学习总结

Evan

架构实战营

2021年11月云主机性能评测报告

博睿数据

云主机 博睿数据 数据链DNA

普华基础软件加入龙蜥并成为理事单位,共创开源操作系统新生态

OpenAnolis小助手

龙蜥社区

2021年面试落下帷幕,这些Java面试重灾区你知道吗?

不想秃头

Java redis 面试 分布式 多线程

Rust 元宇宙 15 —— 细节和重构

Miracle

rust 元宇宙

一周信创舆情观察(11.29~12.5)

统小信uos

如何验证你的产品创意?

石云升

产品思维 28天写作 12月日更

架构实战营-模块1-作业

Pyel

「架构实战营」

Hoo虎符研究院 | Arweave调研报告

区块链前沿News

Arweave Hoo虎符 虎符交易所 虎符研究院 去中心化存储

GrowingIO Terraform 实践

GrowingIO技术专栏

运维 SRE Terraform 项目实践 资源编排

FPGA大发展!现在或许是从事FPGA行业的最好时代

科技新消息

自定义View:如何实现点击图标旋转的动画效果

Changing Lin

12月日更

Python代码阅读(第69篇):首字母大小写变换

Felix

Python Code 字符串 阅读代码 Python初学者

想给用户天涯若比邻的体验?业务全球化面临的三重挑战

声网

网络

Python Qt GUI设计:菜单栏、工具栏和状态栏的使用方法(拓展篇—2)

不脱发的程序猿

Python qt GUI设计 Qt Creator 菜单栏、工具栏、状态栏

微信业务架构图&学生管理系统毕业架构设计

Spring

架构实战营

TypeScript 之 Class(下)

冴羽

JavaScript typescript 翻译 大前端

百万级 QPS 业务新宠,金山办公携手 Apache APISIX 打造网关实践新体验

Apache APISIX 中国社区

云原生 API网关 wps API Gateway Apache APISIX

云ERP系统究竟是什么?

低代码小观

流程管理 ERP 业务流程管理 业务流程自动化

毕业总结

小智

架构训练营

西门子低代码:探讨应用程序开发的下一步演进方向

西门子低代码:探讨应用程序开发的下一步演进方向

了解这12个概念,让你的JavaScript水平更上一层楼_前端_Nick Scialli_InfoQ精选文章