FreeCodeCamp学习总结-ES6部分

930 阅读11分钟

前言

FreeCodeCamp是一个以“帮助人们免费学习编程”为主旨的非营利性组织,其网站收录了众多训练式题目。作为前端入门有较大的帮助。

英文版(源)地址:www.freecodecamp.org/

中文社区:chinese.freecodecamp.org/forum/t/top…

此系列博文为相关知识点记录,算是自己对前端入门的一个回顾。有空还是强烈建议去刷一遍原题+知识点

ECMAScript 是 JavaScript 的标准化版本,它旨在统一语言语言的规范和功能。所有主流的浏览器或者 Javascript 的运行环境都支持这个规范,因此 ECMAScript 和 JavaScript 的术语是通用的。

在2009年以前,freeCodeCamp 的大多数挑战都是遵循的 ECMAScript 5 (ES5) 的规范。但是 JavaScript 是一门不断发展的语言。随着不断的功能添加及修订,ECMAScript 发布了新的版本供开发者使用。

在2015年,ECMAScript 发布了被称为 ECMAScript 6 (ES6) 的最新版本。在这个挑战里,我们会学习新版本添加的许多强大的功能,包括:

  • 箭头函数
  • 模块
  • Promises 对象
  • 异步生成器 Generators
  • let以及const语法

请注意

不是所有的浏览器都支持 ES6 的功能,在浏览器支持 ES6 代码之前,如果你想要在你的项目里使用 ES6 规范,你可能需要一些转译工具将 ES6 代码转换成 ES5 的代码。

  • 探索 var 和 let 关键字之间的差异
    • 使用var关键字来声明变量,会出现重复声明导致变量被覆盖却不会报错的问题
    • 在 ES6 中引入了新的关键字let来解决var关键字带来的潜在问题
    • 与var不同的是,当使用let的时候,同一名字的变量只能被声明一次。
    • 请注意"use strict"。这代表着开启了严格模式,用于检测常见的代码错误以及"不安全"的行为
  • 比较 var 和 let 关键字的作用域
    • 当你使用var关键字来声明一个变量的时候,这个变量会被声明成全局变量,或是函数内的局部变量。
    • let关键字的作用类似,但会有一些额外的特性。如果你在代码块、语句或表达式中使用关键字let声明变量,这个变量的作用域就被限制在当前的代码块,语句或表达式之中

    这里可以再多做点调查

  • 用 const 关键字声明只读变量
    • const拥有let的所有优点,所不同的是,通过const声明的变量是只读的。这意味着通过const声明的变量只能被赋值一次,而不能被再次赋值。
    • 使用const声明的变量进行最佳实践的重命名,变量名中的字母应该都是大写的。
  • 防止对象改变
    • const声明并不会真的保护你的数据不被改变。为了确保数据不被改变,JavaScript 提供了一个函数Object.freeze来防止数据改变
    • 当一个对象被冻结的时候,你不能再对它的属性再进行增、删、改的操作。任何试图改变对象的操作都会被阻止,却不会报错
let obj = {
  name:"FreeCodeCamp",
  review:"Awesome"
};
Object.freeze(obj);
obj.review = "bad"; // obj 对象被冻结了,这个操作会被忽略
obj.newProp = "Test"; // 也会被忽略,不允许数据改变
console.log(obj);
  • 使用箭头函数编写简洁的匿名函数
    • 在 JavaScript 里,我们会经常遇到不需要给函数命名的情况,尤其是在需要将一个函数作为参数传给另外一个函数的时候。这时,我们会创建匿名函数。因为这些函数不会在其他地方复用,所以我们不需要给它们命名。
    • 当不需要函数体,只返回一个值的时候,箭头函数允许你省略return关键字和外面的大括号。这样就可以将一个简单的函数简化成一个单行语句。
    • const myFunc= () => "value"
  • 编写带参数的箭头函数
  • 编写高阶箭头函数
    • 箭头函数在类似map(),filter(),reduce()等需要其他函数作为参数来处理数据的高阶函数里会很好用。
    • map函数可以看成是一种映射函数,而且是一一映射
      • [1, 2, 3, 4].map(a=>a*10) 结果为[10, 20, 30, 40]
    • filter函数可以看成是一个过滤函数,返回符合条件的元素的数组
      • filter和map的区别:filter需要在循环的时候判断一下是true还是false,是true才会返回这个元素;map没有这个过程。
      • filter函数适合筛选一个数组中满足条件的元素,注意:filter函数只是筛选功能,不能改变元素、操作元素
      • [1, 2, 3, 4].fliter(a => a>3) 结果为[4, 5]
    • reduce函数可以理解成一个迭代函数
      • reduce函数有四个参数:之前值,当前值,索引值,数组本身。previous值取决于[initialValue]如果指定[initialValue]指定是,则作为previous的初始值,也可作为空数组[],如果缺省的话,则将数组的第一个元素作为previous的初始值,下次循环时,之前值就是上一次的当前值,而当前值会变成下一个索引对应的元素,依次类推。
      • 这个好像不大理解

    • find 查找到第一个符合条件的元素,则立刻返回
      • [1, 2, 3, 4].find(i => i>2) 结果为3
