写点什么

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

2019 年 3 月 06 日

了解这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:506848
用户头像

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

关注

评论 1 条评论

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

Underlay网络:如何立住可靠又支持大规模无收敛的“人设”

华为云开发者社区

云服务 交换机

一个草根的日常杂碎(10月9日)

刘新吾

随笔杂谈 生活记录 社会百态

Kubeless 函数部署遇到了问题,如何 Debug? | 玩转 Kubeless

donghui

Serverless kubeless

华为丁耘,解读百尺竿头的中国5G

脑极体

华为云专家带你解读文本情感分析任务

华为云开发者社区

内容 数据 分析

融合与共生之下,区块链都能“+”什么?

CECBC区块链专委会

区块链 大数据

Apple Developer 开发者账号申请&实名认证【2020】

iHTC

Apple Developer iOS Developer 苹果实名认证

正则表达式知识总结

iHTC

正则表达式

商业模式和盈利模式的思考

iHTC

商业模式 盈利模式 地摊经济

一个草根的日常杂碎(10月10日)

刘新吾

随笔杂谈 生活记录 社会百态

打通Docker镜像发布容器运行流程

架构师修行之路

Docker 容器 分布式 微服务

技术解读丨分布式缓存数据库Redis大KEY问题定位及优化建议

华为云开发者社区

云计算 华为 技术

优秀开源项目、博客、书籍整理

小铁匠

收藏教程 资源汇总

JVM系列笔记 - 寄存器

朱华

JVM

技术心得丨一种有效攻击BERT等模型的方法

华为云开发者社区

学习 AI

你不知道的java对象序列化的秘密

程序那些事

Java java序列化 序列化的秘密

OpenResty 项目脚手架

小铁匠

lua nginx openresty

随想

Nydia

分配时间戳和生成水位线

小知识点

scala 大数据 flink

iOS Handle Refunds 处理退款 --- WWDC20(Session 10661)

iHTC

WWDC2020 wwdc iap 苹果退款 iOS退款

第四周总结

_

极客大学架构师训练营 第四周总结

通俗易懂和你聊聊寄存器那些事(精美图文)

cxuan

后端 计算机 汇编

后疫情时代,华为云会议如何定义未来会议?

华为云开发者社区

视频 会议

面经手册 · 第13篇《除了JDK、CGLIB,还有3种类代理方式?面试又卡住!》

小傅哥

Java 字节码编程 asm 动态代理 cglib

第四周 系统架构学习总结

钟杰

极客大学架构师训练营

极客时间 - 架构师一期 - 第四周作业

_

第四周作业 架构师一期

一个草根的日常杂碎(10月11日)

刘新吾

随笔杂谈 生活记录 社会百态

容器技术为什么会这么流行

架构师修行之路

Docker 分布式 微服务 容器化

CECBC区块链专委会副主任吴桐主讲全国社保基金数字货币讲座

CECBC区块链专委会

区块链 数字货币

区块链需与5G等技术打好“组合拳”

CECBC区块链专委会

区块链 5G

当我们在谈论跨平台的时候 ——— 我们在说什么

iHTC

跨平台

2021年全国大学生计算机系统能力大赛操作系统设计赛 技术报告会

2021年全国大学生计算机系统能力大赛操作系统设计赛 技术报告会

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