要了解的12个JavaScript概念

JavaScript是一种复杂的语言。 如果您从事任何级别的JavaScript开发,那么这对于您理解该语言的基本概念至关重要。 我们今天出版的翻译材料涵盖了12个关键的JavaScript概念。 当然,JavaScript开发人员需要了解更多,但是如果没有我们今天要谈论的内容,他绝对做不到。



1.存储值和引用的变量


对于希望正确编写工作代码的人来说,了解JavaScript中变量值的确切分配方式非常重要。 对这种机制的误解导致编​​写程序,其中变量的值可能会意外更改。

如果实体具有原始类型之一(特别是BooleannullundefinedStringNumber类型),则JavaScript始终使用该实体的值。 即,将值写入相应的变量。 如果我们在谈论一个对象(例如, ObjectArrayFunction类型),则在将其分配给变量时,将对该Object的引用写入该对象在内存中的地址。

考虑一个例子。 在以下代码片段中,一个字符串被写入var1 。 之后,将var2的值写入var2变量。 由于var1变量具有原始类型( String ),因此var1可用字符串的副本var2被写入var1 。 这使我们可以将var2视为完全独立于var1的变量,尽管它存储的值与var1相同。 将新值写入var1不会影响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' } 

如您所见,这里我们正在使用var2变量,当它们存储对同一对象的引用时,在var1变量中会反映出它发生了什么。 很难想象如果有人决定存储对象的变量的行为与存储基元类型的变量的行为相同,那么这将在实际代码中导致什么。 例如,当它们创建旨在与传递给它的对象值一起使用的函数而该函数无意间更改了该值时,这尤其令人不快。

2.短路


结束语是JavaScript中的一种重要设计模式,允许您使用变量来组织受保护的工作。 在下面的示例中, createGreeter()函数返回一个匿名函数,该函数可以访问带有greeting参数的提供的参数,该参数包含字符串Hello 。 对该匿名函数的引用将写入sayHello变量。 之后,无论我们调用sayHello()函数多少次,它始终可以访问greeting值。 在这种情况下,对greeting访问将仅是一个匿名函数,该链接的链接记录在sayHello

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

这是一个非常简单的例子。 如果我们看一些更接近真实世界的东西,我们可以想象,例如,一个用于连接到某个API的函数(我们称其为apiConnect() ),该函数在首次调用时会被传递一个API访问密钥。 反过来,此函数返回一个对象,该对象包含使用传递给apiConnect()的API访问密钥的几种方法。 在这种情况下,密钥存储在闭包中,当您调用这些方法时,不再需要提及它。

 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'); //     API     api.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.点差算子


传播算子是一个相当简单的构造,对于没有准备的人来说似乎难以理解。 以下示例具有一个数值数组,这是我们需要在其中找到的最大值。 我们想为此使用Math.max()方法,但是它不知道如何使用数组。 作为参数,他假设独立的数值。 为了从数组中提取其元素,我们使用了散布运算符,它看起来像三点。

 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.数组方法


数组方法通常为开发人员提供方便的工具,以精美地解决各种数据转换任务。 我有时会在StackOverflow上回答问题。 其中,通常有一些专门用于处理对象数组的方法或其他方法。 在这种情况下,数组方法特别有用。

在这里,我们将考虑几种这样的方法,将它们彼此相似的原理结合在一起。 应当指出,这里我不会告诉您所有数组方法。 您可以在MDN上找到其完整列表(顺便说一下,这是我最喜欢的JavaScript参考)。

▍Map(),filter()和reduce()方法


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()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循环枚举和分析数组的情况下,当使用break语句在数组中找到所需的元素时,此类循环会中断,这种方法非常有用。

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()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 

liceSlice()和splice()方法


这些方法用于修改数组或返回数组的某些部分。

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()方法根据传递给它的函数指定的条件对数组进行排序。 此函数接受数组的两个元素(例如,可以将它们表示为参数ab ),并与它们进行比较,如果不需要交换元素,则返回;如果将a放在比其低的索引处,则返回0。 b是负数,并且如果b需要以比a低的索引放置,则a是正数。

 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.发电机


JavaScript生成器使用星号字符声明。 它们允许您指定next()调用next()方法时将返回什么值。 可以将生成器设计为返回有限数量的值。 如果此类生成器返回了所有此类值,则对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 

这是一个生成器,旨在通过循环返回无限数量的值。

 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 //   ... 

8.用于检查值的相等性(==)和严格相等性(===)的运算符


对于任何JS开发人员而言,了解相等( == )和严格相等( === )运算符之间的区别都非常重要。 事实是,运算符==在比较值之前会执行其类型的转换(乍看之下可能会导致奇怪的后果),而运算符===不会执行类型转换。

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

9.对象比较


我有时不得不看看JS编程的新手如何犯同样的错误。 他们尝试直接比较对象。 “存储”对象的变量包含对它们的引用,而不是这些对象本身。

因此,例如,在下面的示例中,对象看起来相同,但是直接比较时,我们得知对象是不同的,因为每个变量都包含一个指向其自己对象的链接,并且这些链接彼此不相等。

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

此外,在下面的示例中,由于两个变量都存储对同一对象的引用,因此可以证明joe1等于joe2

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

实际对象比较的方法之一是将它们初步转换为JSON字符串格式。 确实,这种方法有一个问题,那就是在获得的对象的字符串表示形式中,不能保证其属性的某些顺序。 比较对象的一种更可靠的方法是使用一个特殊的库,该库包含用于对象的深层比较的工具(例如,这是lodash库的isEqual()方法)。

为了更好地理解比较对象的复杂性并了解在不同变量中编写指向相同对象的链接的可能结果,请看一下本文讨论的第一个JS概念。

10.回调函数


回调函数是一个相当简单的JavaScript概念,新手有时会遇到困难。 考虑以下示例。 在这里, console.log函数(就是这样-不带括号myFunc()作为回调函数传递给myFunc() 。 此函数设置一个计时器,此后将调用console.log() ,并在控制台中显示传递给myFunc()的字符串。

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

11.承诺


掌握了回调函数并开始在各处使用它们之后,您很快就会发现自己处于所谓的“回调地狱”中。 如果您真的在那里-看一下诺言。 异步代码可以包装在一个Promise中,并在成功执行后,通过调用适当的方法告知系统有关Promise的成功解决的信息,如果出了问题,请调用指示该方法并拒绝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);  }); //  Math.random()  , ,  0.9,    : // "Success: Hooray!" //  Math.random()  , ,  0.9,  0.9,    : // "Error: On no!" 

12.异步/等待构造


在兑现承诺之后,很有可能您会想要更多。 例如,掌握async / await构造。 它是诺言的句法糖。 在以下示例中,我们使用async创建一个异步函数,并在其中使用await关键字组织等待迎接者greeter的等待。

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

总结


如果我们之前在这里谈论的内容对您不熟悉,那么您很可能(至少一点)通过阅读本文而超越了自己。 如果您在这里没有发现任何新鲜事物,那么我希望该材料为您提供练习和增强JavaScript知识的机会。

亲爱的读者们! 您还将在本文中添加哪些其他JavaScript概念?

Source: https://habr.com/ru/post/zh-CN441566/


All Articles