es6 兼容性 如何使用babel来编译我们的js(es6)代码
首先来谈一下es6 es6在大多数情况是没问题,据了解国内的环境,大部分业务(银行系统除外)支持到IE8就可以了,如果要兼容到IE6+,可以借助babel插件来完成,只要babel支持的都没问题的。
ES6新特性在Babel下的兼容性列表
ES6特性 | 兼容性 |
---|---|
箭头函数 | 支持 |
类的声明和继承 | 部分支持,IE8不支持 |
增强的对象字面量 | 支持 |
字符串模板 | 支持 |
解构 | 支持,但注意使用方式 |
参数默认值,不定参数,拓展参数 | 支持 |
let与const | 支持 |
for of | IE不支持 |
iterator, generator | 不支持 |
模块 module、Proxies、Symbol | 不支持 |
Map,Set 和 WeakMap,WeakSet | 不支持 |
Promises、Math,Number,String,Object 的新API | 不支持 |
export & import | 支持 |
生成器函数 | 不支持 |
数组拷贝 | 支持 |
那么我们来说一下如何使用这个babel来兼容我们的es6 代码(当然前提是babeljs文件已经存在在项目中,这里只是说明如何在webpack中让babel编译我们的js或者是jsx文件)
上一小小段代码:
module: { loaders: [ // { test: /\.(jsx|js)$/, loader: ['babel-loader'], exclude: /node_modules/ } ] }复制代码
一般处理我们的打包文件,我们都会放在module下的loader里去处理,上段代码中将以jsx/js为结尾的文件都会做一个babel来编译。
es6好用的新特性
1.var let const
在之前的js的版本中,我们多会选择用var来声明定义一个变量,这么做的弊端会造成浪费和占用了大量内存,在新的js版本es6中出现了两个新的用来声明标识符的方式:let 和 const
其实很好理解let其实定义了一个拥有着自己代码块的变量,所谓代码块其实就是当你在一个{}中使用let 定义一个变量后,let只在{}中存在,但是与js的函数作用域不同的是let定义的变量不会被提升。
var a = 1;function fn1(){ console.log(a); a = 2;}console.log(a);//1fn1(); // 2var b = 2;function fn2(){ console.log(b); let b = 1;}console.log(b)//2fn2(); //复制代码
从这个例子中我们可以看到分别打印a,b变量 用let改的值在出了函数{}后失去了作用,而不是用let的赋值在出了{}后依然生效。
再举一个好用的应用场景,我们在一些特定需求时,会避免不了使用闭包,不了解闭包请移步跨域 闭包 ,例如一个常见的烂大街的问题,一个ul里的li打印它的索引或者内容。在es5中我们是这样做的:
var arr = [];for(var i=0; i<5; i++){ arr.push((function (a){ return function (){ console.log(a); } })(i))}arr[1]() // 1arr[2]() // 2arr[3]() // 3复制代码
闭包的缺点时会占用内存不是放掉,那么我们来换一种写法:
var arr = []; // let arr = [] 都可以for(let i=0; i<5; i++){ arr.push(function (){ console.log(i) })}arr[0]() // 0arr[1]() // 1arr[2]() // 2复制代码
使用let会完美的解决这个问题。
再说说const就很好理解了,它就是定义一个常量的声明方式,拥有其他语言基础这个概念就不难理解,再次就不在做多余赘述。2.解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
let [a, b, c] = [1, 2, 3];//等同于let a = 1;let b = 2;let c = 3;复制代码
对象的解构赋值:获取对象的多个属性并且使用一条语句将它们赋给多个变量。
var { StyleSheet, Text, View} = React;等同于var StyleSheet = React.StyleSheet;var Text = React.Text;var View = React.View;复制代码
3.箭头函数
ES6中新增箭头操作符用于简化函数的写法,操作符左边为参数,右边为具体操作和返回值。
var sum = (num1, num2) => { return num1 + num2; }//等同于var sum = function(num1, num2) { return num1 + num2; };复制代码
同时箭头函数还有修复了this的指向,使其永远指向词法作用域:
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象 return fn(); }};obj.getAge(); // 25复制代码
4.类 class
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类,与多数传统语言类似。
//定义类class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; }}复制代码
同样的继承使用的是extends关键字
5.字符串
新增字符串处理函数:
startsWith() //检查是否以指定字符串开头,返回布尔值let str = 'javascript';str.startsWith('java'); endsWith() //检查是否以指定字符串结尾,返回布尔值let str = 'javascript'str.endsWith('pt');includes() //检查字符串是否包含指定字符串 ,返回布尔值let str = 'javascript';str.includes('j');repeat() //指定字符串重复次数let str = 'javascript';str.repeat('3');复制代码
Template Literals 字符串模板
使用 字符串模板字面量,我可以在字符串中直接使用特殊字符,而不用转义。var text = "This string contains \"double quotes\" which are escaped.";let text = `This string contains "double quotes" which don't need to be escaped anymore.`;复制代码
字符串模板字面量 还支持直接插入变量,可以实现字符串与变量的直接连接输出.
var name = 'Tiger';var age = 13;console.log('My cat is named ' + name + ' and is ' + age + ' years old.');更简单的版本:const name = 'Tiger';const age = 13;console.log(`My cat is named ${name} and is ${age} years old.`);复制代码
ES5中,我们要这样生成多行文本:
var text = ( 'cat\n' + 'dog\n' + 'nickelodeon');或者:var text = [ 'cat', 'dog', 'nickelodeon'].join('\n');复制代码
字符串模板字面量 让我们不必特别关注多行字符串中的换行转义符号,直接换行即可:
let text = ( `catdognickelodeon`);复制代码
字符串模板字面量 内部可以使用表达式,像这样:
let today = new Date();let text = `The time and date is ${today.toLocaleString()};复制代码
6.第七种基本类型 Symbol
Symbols和其它基本类型大不一样。
从创建开始就是不可变的。你不能为它设置属性(如果你在严谨模式下尝试,会报类型错误)。它可以作为属性名。这是它的类字符串性质。另一方面,每一个symbol都是唯一的。与其他的不同(就算他们的描述是一样的)你可以很容易地新创建一个。这是它的类对象性质。ES6 symbols与Lisp和Ruby中的更传统的symbols很类似,但是没有如此紧密地集成到语言中。在Lisp中,所有的标识符都是symbols。在JS中,标识符和大多数属性的键值的首先仍是字符串,Symbols只是为开发人员提供了一个额外选择。关于symbols的一个忠告:与JS中的其它类型不同,它不能被自动转换为字符串。试图拼接symbol与字符串将会引起类型错误。(表示读过文档后依然蒙圈 先选择性放弃这个点)
7.Promise
一个 Promise 对象可以理解为一次将要执行的操作(常常被用于异步操作),使用了 Promise 对象之后可以用一种链式调用的方式来组织代码,让代码更加直观。而且由于 Promise.all 这样的方法存在,可以让同时执行多个操作变得简单。接下来就来简单介绍 Promise 对象。
直观些直接上代码:
function helloWorld (ready) { return new Promise(function (resolve, reject) { if (ready) { resolve("Hello World!"); } else { reject("Good bye!"); } });}helloWorld(true).then(function (message) { alert(message);}, function (error) { alert(error);});复制代码
上面的代码实现的功能非常简单,helloWord 函数接受一个参数,如果为 true 就打印 "Hello World!",如果为 false 就打印错误的信息。helloWord 函数返回的是一个 Promise 对象。
在 Promise 对象当中有两个重要方法————resolve 和 reject。resolve 方法可以使 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操作,在这个例子当中就是 Hello World!字符串。reject 方法则是将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操作。Promise 的三种状态
上面提到了 resolve 和 reject 可以改变 Promise 对象的状态,那么它究竟有哪些状态呢?Promise 对象有三种状态:- Fulfilled 可以理解为成功的状态
- Rejected 可以理解为失败的状态
- Pending 既不是 Fulfilld 也不是 Rejected 的状态,可以理解为 Promise 对象实例创建时候的初始状态helloWorld 的例子中的 then 方法就是根据 Promise 对象的状态来确定执行的操作,resolve 时执行第一个函数(onFulfilled),reject 时执行第二个函数(onRejected)。then 和 catchthenhelloWorld 的例子当中利用了 then(onFulfilld, onRejected) 方法来执行一个任务打印 "Hello World!",在多个任务的情况下 then 方法同样可以用一个清晰的方式完成。
function printHello (ready) {return new Promise(function (resolve, reject) { if (ready) { resolve("Hello"); } else { reject("Good bye!"); }});}复制代码
上述例子通过链式调用的方式,按顺序打印出了相应的内容。then 可以使用链式调用的写法原因在于,每一次执行该方法时总是会返回一个 Promise 对象。另外,在 then onFulfilled 的函数当中的返回值,可以作为后续操作的参数,因此上面的例子也可以写成:
printHello(true).then(function (message) { return message;}).then(function (message) { return message + ' World';}).then(function (message) { return message + '!';}).then(function (message) { alert(message);});function printWorld () { alert("World");}function printExclamation () { alert("!");}printHello(true) .then(function(message){ alert(message); }) .then(printWorld) .then(printExclamation);复制代码
同样可以打印出正确的内容。
catchcatch 方法是 then(onFulfilled, onRejected) 方法当中 onRejected 函数的一个简单的写法,也就是说可以写成 then(fn).catch(fn),相当于 then(fn).then(null, fn)。使用 catch 的写法比一般的写法更加清晰明确。Promise.all 和 Promise.racePromise.all 可以接收一个元素为 Promise 对象的数组作为参数,当这个数组里面所有的 Promise 对象都变为 resolve 时,该方法才会返回。var p1 = new Promise(function (resolve) { setTimeout(function () { resolve("Hello"); }, 3000);});var p2 = new Promise(function (resolve) { setTimeout(function () { resolve("World"); }, 1000);});Promise.all([p1, p2]).then(function (result) { console.log(result); // ["Hello", "World"]});复制代码
上面的例子模拟了传输两个数据需要不同的时长,虽然 p2 的速度比 p1 要快,但是 Promise.all 方法会按照数组里面的顺序将结果返回。
日常开发中经常会遇到这样的需求,在不同的接口请求数据然后拼合成自己所需的数据,通常这些接口之间没有关联(例如不需要前一个接口的数据作为后一个接口的参数),这个时候 Promise.all 方法就可以派上用场了。还有一个和 Promise.all 相类似的方法 Promise.race,它同样接收一个数组,不同的是只要该数组中的 Promise 对象的状态发生变化(无论是 resolve 还是 reject)该方法都会返回。兼容性最后是关于 Promise 对象的兼容性问题。在浏览器端,一些主流的浏览器都已经可以使用 Promise 对象进行开发,在 Node.js 配合 babel 也可以很方便地使用。
如果要兼容旧的浏览器,建议可以寻找一些第三方的解决方案,例如 jQuery 的 $.Deferred。