使用箭头函数的语法来计算squaredIntegers数组里正整数的平方(分数不是整数)
const realNumberArray = [4, 5.6, -9.8, 3.14, 42, 6, 8.34];
const squareList = (arr) => {
  "use strict";
  // 在这行以下修改代码
  const squaredIntegers = arr.filter((num) => num > 0 && num % parseInt(num) == 0).map((num) => num * num);
  // 在这行以上修改代码
  return squaredIntegers;
};
  • 设置函数的默认参数
    • 默认参数会在参数没有被指定(值为 undefined )的时候起作用
  • 将 rest 操作符与函数参数一起使用
    • 用于函数参数的 rest 操作符帮助我们创建更加灵活的函数。在rest操作符的帮助下,你可以创建有一个变量来接受多个参数的函数。这些参数被储存在一个可以在函数内部读取的数组中
    • rest操作符可以避免查看args数组的需求,并且允许我们在参数数组上使用map(),filter(),和reduce()
const sum = (function() {
  "use strict";
  return function sum(...args) {
    return args.reduce((a, b) => a + b, 0);
  };
})();
  • 使用 spread 运算符展开数组项
    • apply()来计算数组的最大值:
var arr = [6, 89, 3, 45];
var maximus = Math.max.apply(null, arr); // 返回 89
我们必须使用Math.max.apply(null,arr),是因为直接调用Math.max(arr)会返回NaNMath.max()函数需要传入的是一系列由逗号分隔的参数,而不是一个数组。
  • ...arr返回了一个“打开”的数组。或者说它 展开 了数组。
  • const arr = [6, 89, 3, 45]; const maximus = Math.max(...arr); // 返回 89
  • 然而,展开操作符只能够在函数的参数中,或者数组之中使用
const arr1 = ['JAN', 'FEB', 'MAR', 'APR', 'MAY'];
let arr2;
(function() {
  "use strict";
  arr2 = [...arr1]; // 改变这一行
})();
  • 使用解构赋值从对象中分配变量
    • 解构赋值 就是可以从对象中直接获取对应值的语法
var voxel = {x: 3.6, y: 7.4, z: 6.54 };
var x = voxel.x; // x = 3.6
var y = voxel.y; // y = 7.4
var z = voxel.z; // z = 6.54

const { x, y, z } = voxel; // x = 3.6, y = 7.4, z = 6.54

如果你想将voxel.x,voxel.y,voxel.z的值分别赋给a,b,c
const { x : a, y : b, z : c } = voxel; // a = 3.6, b = 7.4, c = 6.54

使用解构语法去得到输入的str字符串的长度,并将长度赋值给len。
  const {length : len} = str; // change this
  • 使用解构赋值从嵌套对象中分配变量
    • const { tomorrow: { max: maxOfTomorrow } } = forecast;
  • 使用解构赋值从数组中分配变量
    • 与数组解构不同,数组的扩展运算会将数组里的所有内容分解成一个由逗号分隔的列表。所以,你不能选择哪个元素来给变量赋值。
    • 使用数组解构来交换变量a与b的值。使a、b能分别获得对方的值。
    • [a, b] = [b, a];
  • 使用解构赋值配合 rest 操作符来重新分配数组元素
const [a, b, ...arr] = [1, 2, 3, 4, 5, 7];
console.log(a, b); // 1, 2
console.log(arr); // [3, 4, 5, 7]
rest操作只能对数组列表最后的元素起作用。这意味着你不能使用rest操作符来截取原数组中间元素的子数组。

使用解构赋值以及rest操作符来进行一个Array.prototype.slice相同的操作。使得arr是原数组source除开前两个元素的子数组。
  const [a, b, ...arr] = list;
  • 使用解构赋值将对象作为函数的参数传递
const profileUpdate = (profileData) => {
  const { name, age, nationality, location } = profileData;
  // 对这些变量执行某些操作
}
上面的操作解构了传给函数的对象。这样的操作也可以直接在参数里完成:

const profileUpdate = ({ name, age, nationality, location }) => {
  /* 对这些参数执行某些操作 */
}
  • 使用模板字面量创建字符串
    • variable语法是一个占位符。这样一来,你将不再需要使用+运算符来连接字符串。当需要在字符串里增加变量的时候,你只需要在变量的外面括上{variable}语法是一个占位符。这样一来,你将不再需要使用+运算符来连接字符串。当需要在字符串里增加变量的时候,你只需要在变量的外面括上{和},并将其放在字符串里就可以了。

const result = {
  success: ["max-length", "no-amd", "prefer-arrow-functions"],
  failure: ["no-var", "var-on-top", "linebreak"],
  skipped: ["id-blacklist", "no-dup-keys"]
};
function makeList(arr) {
  "use strict";

  // 在这行以下修改代码
  const resultDisplayArray = [`<li class="text-warning">${arr[0]}</li>` ,
  `<li class="text-warning">${arr[1]}</li>` ,
  `<li class="text-warning">${arr[2]}</li>`];
  // 在这行以上修改代码

  return resultDisplayArray;
}
/**
 * makeList(result.failure) 应该返回:
 * [ <li class="text-warning">no-var</li>,
 *   <li class="text-warning">var-on-top</li>,
 *   <li class="text-warning">linebreak</li> ]
 **/
  • 使用简单字段编写简洁的对象字面量声明 const getMousePosition = (x, y) => ({ x, y });
  • 用 ES6 编写简洁的函数声明
    • 在 ES6 语法的对象中定义函数的时候,你可以完全删除function关键字和冒号
