整理了ES6常用的一些语法,跟大家分享(promise、generator什么的还没有梳理清楚,后续再更新。。。)
1⃣️ 变量声明-let 与 const
(首先关于测试结果:这里有个小问题,如果用let/const声明的变量,在控制台调试的话,第一次输出结果后,第二次如果你还想用,要么把代码放入编辑器中,再打开浏览器看结果,要么就把变量名更改重新输入结果,否则是会报错的)
let与const的作用与var 类似,都是用来声明变量但是在实际应用中,它俩有各自的特殊用途
(注意:ES6里let和const没有变量的提升,一定要声明后再使用,但代码编译成ES5之后,变量提升依旧存在)
先举个栗子
var name = 'aaron';
if(true) {
var name = 'peter';
console.log(name); // peter
}
console.log(name); //peter
我们可以看到,使用var声明的变量,两次输出结果都是peter,那是因为ES5里只有全局作用域和函数作用域,没有块级作用域,那么我们怎么才能让它两次打印的结果分别是aaron 和 peter呢? 现在let就可以派上用场了
改造一下上面的栗子
let name = 'aaron';
if(true) {
let name = 'peter';
console.log(name); // peter
}
console.log(name); //aaron
现在可以看到,两次的结果已经不相同了,let实际上为
JavaScript新增了块级作用域。用它所声明的变量,只在let
命令所在的代码块内有效。
下面再来看一发关于关于for循环的栗子,问题就是用来计数的循环变量泄露为全局变量,会对我们的一些操作带来很大的影响,话不多说,来看栗子
var a = [];
for (var i = 0; i < 10; i++) { a[i] = function() { console.log(i) }; } a[6](); //10
原本我们的需求是 a[6](),结果可以输出6,但无论我们写的是a[i](),最终输出的结果都是10,因为for循环结束之后,i的值已经变成了10,而i又是全局变量,当函数之行的时候,首先在函数体内部没有i这么一个变量,所以它会去上一级作用域去寻找,本栗中它的上一级作用域就是全局作用域,所以也就找到了已经变为10的全局变量i,所以a[i]();无论你[]内写0~9哪个数字,最终输出结果都是10;
在没有ES6之前,如果我们想让它的输出就过就是6,就要使用到闭包(闭包这东西,个人的理解,用大白话说就是把你想要实现功能的方法,外面再给它包一个函数。内部return你要实现功能的方法,由于函数的作用域,这样可以避免变量泄露变成全局变量,从而带来一些我们不想看到的结果)
var a = []; function fn(i){ function inner() { console.log(i); } return inner; } for (var i = 0; i < 10; i++) { a[i] = fn (i); } a[6](); //6
讲真,这样很麻烦有没有?现在有了ES6 的let ,完全可以不用这么写了
var a = []; for (let i = 0; i < 10; i++) { a[i] = function() { console.log(i) }; } a[6](); //6
只是改了几个字母,var改成了let,已经实现了我们的需求,很方便有没有?!
const
也用来声明变量,但是声明的是常量。一旦声明,常量的值就不能改变。改变的话,浏览器会报错
栗子
const A = 1;
A = 2; // Uncaught TypeError: Assignment to constant variable.
针对const的这个特性,我们可以在引用第三方库的时,把需要声明的变量,用const来声明,这样可以避免未来不小心重命名而导致出现bug
const xxx = require('xxxxx');
注意:当值为基础数据类型时,那么这里的值,就是指值本身。
而当值对应的为引用数据类型时,那么这里的值,则表示指向该对象的引用。这里需要注意,正因为该值为一个引用,只需要保证引用不变就可以,我们仍然可以改变该引用所指向的对象。
栗子
const obj = {
a: 20,
b: 30
}
obj.a = 30;
obj.c = 40;
console.log(obj); // Object {a: 30, b: 30,c:40}
这种情况下只要不是直接覆盖obj的值,只改变属性什么的还是还可以
2⃣️模版字符串
使用 反引号``(键盘上esc下面那个键) 将整个字符串包裹起来,而在其中使用 ${} 来包裹一个变量或者一个表达式
// es5
var a = 20;
var b = 30;
var string = a + "+" + b + "=" + (a + b);
// es6
const a = 20;
const b = 30;
const string = `${a}+${b}=${a+b}`;
3⃣️解构(destructuring)赋值
数组以序列号一一对应,这是一个有序的对应关系。
对象根据属性名一一对应,这是一个无序的对应关系。
为了更好的理解,直接上栗子吧
数组的解构赋值
// es5
var arr = [1, 2, 3];
var a = arr[0];
var b = arr[1];
var c = arr[2];
// es6
const arr = [1, 2, 3];
const [a, b, c] = arr;
console.log(a,b,c) // 1,2,3
对象的解构赋值
const MYOBJ = {
className: 'trigger-button',
loading: false,
clicked: true,
disabled: 'disabled'
}
现在我们想要取得其中的2个值:loading与clicked:
// es5
var loading = MYOBJ.loading;
var clicked = MYOBJ.clicked;
// es6
const { loading, clicked } = MYOBJ;
console.log(loading);// false
// 还可以给一个默认值,当props对象中找不到loading时,loading就等于该默认值
const { loadings = false, clicked } = MYOBJ;
console.log(loadings);// false
4⃣️展开运算符(spread operater)
在ES6中用...
来表示展开运算符,它可以将数组方法或者对象进行展开。上栗子
1.函数调用中使用展开运算符
函数调用里,将一个数组展开成多个参数,我们会用到apply:
function test(a, b, c) { }
var args = [0, 1, 2];
test.apply(null, args);
在ES6里可以这样写
function test(a,b,c) { }
var args = [0,1,2];
test(...args);
2.数组字面量中使用展开运算符
有了ES6,我们可以直接加一个数组直接合并到另外一个数组当中
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 10, 20, 30];
// 这样,arr2 就变成了[1, 2, 3, 10, 20, 30];
展开运算符也可以用在push函数中,可以不用再用apply()函数来合并两个数组:
var arr1=['a','b','c'];
var arr2=['d','e'];
arr1.push(...arr2); //['a','b','c','d','e']
3.用于解构赋值
let [arg1,arg2,...arg3] = [1, 2, 3, 4];
arg1 //1
arg2 //2
arg3 //['3','4']
注意:解构赋值中展开运算符只能用在最后,否则会报错
4.展开运算符可以将伪数组变成真正的数组
var list=document.querySelectorAll('div');
var arr=[..list];
Object.prototype.toString.apply(list) // "[object NodeList]"
Object.prototype.toString.apply(arr) // "[object Array]"
关于对象展开
好像目前ES6还不支持这样,现在这样写浏览器会报错,ES7草案里貌似有提到,所以对象展开这里先了解一下就好了
const obj1 = { a: 1, b: 2, c: 3 } const obj2 = { ...obj1, d: 4, e: 5, f: 6 } // 结果类似于 const obj2 = Object.assign({}, obj1, {d: 4,e:5,f:6})
扩展:Object.assign()
方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。 语法:Object.assign(target, 一个或多个sources)
5⃣️ arrow function箭头函数
函数的快捷写法,不需要通过 function
关键字创建函数,并且还可以省略 return
关键字。(注意:箭头函数本身没有this,如果在箭头函数内使用this,这个this一定是它上级的this,再有就是箭头函数可以代替函数表达式,但代替不了函数声明,它还是需要声明才能使用的)。
(parma)=>{expression},箭头函数根据parma个数的不同,写法上还可以做如下改变
() => { expression } // 零个参数用 () 表示
x => { expression } // 一个参数可以省略 ()
(x, y) => { expression } // 多参数不能省略 ()
注意: 在ES6中,会默认采用严格模式,因此this也不会自动指向window对象了,而箭头函数本身并没有this,因此this就只能是undefined,这一点,在使用的时候,一定要慎重慎重再慎重,不然踩了坑你都不知道自己错在哪!这种情况,如果你还想用this,就不要用使用箭头函数的写法。
栗子
var person = {
name: 'tom',
getName: function() {
return this.name;
}
}
// 用ES6的写法来重构上面的对象
const person = {
name: 'tom',
getName: () => this.name
}
// 但是编译结果却是
var person = {
name: 'tom',
getName: function getName() {
return undefined.name;
}
};
对上面的代码稍作改动
const person = {
name: 'tom',
getName: function() {
return setTimeout(() => this.name, 1000);
}
}
// 编译之后变成
var person = {
name: 'tom',
getName: function getName() {
var _this = this; // 使用了我们在es5时常用的方式保存this引用
return setTimeout(function () {
return _this.name;
}, 1000);
}
};
6⃣️函数参数的默认值
之前我们想要保证传入函数的参数有一个默认值,通常需要这么写
function add(x) {
var x = x || 20;
return x;
}
console.log(add()); // 20
但这种方法是有缺陷的,比如说我们如果传入一个false
function add(x) {
var x = x || 20;
return x;
}
console.log(add(false)); // 20
打印结果是20 而不是fasle,显然合格结果不是我们想要的,如果我们想要打印出false,就还要再做什么if判断等一些列操作,很麻烦,现在有了es6,我们可以很容易解决这个问题,下面我们来看一下es6的写法
function add(x = 20) {
return x ;
}
console.log(add());// 20
console.log(add(false)); // false
可以看到,es6很容易就解决了这个问题。
7⃣️对象字面量({})扩展
ES6针对对象字面量做了许多简化语法的处理
1)精简属性:
const name = 'Jane';
const age = 20
// es6写法
const person = {
name,
age
}
// es5写法
var person = {
name: name,
age: age
};
2)精简方法:
// es6写法
const person = {
name,
age,
getName() { // 只要不使用箭头函数,this就还是我们熟悉的this
return this.name
}
}
// es5写法
var person = {
name: name,
age: age,
getName: function getName() {
return this.name;
}
};
3)属性名表达式:(这里有点儿恶心,经过几次代码测试,最终确定下面这样解释的话,可能会容易理解一些)
在对象字面量中可以使用中括号作为属性,表示属性也能是一个变量了,而且这个变量的值还可以改变
const name = 'Jane';
const person = {
[name]: true,
['a'+'ge']: true
}
注意:上面的对象{}内写了两个[]属性 ,切记[]里面如果是一个变量的话,那么这个变量一定要是一个已经声明过的,否则结果就是undefined,[]如果是表达式,那么访问的时候,要向下面这样写
console.log(person['a'+'ge'])/console.log(person['age'])/console.log(oerson.age) // true
console.log(person[name]);// true
注意:对象内用[变量]当作属性时,访问该属性只能用[]语法,对象内给这个属性赋什么值,它就会变成什么值,而且这个值也可以通过[]里面传入访问变量最初设置的值,也可以访问到,写就是说像下面这样写 和上面结果是一样的
console.log(person['Jane']); // true
对象的方法也可以这样写
let obj = {
['h'+'ello']() {
return 'hi';
}
};
console.log(obj.hello()); // hi
8⃣️class、extend、super
class、extend、super这三个特性涉及了ES5中最令人头疼的的几个部分:构造函数、继承,原型...
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念。新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
新旧语法对比
class
// ES5
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 原型方法
Person.prototype.getName = function() {
return this.name
}
// ES6
class Person {
constructor(name, age) { // 构造函数
this.name = name;
this.age = age;
}
getName() { // 原型方法
return this.name
}
}
上面代码首先用class
定义了一个“类”,可以看到里面有一个constructor
方法,这就是构造方法,而this
关键字则代表实例对象。
简单地说,constructor
内定义的方法和属性是实例对象自己的,而constructor
外定义的方法和属性则是所有实例对象可以共享的。这个和ES5里的构造函数是差不多的意思,相当于把方法定义在构造函数里是私有的,而把方法定义到原型中,所有实例共享
extend继承
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
return this.name
}
}
// Student类继承Person类
class Student extends Person {
constructor(name, age, gender, classes) {
super(name, age);
this.gender = gender;
this.classes = classes;
}
getGender() {
return this.gender;
}
}
Class之间可以通过extends
关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个Cat类,该类通过extends
关键字,继承了Animal类的所有属性和方法。
super
关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor
方法中调用super
方法,否则新建实例时会报错。这是因为子类没有自己的this
对象,而是继承父类的this
对象,然后可以对其进行加工。如果不调用super
方法,子类就得不到this
对象。
关于 super,像上面的栗子
// 构造函数中
// es5写法
Person.call(this);
// es6写法
super(name, age);
class、extend/super三者的综合实栗
class Animal {
constructor(){
this.type = 'animal'
}
says(say){
console.log(this.type + ' says ' + say)
}
}
let animal = new Animal()
animal.says('hello') //animal says hello
class Cat extends Animal {
constructor(){
super()
this.type = 'cat'
}
}
let cat = new Cat()
cat.says('hello') //cat says hello
9⃣️模块的 Import 和 Export
import
用于引入模块,export
用于导出模块。
// 引入整个文件
import dva from 'dva';
// 引入函数(可以是一个或多个)
import { connect } from 'dva';
import { Link, Route } from 'dva/router';
// 引入全部并作为 github 对象
import * as github from './services/github';
// 导出默认
export default App;
// 部分导出,复合写法是 export { App } from './file';
等价于import { App } from './file;export{App}