一、 ES6 基本语法
1.1 let
作用域就是一个变量的有效的范围,就是你声明一个变量以后,这个变量在什么场合可以使用它。以前JavaScript只有全局作用域和函数作用域,现在JavaScript也有了块级作用域(block scope)
1.1.1 let 语法
let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];
1.1.2 参数解释
var1, var2, …, varN 变量名。可以是任意合法的标识符。
value1, value2, …, valueN 变量的初始值。可以是任意合法的表达式。
1.1.2 let 描述
let允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与var关键字不同的是,它声明的变量只能是全局或者整个函数块的。
1.1.3 let 作用域规则
let声明的变量只在其声明的块或子块中可用,这一点,与var相似。二者之间最主要的区别在于var声明的变量的作用域是整个封闭函数。
看下面的例子:
function varTest() {
var x = 1;
if (true) {
var x = 2; // 同样的变量!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // 不同的变量
console.log(x); // 2
}
console.log(x); // 1
}
1.2 const
1.2.1 const 语法
const name1 = value1 [, name2 = value2 [, ... [, nameN = valueN]]];
1.2.2 参数解释
nameN
常量名称,可以是任意合法的标识符。valueN
常量值,可以是任意合法的表达式。
1.2.3 const描述
此声明创建一个常量,其作用域可以是全局或本地声明的块。 与var变量不同,全局常量不会变为窗口对象的属性。需要一个常数的初始化器;也就是说,您必须在声明的同一语句中指定它的值(这是有道理的,因为以后不能更改)。
const声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。例如,在引用内容是对象的情况下,这意味着可以改变对象的内容(例如,其参数)。
1.2.4 实例
<script>
const a = "123";
a = "234";
console.log(a);// 会出错 Uncaught TypeError: Assignment to constant variable.
const arr = [1,2,3];
arr.push(4);
console.log(arr);// [1,2,3,4]
arr = [];
console.log(arr);// 改变数组的指向会出错 Uncaught TypeError: Assignment to constant variable
</script>
1.2.5 小结
如果const后面的变量是普通变量,改变值报错。如果后面存储的是数组或者对象,那么改变它的指向也会报错,但是如果改变数组或者对象的值是不会发生错误的
1.3 解构赋值
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
1.3.1 数组的解构赋值
- ES5 中的变量赋值,只能直接指定值
var a = 1;
var b = 2;
var c = 3;
- ES6 可以这样写
var [a, b, c] = [1, 2, 3];
console.log(a,b,c);// 1 2 3
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
1.3.2 允许使用默认值
var [a=1] = []
console.log(a);// 1
var [a,b=2] = [3];
console.log(a,b);//3 2
var [a,b=4] = [5,undefined];
console.log(a,b);// 5 4
1.3.4 使用默认值的注意事项
注意一:
ES6内部使用严格相等运算符(===),判断一个位置是否有值。 所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。
var [d=8] = [undefined];
console.log(d);// 8
var [a=9] = [null];
console.log(a);// null
上面代码中,如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined。
注意二:
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
function getNumber(){
console.log("只有用到我的时候我才出现");
}
var [a=getNumber()] = ["我先来"];
console.log(a);// 会输出 我先来
上面代码中,因为a能取到值,所以函数f根本不会执行。上面的代码其实等价于下面的代码。
function getNumber(){
console.log("只有用到我的时候我才出现");
}
let a;
if(["我先来"][0]===undefined){
a = getNumber();
}else{
a = ["我先来"][0];
}
console.log(a);// 会输出 我先来
注意三:
默认值可以引用解构赋值的其他变量,但该变量必须已经声明
let [a,b=a]=[1];
console.log(a,b);// a = 1 b = 1
let [x=2,y=x]=[];
console.log(x,y);// x = 2 y = 2
let [c=1,d=c]=[12,3];
console.log(c,d);// c =12 d = 3
let [e=f,f=1]=[];
console.log(e,f);// 报错 f is not defined
上面最后一个表达式之所以会报错,是因为e用到默认值f时,f还没有声明
1.3.5 对象的解构赋值
解构赋值不仅可以用于数组,还可以用于对象.
eg:
let {name,age} = {name:"wxk",age:20};
console.log(name); // wxk
-
注意:对象的解构与数组有一个重要的不同
- 数组的元素是按次序排列的,变量的取值由它的位置决定 - 对象的属性没有次序,变量必须与属性同名,才能取到正确的值
小编总结:等号左边的变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。但是如果变量没有对应的同名属性,则会导致取不到值,最后等于undefined
1.4 函数
1.4.1 为函数的参数设置默认值
在es6里面我们可以给定义的函数接收的参数设置默认的值,如果不去指定这个函数的参数的值的话,就会使用这些参数的默认的值
function Person(name="Tom",age=12){
console.log(name);//Tom
console.log(age);// 12
}
Person();
如果在调用函数的时候传入实参,则会改变默认参数的值。eg:
function Person(name="Tom",age=12){
console.log(name);//Jack
console.log(age);// 20
}
Person("Jack",20);
1.4.2 ... 操作符
...是es6中新添加的一种操作符,可以叫做spread(扩展)或则rest(剩余)
具体用法如下:
- rest (剩余操作符)
剩余操作符一般会用在函数的参数里面。比如:
想让一个函数支持更多的参数,参数的数量不受限制,这个时候就可以 使用剩余操作符。
eg:
<script>
function restName(first,second,...third){
console.log(first);// Tom
console.log(second);// Jack
console.log(third);// [Emma Edith May]
}
restName("Tom","Jack","Emma","Edith","May");
</script>
剩余操作符后面的变量会变成一个数组,多余的参数会被放入这个数组中
- spread(扩展运算符)
...操作符如果用在数组的前面,作用就是将这个数组展开,因此称为扩展操作符。相当于rest操作符的逆运算
eg:
<script>
let arr1 = ["Tom","Jack","Emma","Edith","May"];
let arr2 = ["Haliey","Lvy"];
let arr3 = [...arr1,...arr2];
console.log(arr3);
</script>
这里呢把数组arr1 和 数组 arr2 用 ... 操作符进行了扩展,所有他们变成了字符串,不信你们可以尝试一下,直接输出 console.log(...arr1)
1.4.3 函数的 name 属性
es6给函数添加了一个name属性,使用这个属性我们可以得到函数的名字
eg:
<script>
function getName(){}
console.log(getName.name);// getName;
</script>
1.4.4 箭头函数(重点)
箭头函数的语法
- 不引入参数
<script>
// ES5 语法
var sum = function(){
return 1 + 2;
}
// 等同于
// ES6 语法
var sum = ()=> 1 + 2;
console.log(sum());// 3
</script>
2. 引入单个参数
<script>
//ES5 语法
var refelect = function(value){
return value;
}
// 等同于
// ES6
var refelect = (value)=> value;
console.log(refelect(144155));// 144155
</scripte>
3. 引入多个参数,则应加上小括号
// ES5 语法
function sum(num1,num2){
return num1 + num2;
}
// 等同于
// ES6 语法
var sum = (num1,num2)=> num1 + num2;
console.log(sum(5,9));//14
4. 若你想使用标准的函数体,或者函数体内可能有更多的语句要执行,则要用大括号将函数体括起来,并明确定义返回值。
// ES6 语法
var sum = (num1, num2) => { return num1 + num2; }
//等同于:
// ES5 语法
var sum = function(num1, num2) {
return num1 + num2;
};
5. 箭头函数若要返回自定义对象的话,就必须用小括号把该对象括起来先
<script>
// 定义一个函数表达式,返回一个对象 这是ES5语法
var person = function(name){
return {
name: "Tom",
age: 20
};
};
// 等同于 下面ES6 语法
var person = (name)=>({name: "Tom",age: 20})
console.log(person().name);// Tom
</script>
1.4.5 箭头函数的 this 指向
箭头函数本身是没有this和arguments的,在箭头函数中引用this实际上是调用的是定义时的上一层作用域的this。
这里强调的是上一层作用域,是因为对象是不能形成独立的作用域的。
看下面的小例子更容易明白:
eg1
<script>
var obj = {
say: function() {
var f1 = ()=>{
console.log("1111",this);
}
f1();
}
}
var o = obj.say;
o();//f1执行时,say函数指向window,所以f1中的this指向window
obj.say();//f1执行时,say函数指向obj,所以f1中的this指向obj;
</script>
eg2:
var ojb = {
pro: {
getPro: ()=>{
console.log(this);
}
}
}
ojb.pro.getPro();//this指向的是window,因为箭头函数定义时,getPro的上一级是pro,是一个对象,不能形成单独的作用域,故指向window。
总结箭头函数的this指向:箭头的this,向上找,找到非箭头函数,看一下这个非箭头函数的this是谁,那么箭头函数的this就是这个非箭头函数的this。
1.5 字符串
1.5.1 includes(), startsWith(), endsWith()
传统上,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
举例说明三个方法怎么用:
<script>
var string = "我喜欢的人喜欢我";
console.log(string.includes("喜欢"));// true
console.log(string.startsWith("我"));// true
console.log(string.endsWith("喜欢"));// false
</script>
注意:这三个方法都支持第二个参数,表示开始搜索的位置
1.5.2 模板字符串
模板字符串中所有的空格、新行、缩进,都会原样输出在生成的字符串中
<script>
// 以前我们将变量和字符串进行连接时的写法是这样的:
var str1 = "Hello";
var str2 = "World";
var str3 = str1 +str2 + "前端工程师";
console.log(str3);//HelloWorld前端工程师
// ES6 中的写法是这样的,很简单
var str4 = `前端工程师${str1},${str2}`;
console.log(str4);// 前端工程师Hello,World
</script>
注意:模板字符串(template string)是增强版的字符串,用反引号(`),标识,嵌入的变量名写在${}之中
1.6 对象 Object
1.6.1 属性的简洁表示法
<script>
// 以前在对象里面添加跟已定义的变量名字相同的属性如下:
var name= "Tom";
var age= 20;
var person= {
name:name,
age:age
}
console.log(person.name);// Tom
console.log(person.age);// 20
// ES6 给出了简单的写法,在对象里面直接加上这属性, 不需要再指定值
var color= "red";
var weight= 50;
var animal = {
color,
weight
}
console.log(animal.color);// red
console.log(animal.weight);// 50
</script>
ES6为我们提供了一种简写方法,就是在对象里面直接加上这属性,不需要再指定值
1.6.2 对象新增函数Object.is()
ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
ES6提出“Same-value equality”算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
见下面的例子:
<script>
console.log(+0 === -0);// true 在数学上二者是不等的
console.log(NaN === NaN);// false 理论上是相等的,但是在ES5中是不等的 ,这时ES6 就有了一个方法专门针对这些 bug ,那就是 is()
console.log(Object.is(+0 , -0));// false
console.log(Object.is(NaN , NaN));// true
</script>
1.6.3 对象新增函数Object.assign()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
<script>
// 定义一个person 对象
var person = {
name:"Tom",
age:20
}
// 定义一个学生对象
var student = {
stu: "前端",
moy: "10k"
}
var obj = {}
// assign 第一参数为目标对象,后面的为源对象
Object.assign(obj,person,student);
console.log(obj);
</script>
1. 注意:如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后 面的属性会覆盖前面的属性
<script>
// 定义一个person 对象
var person = {
name:"Tom",
age:20
}
// 定义一个学生对象
var student = {
name:"Jack",
stu: "前端",
moy: "10k"
}
var obj = {
}
Object.assign(obj,person,student);
console.log(obj);//{name: "Jack", age: 20, stu: "前端", moy: "10k"}
</script>
2. 如果只有一个参数,Object.assign会直接返回该参数
var cat = {
height:50
}
var str = {}
Object.assign(str,cat);
console.log(str);//{height: 50}
3. 如果该参数不是对象,则会先转成对象,然后返回
console.log(Object.assign(2));// Number {2}
4. 由于undefined和null无法转成对象,所以如果它们作为参数,就会报错。
1.7 集合对象
1.7.1 Set 对象
Set对象是一组值的集合,这些值是不重复的,无序的,与我们在数学中学到的集合是一样的。我们可以往里面添加、删除、查询数据
1. 先声明一个Set对象
var mySet = new Set();
2. 往这个集合对象中添加元素
mySet.add(1);
mySet.add("some text");
3. 判断集合中是否存在一个元素1
mySet.has(1); // true 返回一个布尔值,表示该值在Set中存在与否。
4. 删除集合中的字符串
mySet.delete("foo");//移除Set的中与这个值相等的元素
5. 获取集合中元素的数量
mySet.size; // 返回Set对象的值的个数。
6. 删除集合中所有的元素
mySet.clear();//移除Set对象内的所有元素。
<script>
//声明一个set对象
var mySet = new Set();
// 向集合中添加元素
mySet.add(123);
mySet.add("前端");
mySet.add("全栈");
console.log(mySet);
// 判断该对象中是否存在123
var a = mySet.has(123);
console.log(a);// true
// 删除该对象中的 123
mySet.delete(123);
console.log(mySet);
// 获取元素的数量 size
var b = mySet.size;
console.log(b);
// 删除集合中所有的元素
mySet.clear();
console.log(mySet);
</script>
1.7.2 Map对象
如果你需要一个键值对的数据结构,我们可以声明一个Map对象,这个对象里面可以包含多个项目,每个项目都有一个名字,还有一个跟它对应的值
1. 创建Map对象的方法是使用new操作符去声明这个对象
var myMap = new Map();
2. 向Map对象里面添加键值对,其中键和值可以是任意值(对象或者原 始值)
var obj = {};
var fun = function(){};
var str = “HelloWorld”;
myMap.set(obj, “我是对象”);
myMap.set(fun, “我是函数”);
myMap.set(str, “我是字符串”);
3. 查看集合中元素的数量
myMap.size
4. 获取相应的键值
myMap.get(obj);
5. 删除一个键值对,然后再判断该键值对是否存在
myMap.delete(fun);
myMap.has(fun);
6. 删除Map集合中所有的键值对
myMap.clear();
eg:
<script>
// 创建一个Map 对象
var myMap = new Map();
// 向对象中添加数据
var obj = {
name: "Tom",
age: 25
}
var str = "前端工程师";
var sum = function(a,b){
return a + b;
}
myMap.set(obj,obj.name);
myMap.set(str,"前端工程师");
myMap.set(sum,sum(2,4));
console.log(myMap);
</script>
其余的方法就不演示了,和上面的Set方法一样
1.7.3 Class (重点)
<script>
//ES5 中使用面向对象是这样的
function person(name,age){
this.name = name;
this.age = age;
this.run = function(){
console.log("每天坚持锻炼");
}
}
// 实例化一个对象
var student = new person("Tom",20);
student.run();// 会输出 每天坚持锻炼
</script>
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码就可以这样改
class person {
constructor(name,age){
this.name = name;
this.age = age;
}
run(){
console.log("每天坚持锻炼");
}
}
var student = new person("Jack",15);
student.run();
小编分析:上面代码定义了一个“类”,可以看到里面有一个constructor方法, 这就是构造方法,而this关键字则代表实例对象。也就是说,ES5的构造函数person,对应ES6的person类的构造方法。person类除了构造方法,还定义了一个run方法。注意,定义“类”方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。构造函数的prototype属性,在ES6的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面
1.7.4 class继承 (重点)
<script>
// 定义一个猫科动物
class Felidae{
constructor(color,weight){
this.color = color;
this.weight = weight;
}
eat(){
console.log("吃肉");
}
}
// 定义一个猫类,并继承猫科动物的一些特征
class Cat extends Felidae {
constructor(color,weight,height){
super(color, weight);
this.height = height;
}
silly(){
console.log("萌萌的猫咪很可爱!");
}
}
// 实例化一个Kitty猫
var Kitty = new Cat("pink",20,30);
Kitty.silly();
</script>
extends关键字用于实现类之间的继承。子类继承父类,就继承了父类的所有属性和方法,使用super可以调用父类的方法
1.8 Promise对象
1.8.1 什么是Promise对象
一个 Promise 对象可以理解为一次将要执行的操作(常常被用于异步操作),使用了 Promise 对象之后可以用一种链式调用的方式来组织代码,让代码更加直观。而且由于 Promise.all 这样的方法存在,可以让同时执行多个操作变得简单
下面简单介绍Promise 对象,如下代码:
<script>
function HelloWorld(ready){
return new Promise(function(resolve,reject){
if(ready){
resolve("HelloWorld");
}else{
reject("GoodBay");
}
});
}
HelloWorld(true).then(function(message){
console.log(message);
},function(error){
console.log(error);
});
</script>
上面的代码实现的功能非常简单,helloWord 函数接受一个参数,如果为 true 就打印 "Hello World!",如果为 false 就打印错误的信息。helloWord 函数返回的是一个 Promise 对象。在 Promise 对象当中有两个重要方法————resolve 和 reject。
resolve 方法可以使 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操作,在这个例子当中就是 Hello World!字符串
reject 方法则是将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操作
1.8.2 Promise的三种状态
- resolved 可以理解为成功的状态
- rejected 可以理解为失败的状
- pending promise对象实例创建时候的初始状态
helloWorld 的例子中的 then 方法就是根据 Promise 对象的状态来确定执行的操作,resolve 时执行第一个函数(onFulfilled),reject 时执行第二个函数(onRejected)
如何查看Promise对象的方法,在控制台输出 new Promise(function(){})
见下图:
### 1.8.3 使用Promise处理多任务
看下面的例子:
function HelloWorld(ready){
return new Promise(function(resolve,reject){
if(ready){
resolve("HelloWorld");
}else{
reject("GoodBay");
}
});
}
HelloWorld(true).then(function(message){
console.log(message);
},function(error){
console.log(error);
}).then(function(){
console.log("你好,世界!");
}).then(function(){
console.log("讲真的");
});
解析:可以看到后面的方法用了链式的方式进行编程,这是因为,then返回的还是一个 Promise 对象,因此返回的对象依然具有 then 方法,并且返回的对象状态是 resolved, 所以后面的 then方法全部是执行then 方法中的第一个函数参数
1.8.4 catch方法
<script>
function Cat(ready){
return new Promise(function(resolve,reject){
if(ready){
resolve("Tom");
}else{
reject("Kitty");
}
})
}
Cat(false).then(function(value1){
console.log(value1)
}).catch(function(){
console.log("此时执行的是then方法中的第二个函数参数");
});
</script>
catch 方法是 then(onFulfilled, onRejected) 方法当中 onRejected 函数的一个简单的写法,也就是说可以写成then(fn).catch(fn),相当于 then(fn).then(null, fn)。使用 catch 的写法比一般的写法更加清晰明确
1.8.5 all和race方法
console.time();
var p1 = new Promise(function(resolve) {
setTimeout(function() {
resolve("Hello");
}, 3000);
});
var p2 = new Promise(function(resolve) {
setTimeout(function() {
resolve("world");
}, 3000);
})
Promise.all([p1, p2]).then(function(result) {
console.log(result);
console.timeEnd();
});
上面的例子模拟了传输两个数据需要不同的时长,虽然 p2 的速度比 p1 要快,但是 Promise.all 方法会按照数组里面的顺序将结果返回
日常开发中经常会遇到这样的需求,在不同的接口请求数据然后拼合成自己所需的数据,通常这些接口之间没有关联(例如不需要前一个接口的数据作为后一个接口的参数),这个时候 Promise.all 方法就可以派上用场了
还有一个和 Promise.all 相类似的方法 Promise.race,它同样接收一个数组,不同的是只要该数组中的 Promise 对象的状态发生变化(无论是 resolve 还是 reject)该方法都会返回
1.9 fetch网络请求
与ajax 请求的对比见下面的代码:
<script>
// ajax 网络请求
var xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
}
// fetch 中的 get 请求
fetch(url).then(function(data) {
return data.json();
}).then(function(data) {
console.log(data);
}).catch(error){
console.log(error)
};
// fetch 中的 post 请求
fetch(url, {
method: "post",
mode: 'same-origin',
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: "a=1&b=2"
}).then(function(data) {
// return data.json();
}).then(function(data) {
// console.log(data);
})
</script>
如果有需要可以按照上面的方式进行编写
二、ES6之Module系统
2.1 为什么要进行模块化?
- 可维护性的需要
- 可测性的需要
- 性能的需要
- 架构的需求
- 代码复用
- 多人协作的需要
2.2 导入导出
小编这里只说步骤:
第一步:在你需要导出的 js 文件中某个方法或对象时,前面加上 export 关键字
第二步: 在另一个js文件中导入你要导出的js文件,import 对象/方法 from ×××.js
第三步: 在html中引入入口js 文件,就是第二步中的另一个js文件。
<script src=".js" type="module"><script>,这里的type属性一定要加上
三、总结
上面的ES6 知识并不是很全,还有其他的一部分,有心的小伙伴可以自己去学习!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。