const person = {
  name: "Taylor",
  sayHello() {
    return `Hello! My name is ${this.name}.`;
  }
};
  • 使用 class 语法定义构造函数
    • class只是一个语法糖,它并不像 Java、Python 或者 Ruby 这一类的语言一样,严格履行了面向对象的开发规范。
    • 在 ES5 里面,我们通常会定义一个构造函数,然后使用 new关键字来实例化一个对象
    • class的语法只是简单地替换了构造函数的写法
ES5
var SpaceShuttle = function(targetPlanet){
  this.targetPlanet = targetPlanet;
}
var zeus = new SpaceShuttle('Jupiter');

ES6
class SpaceShuttle {
  constructor(targetPlanet){
    this.targetPlanet = targetPlanet;
  }
}
const zeus = new SpaceShuttle('Jupiter');
  • 注意class关键字声明了一个新的函数,并在其中添加了一个会在使用new关键字创建新对象时调用的构造函数。
function makeClass() {
  "use strict";
  /* 在这行下面改写代码 */
  class Vegetable {
    constructor(name){
      this.name = name;
    }
  }
  /* 在这行上面改写代码 */
  return Vegetable;
}
const Vegetable = makeClass();
const carrot = new Vegetable('carrot');
console.log(carrot.name); // => 应该显示 'carrot'
  • 使用 getter 和 setter 来控制对象的访问
    • Getter 函数的作用是可以让返回一个对象私有变量的值给用户,而不需要直接去访问私有变量
    • Setter 函数的作用是可以基于传进的参数来修改对象中私有变量的值。这些修改可以是计算,或者是直接替换之前的值
function makeClass() {
  "use strict";
  /* 请把你的代码写在这条注释以下 */
  class Thermostat{
    constructor(temperature){
      this._temperature = temperature;
    }
    temperature(){
      return 5/9*(this._temperature-32);
    }
    temperature(temp){
      this._temperature = temp;
    }
  }
  /* 请把你的代码写在这条注释以上 */
  return Thermostat;
}
const Thermostat = makeClass();
const thermos = new Thermostat(76); // 使用华氏温度来初始化
let temp = thermos.temperature; // 摄氏温度24.44度
thermos.temperature = 26;
temp = thermos.temperature; // 摄氏温度26度
  • 了解 import 和 require 之间的差异
    • 在过去,我们会使用require()函数来从外部文件或模块中引入函数或者代码。这时候会遇到一个问题:有些文件或者模块会特别大,但你却往往只需要引入其中的一些核心代码。
    • ES6 给我们提供了import这个便利的工具。通过它,我们能够从外部的文件或者模块中选择我们需要的部分进行引入,从而节约载入的时间和内存空间。
    • import { function } from "file_path_goes_here" // 我们还可以用同样的方式来引入变量!
    • 在大括号里的函数名的两侧加上空格是一个最佳实践——这可以帮助我们轻松的阅读import语句。
    • 在许多的例子中,在文件的路径前会加上./;否则, node.js 会先尝试去node_modules目录中寻找依赖项。
  • 用 export 来重用代码块
    • 我们想要一些代码——函数或者变量——在其他文件中使用,我们必须将它们导出来供其他文件导入。和import一样,export也是一个非浏览器的功能。

const capitalizeString = (string) => {
  return string.charAt(0).toUpperCase() + string.slice(1);
}
export { capitalizeString } //如何导出函数。
export const foo = "bar"; //如何导出变量。

另外,如果你想要将你所有的export语句打包成一行,你可以像下面这个例子一样实现:

const capitalizeString = (string) => {
  return string.charAt(0).toUpperCase() + string.slice(1);
}
const foo = "bar";
export { capitalizeString, foo }
  • 用 * 从文件中导入所有内容
    • import * as object_with_name_of_your_choice from "file_path_goes_here" object_with_name_of_your_choice.imported_function
    • 你可以在import * as之后添加任意的名称。这个方法接收到的值是一个对象,你可以使用点表示法来获取对象里具体的值。
    • import * as myModule from "capitalize_strings";
  • 用 export default 创建一个默认导出
    • 在文件中只有一个值需要导出的时候,你通常会使用这种语法。它也常常用于给文件或者模块创建返回值。
    • 当使用export default去声明一个文件或者模块的返回值,你在每个文件或者模块中应当只默认导出一个值。特别地,你能将export deafult与var,let与const一起使用 export default function subtract(x,y) {return x - y;}
  • 导入一个默认的导出
    • 这个语法只有一处不同的地方 —— 被导入的add值,并没有被花括号{}所包围。与导出值的方法不同,导入默认导出的写法仅仅只是简单的将变量名写在import之后。

默认似乎都只有一个可以导入导出,待详细查询