新增语法
for-of循环
for-of 循环机制可以对任何集合(Array,Map,Set,String或迭代器)进行循环。
for-in 循环是对对象进行循环。
<!-- Chrome 38以上、Edge、Firefox 13以上、Opera 25以上以及Safari 8以上版本的浏览器支持 -->
// Array
var array = [1,2,3,4];
// String
var string = "1234";
// Set
var set = new Set();
set.add('name');
set.add('age');
// Map
var map = new Map();
map.set("name", "张三");
map.set("age", 23);
for (const key of array) {
console.log(key)
}
var object = {
name:'张三',
age: 23
};
for (const key in object) {
console.log(key);
}
for (const key of object) {
console.log(key); // 报错:object is not iterable
}
let与const关键字声明变量
以 let
关键字声明的变量以代码块为作用域,而并非以当前函数为作用域。
以 let
关键字声明的全局变量并非全局对象的属性,所以不能使用 window.变量名
来访问这些变量。
使用 const
关键字声明的变量类似于使用 let 关键字声明的变量,区别在于只能在声明时对其进行赋值。
<!-- Chrome 41以上、Edge、Firefox 44以上、IE 11、Opera 17以上以及Safari 10以上版本的浏览器支持 -->
let a;
a = 1;
const a=1;
使用class关键字声明类
类的机制及使用方法
在 ECMAScript 前,JavaScript 都是通过 prototype 来变相实现其他语言中的“类”的功能。在 ES2015 之后增加了 class 语法,可以方便地实现类。
<!-- Chrome 42以上、Edge、Firefox 45以上以及Safari 10以上版本的浏览器支持 -->
class Cat {
eat() {
console.log("eat food");
}
}
let cat1 = new Cat();
cat1.eat();
实现类的静态方法
为类提供静态方法的定义方法。
var count = 0;
class Cat {
constructor(name) {
this.name = name;
count++;
}
eat() {
console.log(`${this.name} eat food`);
}
static getCount() {
return count;
}
}
console.log(Cat.getCount());
let cat1 = new Cat("大猫");
console.log(Cat.getCount());
let cat2 = new Cat("小猫");
console.log(Cat.getCount());
类的继承
当需要继承类并创建其对象时,我们可以使用 extends
关键字。
class Animal {
constructor(name) {
this.name = name;
}
run() {
console.log(`${this.name} run`);
}
}
class Cat extends Animal {
eat() {
console.log(`${this.name} eat food`);
}
}
var cat1 = new Cat("大猫");
cat1.run();
调用父类方法
当实现类的继承时,可以通过使用 super
关键字调用其父类的构造方法。
class Animal {
constructor(name) {
this.name = name;
}
say() {
console.log(`这是一只${this.color}${this.name}`);
}
}
class Cat extends Animal {
constructor(name, color) {
super(name);
this.color = color;
}
eat() {
console.log(`${this.name} eat food`);
}
}
var cat1 = new Cat("大猫", "黑");
cat1.say();
不确定参数及默认参数值
不确定参数
在使用 JavaScript 时,有时会遇到的问题是创建一个不确定参数的函数,即该函数可以接收任意多个参数。在 ES6 之前我们使用 arguments
对象(该对象为一个类似于数组的对象,其中包含了传递给函数的所有参数)来获取所有参数,而在 ES6 之后使用 … 的方式来获取。
在ES6之前可以这样实现
function show() {
var args = Array.prototype.slice.call(arguments);
// var args = [].slice.call(arguments);
console.log(args);
}
show(1,2,3,5);
<!-- Chrome 47以上、Edge、Firefox 15以上、Opera 34以上以及Safari 10以上版本的浏览器支持 -->
function show(...args) {
console.log(args);
}
show(1,2,3,5);
:::info 注意:只能将函数中的最后一个参数定义为不确定参数。 :::
默认参数值
在 ES6 之前,如果不明确指定参数值,在函数中该参数值为 undefined。
而在 ES6 中,函数调用时不需要明确地指定某个参数值,只需在函数定义时给它一个默认值即可。
<!-- Chrome 49以上、Edge、Firefox 15以上、Opera 45以上以及Safari 10以上版本的浏览器支持 -->
function animalSentence(fruit1 = "苹果", fruit2 = "雪梨") {
return `${this.fruit1} 与 ${this.fruit2}`;
}
console.log(animalSentence()); // 苹果与雪梨
console.log(animalSentence('香蕉')); // 香蕉与雪梨
除此之外,ECMAScript 2015中新增的默认参数值机制具有以下特性:
- 可以在默认参数值指定时使用条件判断表达式;
- 传入 undedined 等同于没有传入参数值;
- 不被指定默认参数值的参数值被视为 undefined;
箭头函数
箭头函数与普通函数的一个主要区别为:箭头函数中没有自己的 this 对象,箭头函数中的 this 对象总是从函数外部直接继承。
<!-- Chrome 45以上、Edge、Firefox 22以上、Opera 32以上以及Safari 10以上版本的浏览器支持 -->
var obj = {
age: 0,
show() {
setInterval(() => {
this.age++;
console.log(this.age);
}, 1000);
},
};
obj.show();
生成器函数
生成器函数的基本概念
ES2015 中新增了生成器函数,它的特点如下:
- 普通函数以 function 关键字开始,生成器函数以
function*
关键字开始; - 在生成器函数中,
yield
为一个关键字,其作用类似于 return; - 普通函数不能被暂停,但生成器函数可以。普通函数内部只返回一次,虽然生成器函数也只返回一次,但可在其内部书写多条 yield 语句。yield 语句将生成器函数暂且挂起,所以可以被后期恢复。
对于 JavaScript 普通函数来说,一旦你调用函数,函数会立即执行,直到函数返回或抛出错误;但当你调用一个生成器函数时,函数并不会立即执行,只是返回一个被暂停的 Generator 对象。
Generator 对象具有一个 next
方法,每次你调用该对象的 next 方法之后,函数体中的代码继续执行,直到下一个 yield 语句被执行后。
next 方法的返回值对象(IteratorResult
对象)具有 done
属性与 value
属性。done 属性值可用来判断对集合的访问是否已结束,value 属性值为每一次访问时迭代器的返回值。
当一个生成器函数被运行时,它与其被调用脚本代码处于同一线程中,代码执行顺序是连续的,可确定的,永远
不会被并发执行。与系统线程不同,一个生成器函数只在其内部一条 yield 语句被执行时挂起。
定义迭代器
在使用 for-of 循环对集合遍历时,在每一次循环内执行生成器函数内部代码,直到 yield i;
语句执行,函数返回,然后在浏览器控制台中输出函数返回值,再执行下一次循环。
<!-- Chrome 39以上、Edge、Firefox 26以上、Opera 26以上以及Safari 10以上版本的浏览器支持 -->
function* range(start, stop) {
for (let i = start; i < stop; i++) {
yield i;
}
}
for (const i of range(1, 4)) {
console.log(`输出的是${i}`);
}
解构赋值
解构赋值,是指同时使用数组或对象属性中保存的多个值分别为多个变量赋值。
// 1、ES5之前通过下标来取值,ES6直接将数组中的多个值保存到多个变量
let [pear, apple] = ["雪梨", "苹果"];
// 2、ES5之前通过apply和Math.max结合来获取最大值,ES6之后可以通过spread运算符来将数组传递为函数的参数值
var max = Math.max.apply(null, [1,100,-1,23]);
let max = Math.max(...[1,100,-1,23]);
// 3、对于对象的取值
let {apple, pear} = {apple:'苹果', pear:'雪梨'};
// 4、多阶层中的使用方法,对象同理
let [[firstname, lastname]] = [['张','三']];
【注意】
当对数组使用解构赋值方法给变量赋值后,可以再次对数组使用解构赋值方法修改这些变量值。而对象则不行。
<!-- Chrome 49以上、Edge、Firefox 41以上、Opera 26以上以及Safari 8以上版本的浏览器支持 -->
let [apple, orange, pear] = ['苹果','橙子','梨'];
[apple, orange, paer] = ['大苹果','大橙子','大梨'];
// 报错
let {apple, orange, pear}={apple:'苹果', orange:'橙子', pear:'雪梨'};
{apple, orange, pear}={apple:'大苹果', orange:'大橙子', pear:'大雪梨'}; // 报错
// 错误的原因是左边的{apple, orange, pear}是属性名集合,并不是对象的集合
({apple, orange, pear}={apple:'大苹果', orange:'大橙子', pear:'大雪梨'})'
模板字符串
模板字符串提供了一种全新的字符串定义方式,它使用反引号(``)进行标记。在下面几方面增强了:
- 字符串修改
- 内置表达式
- 多行字符串
- 字符串格式化
- 为安全的HTML本地化而提供的字符串标签
标签化的模板通过在模板字符串前放置一个函数名来修改模板字符串。
<!-- Chrome 41以上、Edge、Firefox 34以上、Opera 28以上以及Safari 9以上版本的浏览器支持 -->
function format(str, ...indexes) {
var result = "";
for (let i = 0; i < str.length; i++) {
if (str[i]) result += str[i];
if (indexes[i]) result += indexes[i];
}
return result;
}
var apple = "苹果";
var pear = "雪梨";
var s = format`这是${apple}还是${pear}`;
console.log(s);
在客户端使用JavaScript模块
在 ES2015 中使用模块概念。
// class.js 通过export导出可供使用的方法或变量给外部
function show() {
console.log("张三");
}
export { show };
在使用端(html文件)中,不再使用 type=”text/javascript” 来引用外部脚本文件,而是使用 type="module"
语句导入 JavaScript 模块文件。
<script type="module">
// "./class.js" 的书写路径必须正确
import {show} from './class.js'; // 导入需要的方法
// import { show as showMehod } from './class.js'; // 导入时通过as更改别名
// import * as All from './class.js'; // 通过通配符*可导入所有的变量、函数、对象
show();
</script>
当你通知 JavaScript 引擎需要运行一个模块时,JavaScript 引擎依序执行以下四个步骤:
- 解析:引擎读取源代码并检查语法错误;
- 加载:引擎加载所有需要导入的模块。目前这个过程并没有被标准化。
- 链接:针对每个新加载的模块,引擎创建一个模块作用域并为模块中所有声明赋予作用域,包括从其他模块中导入的变量、函数或对象等。
- 运行:最终,引擎运行所有模块中的语句。这时,导入工作事实上已经完毕。
基于上述加载规则,JavaScript 模块加载时具有下述使用规则:
- 只能在顶级作用域中使用 import 关键字以及 export 关键字;
- 所有导出必须明确指定导出名;
- 模块对象是被冻结的,不允许在模块外部对模块添加新的特性;
- 不能对模块导入进行错误处理。一个应用程序可能导入很多模块,如果其中有一个模块被加载或链接失败,整个模块系统就被破坏了。不能在 try/catch 机制中进行导入;
- 在模块中的依赖被加载前,不允许运行模块中的任何代码。这意味着模块不控制如何加载其中的依赖。
to be continue…