探索var和let关键字之间的差异
使用let时,具有相同名称的变量只能声明一次
使用var时,具有相同名称的变量可以声明多次
”use strict"将启用Strict模式,该模式捕获常见的编码错误和“不安全”操作。
比较var和let关键字的范围
var全局变量
let其范围仅限于该块,语句或表达式
使用const关键字声明只读变量
const声明的变量是只读的,一旦一个变量被赋值为常量,它就不能被重新赋值
一般对不可变值使用大写变量标识符,对可变值(对象和数组)使用小写
防止对象改变
object.freeze()函数
使用函数编写可重用的JavaScript
JavaScript中,我们通常不需要命名函数,特别是在将一个函数作为参数传递给另一个函数时。相反,我们创建内联函数。我们不需要命名这些函数,因为我们不会在其他任何地方重用它们。
const myFunc = function() {
const myVar = "value";
return myVar;
}
箭头函数语法:
const myFunc = () => {
const myVar = "value";
return myVar;
}
当没有函数体,只有一个返回值时,箭头函数语法使您可以省略关键字return以及代码周围的括号。这有助于将较小的函数简化为单行语句:
const myFunc = () => "value";
value默认情况下,此代码仍将返回字符串。
编写带参数的箭头函数
就像常规函数一样,您可以将参数传递给箭头函数。
const doubler = (item) => item * 2;
doubler(4);
doubler(4)将返回值8。
如果箭头函数具有单个参数,则可以省略包围该参数的括号。
const doubler = item => item * 2;
可以将多个参数传递给箭头函数。
const multiplier = (item, multi) => item * multi;
multiplier(4, 2);
multiplier(4, 2)将返回值8。
为功能设置默认参数
const greeting = (name = "Anonymous") => "Hello " + name;
console.log(greeting("John"));
console.log(greeting());
控制台将显示字符串Hello John和Hello Anonymous。
如果未指定参数(未定义),则会启动默认参数。如您在上面的示例中看到的那样,当您不为参数提供name值Anonymous时,该参数将收到其默认值。您可以根据需要为任意多个参数添加默认值。
将Rest参数与Function参数一起使用
为了帮助我们创建更灵活的功能,ES6引入了rest参数作为功能参数。使用rest参数,您可以创建带有可变数量参数的函数。这些参数存储在一个数组中,以后可以从函数内部进行访问。
一篇关于reduce用法的文章的文章
const sum = (...args) => {
return args.reduce((a, b) => a + b, 0);
}
console.log(sum(12));
使用spread运算符展开数组项
spread运算符即"…"
apply()用于计算数组中的最大值
const arr1 = ['JAN', 'FEB', 'MAR', 'APR', 'MAY'];
let arr2;
arr2 = [...arr1]; // Change this line
console.log(arr2);
使用解构赋值从对象中提取值
考虑以下ES5代码:
const user = { name: 'John Doe', age: 34 };
const name = user.name; // name = 'John Doe'
const age = user.age; // age = 34
这是使用ES6解构语法的等效赋值语句:
const { name, age } = user;
// name = 'John Doe', age = 3
用等效的解构分配替换这两个分配。它仍应分配对象的变量today和tomorrow值today以及tomorrow来自HIGH_TEMPERATURES对象的值。
const HIGH_TEMPERATURES = {
yesterday: 75,
today: 77,
tomorrow: 80
};
// Only change code below this line
const {today,tomorrow} = HIGH_TEMPERATURES;
// Only change code above this line
使用解构赋值从对象分配变量
您可以使用前两课中的相同原理来从嵌套对象中解构值。
使用类似于先前示例的对象:
const user = {
johnDoe: {
age: 34,
email: 'johnDoe@freeCodeCamp.com'
}
};
以下是提取对象属性的值并将其分配给具有相同名称的变量的方法:
const { johnDoe: { age, email }} = user;
这是将对象属性的值分配给具有不同名称的变量的方法:
const { johnDoe: { age: userAge, email: userEmail }} = user;
用等效的解构分配替换这两个分配。它仍应分配对象的变量lowToday和highToday值today.low以及today.high来自LOCAL_FORECAST对象的值。
const LOCAL_FORECAST = {
yesterday: { low: 61, high: 75 },
today: { low: 64, high: 77 },
tomorrow: { low: 68, high: 80 }
};
// Only change code below this line
const { today: { low: lowToday, high: highToday }}=LOCAL_FORECAST;
// Only change code above this line
使用解构赋值从数组分配变量
ES6使解构数组像解构对象一样容易。
扩展运算符和数组解构之间的一个关键区别是,扩展运算符将数组的所有内容解压缩为逗号分隔的列表。因此,您不能选择要选择分配给变量的元素。
解构数组可以让我们做到这一点:
const [a, b] = [1, 2, 3, 4, 5, 6];
console.log(a, b);
控制台将显示的值a和b作为1, 2。
该变量a被分配数组的第一个值,并被b分配数组的第二个值。我们还可以通过使用逗号到达所需索引的方式来解构数组中任何索引的值:
const [a, b,,, c] = [1, 2, 3, 4, 5, 6];
console.log(a, b, c);
控制台将显示的值a,b和c作为1, 2, 5。
使用解构赋值来交换的价值a,并b让a接收存储在值b,并b接收存储在值a。
let a = 8, b = 6;
// Only change code below this line
[a,b]=[b,a];
使用解构赋值配合 rest 操作符来重新分配数组元素
在解构数组的某些情况下,我们可能希望将剩下的元素放进另一个数组里面。
以下代码的结果与使用Array.prototype.slice()相同:
const [a, b, ...arr] = [1, 2, 3, 4, 5, 7];
console.log(a, b); // 1, 2
console.log(arr); // [3, 4, 5, 7]
变量a与b分别获取了数组的前两个元素的值。之后,因为rest操作符的存在,arr获取了原数组剩余的元素的值,并构成了一个新的数组。
rest操作只能对数组列表最后的元素起作用。这意味着你不能使用rest操作符来截取原数组中间元素的子数组。
使用解构赋值以及rest操作符来进行一个Array.prototype.slice相同的操作。使得arr是原数组source除开前两个元素的子数组。
const source = [1,2,3,4,5,6,7,8,9,10];
function removeFirstTwo(list) {
// Only change code below this line
const [a,b,...arr]= list; // Change this line
// Only change code above this line
return arr;
}
const arr = removeFirstTwo(source);
使用解构赋值将对象作为函数的参数传递
在某些情况下,您可以在函数参数本身中解构对象。
考虑下面的代码:
const profileUpdate = (profileData) => {
const { name, age, nationality, location } = profileData;
}
这样可以有效地破坏发送到函数中的对象。这也可以就地完成:
const profileUpdate = ({ name, age, nationality, location }) => {
}
当profileData传递给上述函数时,这些值将从函数参数中解构出来,以供在函数内使用。
使用函数参数中解构赋值half只发送max和min在函数内部。
const stats = {
max: 56.78,
standard_deviation: 4.34,
median: 34.54,
mode: 23.87,
min: -0.75,
average: 35.85
};
// Only change code below this line
const half = ({max,min}) => (max + min) / 2.0;
// Only change code above this line
console.log(half(stats));
使用模板文字创建字符串
模板字符串是 ES6 的另外一项新的功能。这是一种可以轻松构建复杂字符串的方法。
请看以下代码:
const person = {
name: "Zodiac Hasbro",
age: 56
};
// string interpolation
const greeting = `Hello, my name is ${person.name}!
I am ${person.age} years old.`;
console.log(greeting); // 打印出
// Hello, my name is Zodiac Hasbro!
// I am 56 years old.
这段代码有许多的不同:
首先,上面使用的 v a r i a b l e 语 法 是 一 个 占 位 符 。 这 样 一 来 , 你 将 不 再 需 要 使 用 + 运 算 符 来 连 接 字 符 串 。 当 需 要 在 字 符 串 里 增 加 变 量 的 时 候 , 你 只 需 要 在 变 量 的 外 面 括上 {variable}语法是一个占位符。这样一来,你将不再需要使用+运算符来连接字符串。当需要在字符串里增加变量的时候,你只需要在变量的外面括上 variable语法是一个占位符。这样一来,你将不再需要使用+运算符来连接字符串。当需要在字符串里增加变量的时候,你只需要在变量的外面括上{和},并将其放在字符串里就可以了。
其次,在例子使用了反引号(`),而不是引号('或者")将字符串括了起来,并且这个字符串可以换行。
这个新的方式使你可以更灵活的创建复杂的字符串。
使用模板字符串的反引号的语法来展示result对象的failure数组内的每个条目。每个条目应该括在带有text-warning类属性的li标签中,并赋值给resultDisplayArray。
const result = {
success: ["max-length", "no-amd", "prefer-arrow-functions"],
failure: ["no-var", "var-on-top", "linebreak"],
skipped: ["no-extra-semi", "no-dup-keys"]
};
function makeList(arr) {
// Only change code below this line
const failureItems = arr.map(a=>`<li class="text-warning">${a}</li>`);
// Only change code above this line
return failureItems;
}
const failuresList = makeList(result.failure);
使用简单字段编写简洁的对象字面量声明
const getMousePosition = (x, y) => ({
x: x,
y: y
});
getMousePosition是一个简单的函数,它返回包含两个属性的对象。ES6提供了语法糖以消除必须编写的冗余x: x。您只需编写x一次,即可在后台将其转换为x: x(或等效形式)。这是上面重写的相同函数,以使用此新语法:
const getMousePosition = (x, y) => ({ x, y });
使用对象属性速记与对象文字创建并返回一个对象name,age和gender属性。
const createPerson = (name, age, gender) => {
// Only change code below this line
return {
name,
age,
gender
};
// Only change code above this line
};
用ES6编写简洁的函数声明
在 ES5 中,当我们需要在对象中定义一个函数的时候,我们必须如下面这般使用function关键字:
const person = {
name: "Taylor",
sayHello: function() {
return `Hello! My name is ${this.name}.`;
}
};
在 ES6 语法的对象中定义函数的时候,你可以完全删除function关键字和冒号。请看以下例子:
const person = {
name: "Taylor",
sayHello() {
return `Hello! My name is ${this.name}.`;
}
};
使用以上这种简短的语法,重构在bicycle对象中的setGear函数。
// Only change code below this line
const bicycle = {
gear: 2,
setGear(newGear) {
this.gear = newGear;
}
};
// Only change code above this line
bicycle.setGear(3);
console.log(bicycle.gear);
使用class语法定义构造函数
ES6提供了使用class关键字创建对象的新语法。
应该注意的是,class语法只是语法,而不是像Java,Python,Ruby等语言那样,不是面向对象范式的完整的基于类的实现。
在ES5中,我们通常定义一个constructor函数并使用new关键字实例化一个对象。
var SpaceShuttle = function(targetPlanet){
this.targetPlanet = targetPlanet;
}
var zeus = new SpaceShuttle('Jupiter');
该class语法只是替换constructor函数创建:
class SpaceShuttle {
constructor(targetPlanet) {
this.targetPlanet = targetPlanet;
}
}
const zeus = new SpaceShuttle('Jupiter');
应该注意的是,class关键字声明了一个新函数,向其添加了构造函数。在调用new创建新对象时会调用此构造函数。
注意: ES6类名称应按惯例使用UpperCamelCase,如上SpaceShuttle所用。
该constructor方法是用于创建和初始化使用类创建的对象的特殊方法。您将在JavaScript算法和数据结构认证的“面向对象的程序设计”部分中了解有关它的更多信息。
使用class关键字并编写一个constructor以创建Vegetable类。
该Vegetable级允许你创建一个蔬菜对象与属性name获取传递给constructor。
// Only change code below this line
class Vegetable {
constructor(name) {
this.name = name;
}
}
// Only change code above this line
const carrot = new Vegetable('carrot');
console.log(carrot.name); // Should display 'carrot'
使用getter和 setter来控制对象的访问
您可以从对象获取值,并在对象内设置属性的值。
这些被称为经典的getter和setter方法。
Getter函数旨在将对象的私有变量的值简单地返回(获取)给用户,而无需用户直接访问私有变量。
设置器函数用于根据传递到设置器函数中的值来修改(设置)对象私有变量的值。此更改可能涉及计算,甚至可能完全覆盖先前的值。
class Book {
constructor(author) {
this._author = author;
}
// getter
get writer() {
return this._author;
}
// setter
set writer(updatedAuthor) {
this._author = updatedAuthor;
}
}
const novel = new Book('anonymous');
console.log(novel.writer);
novel.writer = 'newAuthor';
console.log(novel.writer);
控制台将显示字符串anonymous和newAuthor。
请注意用于调用getter和setter的语法。它们甚至看起来都不像函数。获取器和设置器很重要,因为它们隐藏了内部实现细节。
注意:按照惯例,私有变量的名称前面应带有下划线(_)。但是,实践本身并未将变量设为私有。
使用class关键字创建一个Thermostat类。在constructor接受一个华氏温度。
在该类中,创建一个getter以获取摄氏度的温度,并创建一个以摄氏setter温度设置的温度。
请记住,C = 5/9 * (F - 32)和F = C * 9.0 / 5 + 32,这里F的温度值是华氏温度,而C同一温度的值是摄氏温度。
注意:实施此步骤时,您将以华氏度或摄氏温度为单位对类内的温度进行跟踪。
这就是吸气剂和吸气剂的力量。您正在为另一个用户创建API,无论您跟踪哪个用户,该用户都可以获取正确的结果。
换句话说,您正在从用户中提取实现细节。
// Only change code below this line
class Thermostat{
constructor(farenheit){
this.farenheit= 5/9 * (farenheit - 32);
}
get temperature(){
return this.farenheit;
}
set temperature(farenheit){
this.farenheit=farenheit;
}
}
// Only change code above this line
const thermos = new Thermostat(76); // Setting in Fahrenheit scale
let temp = thermos.temperature; // 24.44 in Celsius
thermos.temperature = 26;
temp = thermos.temperature; // 26 in Celsius
创建一个模块脚本
JavaScript最初只是扮演很小的角色,而在其他通常是HTML的网络上也发挥着作用。如今,它非常庞大,并且某些网站几乎完全使用JavaScript构建。为了使JavaScript更具模块化,清洁性和可维护性;ES6引入了一种在JavaScript文件之间轻松共享代码的方法。这涉及导出文件的一部分以供一个或多个其他文件使用,以及在需要的地方导入所需的部分。为了充分利用此功能,你需要用你的HTML文档中创建一个脚本type的module。这是一个例子:
<script type="module" src="filename.js"></script>
使用此module类型的脚本现在可以使用import和export功能,您将在即将到来的挑战中了解这些功能。
将脚本添加到类型的HTML文档中,module并为其提供源文件index.js
<html>
<body>
<!-- Only change code below this line -->
<script type="module" src="index.js"></script>
<!-- Only change code above this line -->
</body>
</html>
使用导出共享代码块
想象一下一个名为的文件math_functions.js,其中包含一些与数学运算有关的函数。其中之一存储在变量中,该变量add接受两个数字并返回其总和。您想在多个不同的JavaScript文件中使用此功能。为了与其他文件共享它,您首先需要export它。
export const add = (x, y) => {
return x + y;
}
上面是导出单个函数的常用方法,但是您可以实现如下相同的操作:
const add = (x, y) => {
return x + y;
}
export { add };
导出变量或函数时,可以将其导入另一个文件中并使用它,而无需重写代码。您可以通过对要导出的每个对象重复第一个示例,或者将它们全部放入第二个示例的export语句中,来导出多个对象,如下所示:
export { add, subtract };
编辑器中有两个与字符串相关的函数。使用您选择的方法将它们都导出。
const uppercaseString = (string) => {
return string.toUpperCase();
}
const lowercaseString = (string) => {
return string.toLowerCase()
}
export { uppercaseString, lowercaseString };
使用导入重用JavaScript代码
import允许您选择要加载文件或模块的哪些部分。在上一课中,示例add从math_functions.js文件中导出。您可以通过以下方式将其导入以在另一个文件中使用:
import { add } from './math_functions.js';
在这里,import将add在中找到math_functions.js,仅导入该函数供您使用,而忽略其余部分。该./通知进口来寻找math_functions.js在同一个文件夹中当前文件的文件。以这种方式使用导入时,需要相对文件路径(./)和文件扩展名(.js)。
您可以通过在import语句中添加以下项来从文件中导入多个项:
import { add, subtract } from './math_functions.js';
添加适当的import语句,该语句将允许当前文件使用您在上一课中导出的uppercaseString和lowercaseString函数。这些功能位于一个名为的文件中string_functions.js,该文件与当前文件位于同一目录中。
// Only change code above this line
uppercaseString("hello");
lowercaseString("WORLD!");
import{uppercaseString,lowercaseString} from './string_functions.js';
使用*从文件导入所有内容
假设您有一个文件,并且希望将其所有内容导入当前文件。这可以通过import * as语法来完成。这是一个示例,其中名为的文件的内容math_functions.js被导入到同一目录中的文件中:
import * as myMathModule from "./math_functions.js";
上面的import语句将创建一个名为的对象myMathModule。这只是一个变量名,您可以为它命名。该对象将包含其中的所有导出math_functions.js内容,因此您可以像访问任何其他对象属性一样访问这些函数。这是使用导入的add和subtract函数的方法:
myMathModule.add(2,3);
myMathModule.subtract(5,3);
该文件中的代码需要文件:的内容string_functions.js,即与当前文件位于同一目录中。使用import * as语法将文件中的所有内容导入名为的对象stringFunctions。
// Only change code above this line
import * as stringFunctions from'./string_functions.js';
stringFunctions.uppercaseString("hello");
stringFunctions.lowercaseString("WORLD!");
使用导出默认值创建导出后备
在本export课程中,您了解了称为named export的语法。这使您可以使多个函数和变量可在其他文件中使用。
export您还需要了解另一种语法,称为export default。通常,如果仅从文件导出一个值,则将使用此语法。它还用于为文件或模块创建后备值。
以下是使用示例export default:
export default function add(x, y) {
return x + y;
}
export default function(x, y) {
return x + y;
}
第一个是命名函数,第二个是匿名函数。
由于export default用于声明模块或文件的后备值,因此每个模块或文件中只能将一个值作为默认导出。此外,您不能使用export default与var,let或const
以下功能应为该模块的后备值。请添加必要的代码。
export default function subtract(x, y) {
return x - y;
}
导入默认导出
在上一个挑战中,您了解了export default它及其用途。要导入默认导出,您需要使用其他import语法。在以下示例中,add是math_functions.js文件的默认导出。导入方法如下:
import add from "./math_functions.js";
语法在一个关键位置上有所不同。导入的值add不在大括号({})内。add无论math_functions.js文件的默认导出是什么,这里都只是一个变量名。导入默认值时,您可以在此处使用任何名称。
创建一个JavaScript承诺
JavaScript中的承诺实际上就是它的模样-您可以使用它来做出承诺以做某事,通常是异步的。任务完成后,您要么兑现了诺言,要么没有兑现。Promise是构造函数,因此您需要使用new关键字来创建一个。它以带有两个参数-resolve和的函数作为参数reject。这些是用于确定承诺结果的方法。语法如下所示:
const myPromise = new Promise((resolve, reject) => {
});
创建一个名为的新承诺makeServerRequest。将带有resolve和reject参数的函数传递给构造函数。
通过决心和拒绝完成诺言
一诺有三种状态:pending,fulfilled,和rejected。您在上一个挑战中创建的诺言永远都停留在pending状态中,因为您没有添加完成诺言的方法。给promise参数提供的resolve和reject参数用于执行此操作。resolve当您希望诺言成功时使用,当您希望诺言reject失败时使用。这些是带有参数的方法,如下所示。
const myPromise = new Promise((resolve, reject) => {
if(condition here) {
resolve("Promise was fulfilled");
} else {
reject("Promise was rejected");
}
});
上面的示例使用字符串作为这些函数的参数,但实际上可以是任何东西。通常,它可能是一个对象,您可以使用其中的数据将其放置到您的网站或其他地方。
然后处理一个兑现的承诺
当您有一个过程花费了您代码中未知时间(即异步的时间)(通常是服务器请求)时,承诺最有用。发出服务器请求时,它会花费一些时间,并且在完成请求后,您通常希望对服务器的响应进行某些处理。这可以通过使用该then方法来实现。使用then履行您的诺言后,立即执行该方法resolve。这是一个例子:
myPromise.then(result => {
});
result来自给该resolve方法的参数。
将then方法添加到您的承诺中。使用result它的回调函数的参数,并登录result到控制台。
处理被捕的被拒绝的承诺
catch是您的诺言被拒绝时使用的方法。它会在reject调用promise方法后立即执行。语法如下:
myPromise.catch(error => {
});
error是传递给reject方法的参数。
将catch方法添加到您的承诺中。使用error它的回调函数的参数,并登录error到控制台。