一、新的变量声明方式 let/cons

与var不同,新的变量声明方式带来了一些不一样的特性,其中最重要的两个特性就是提供了块级作用域与不再具备变量提升。

若是对变量提升不怎么了解的话可以去参考我的其他文章   javascript预编译的过程 。

什么是块级作用域腻?

写在 “{}” 内的内容   都是块级作用域。

在es6之前,我们想保护一个变量怎么办,将其放在一个立即执行函数里面,写在局部作用域中。

这样写是不是挺麻烦的捏,多谢几个单词不花时间么?!

在ES6中,我们想保护一个变量 只要 写在花括号中就好了。

{

  let   a=10;

}

下面有两个例子:

  1. {
  2. let a = 20;
  3. }
  4.  
  5. console.log(a); // a is not defined
  6. //写在块级作用域中的内容会被保护起来,所以会打印 a is not defined

而这个简单的例子,会被编译为:

  1. {
  2. let _a = 20;
  3. }
  4. console.log(a); // a is not defined
  5.  
  6. // ES5
  7. console.log(a); // undefined
  8. var a = 20;
  9. //  在es5中var一个变量 会变量提升
  10. //  如同
  11. //  var a;
  12. //  console.log(a); //从上向下顺序执行 当然会打印 undefined 的捏。
  13. //  a=20
  14.  
  15. // ES6
  16. console.log(a); // a is not defined
  17. let a = 20;
  18. //变量不会提升 打印时当然是 a is not defined

当然,你的代码编译成为了ES5之后,仍然会存在变量提升,因此这一点只需要我们记住即可。

在实际使用中,也需要尽量避免使用变量提升的特性带来的负面影响。只有在面试题中,才会对变量提升不停的滥用。使用ES6,我们需要全面使用let/const替换var,那么什么时候用let,什么时候用const就成为了一个大家要熟练区分的一个知识点。

我们常常使用let来声明一个值会被改变的变量,而使用const来声明一个值不会被改变的变量,也可以称之为常量。当值为基础数据类型时,那么这里的值,就是指值本身。而当值对应的为引用数据类型时,那么我这里说的值,则表示指向该对象的引用。

这里需要注意,正因为该值为一个引用,只需要保证引用不变就可以,我们仍然可以改变该引用所指向的对象。当我们试图改变const声明的变量时,则会报错。

写几个例子,大家可以仔细揣摩一下:

  1. let a = null;
  2. a = 20;
  3. const obDev = {
  4. a: 20,
  5. b: 30
  6. }
  7.  
  8. obDev.a = 30;
  9.  
  10. console.log(obDev); // Object {a: 30, b: 30}
  11. const fn = function() {}
  12. const a = obDev.a;
  13. ... ...

只要抓住上面我说的特性,那么在使用let/const时就会显得游刃有余。根据我自己的经验,使用const的场景要比使用let的场景多很多。

二,解构赋值

我们经常定义许多对象和数组,然后有组织地从中提取相关的信息片段。在ES6中添加了可以简化这种任务的新特性:解构。解构是一种打破数据结构,将其拆分为更小部分的过程。

在ES5中,开发者们为了从对象和数组中获取特定数据并赋值给变量,编写了许多看起来同质化的代码

  1. let options = {
  2. repeat: true,
  3. save: false
  4. };
  5. // 从对象中提取数据
  6. let repeat = options.repeat,
  7. save = options.save;

  这段代码从options对象中提取repeat和save的值,并将其存储为同名局部变量,提取的过程极为相似

  如果要提取更多变量,则必须依次编写类似的代码来为变量赋值,如果其中还包含嵌套结构,只靠遍历是找不到真实信息的,必须要深入挖掘整个数据结构才能找到所需数据

  所以ES6添加了解构功能,将数据结构打散的过程变得更加简单,可以从打散后更小的部分中获取所需信息

对象解构:

对象字面量的语法形式是在一个赋值操作符左边放置一个对象字面量。

  1. let node = {
  2. type: "Identifier",
  3. name: "foo"
  4. };
  5. let { type, name } = node;
  6. console.log(type); // "Identifier"
  7. console.log(name); // "foo"
  1. let node = {
  2. type: "Identifier",
  3. name: "foo"
  4. },
  5. type = "Literal",
  6. name = 5;
  7. // 使用解构来分配不同的值
  8. ({ type, name } = node);
  9. console.log(type); // "Identifier"
  10. console.log(name); // "foo"

 

在这个示例中,声明变量type和name时初始化了一个值,在后面几行中,通过解构赋值的方法,从node对象读取相应的值重新为这两个变量赋值

  [注意]一定要用一对小括号包裹解构赋值语句,JS引擎将一对开放的花括号视为一个代码块。语法规定,代码块语句不允许出现在赋值语句左侧,添加小括号后可以将块语句转化为一个表达式,从而实现整个解构赋值过程。

数组解构:

与对象解构的语法相比,数组解构就简单多了,它使用的是数组字面量,且解构操作全部在数组内完成,而不是像对象字面量语法一样使用对象的命名属性

  1. let colors = [ "red", "green", "blue" ];
  2. let [ firstColor, secondColor ] = colors;
  3. console.log(firstColor); // "red"
  4. console.log(secondColor); // "green"

  在这段代码中,我们从colors数组中解构出了"red"和"green"这两个值,并分别存储在变量firstColor和变量secondColor中。在数组解构语法中,我们通过值在数组中的位置进行选取,且可以将其存储在任意变量中,未显式声明的元素都会直接被忽略

  在解构模式中,也可以直接省略元素,只为感兴趣的元素提供变量名

  1. let colors = [ "red", "green", "blue" ];
  2. let [ , , thirdColor ] = colors;
  3. console.log(thirdColor); // "blue"

  这段代码使用解构赋值语法从colors中获取第3个元素,thirdColor前的逗号是前方元素的占位符,无论数组中的元素有多少个,都可以通过这种方法提取想要的元素,不需要为每一个元素都指定变量名

混合解构:

  可以混合使用对象解构和数组解构来创建更多复杂的表达式,如此一来,可以从任何混杂着对象和数组的数据解构中提取想要的信息

  1. let node = {
  2. type: "Identifier",
  3. name: "foo",
  4. loc: {
  5. start: {
  6. line: 1,
  7. column: 1
  8. },
  9. end: {
  10. line: 1,
  11. column: 4
  12. }
  13. },
  14. range: [0, 3]
  15. };
  16. let {
  17. loc: { start },
  18. range: [ startIndex ]
  19. } = node;
  20. console.log(start.line); // 1
  21. console.log(start.column); // 1
  22. console.log(startIndex); // 0

  这段代码分别将node.loc.start和node.range[0]提取到变量start和startlndex中

  解构模式中的loc和range仅代表它们在node对象中所处的位置(也就是该对象的属性)。当使用混合解构的语法时,则可以从node提取任意想要的信息。这种方法极为有效,尤其是从JSON配置中提取信息时,不再需要遍历整个结构了

三、 箭头函数的使用

之前我说ES6颠覆了js的编码习惯,箭头函数的使用占了很大一部分。

首先是写法上的不同:

  1. // es5
  2. var fn = function(a, b) {
  3. return a + b;
  4. }
  5.  
  6. // es6 箭头函数写法,当函数直接被return时,可以省略函数体的括号
  7. const fn = (a, b) => a + b;
  8.  
  9. // es5
  10. var foo = function() {
  11. var a = 20
  12. var b = 30;
  13. return a + b;
  14. }
  15.  
  16. // es6
  17. const foo = () => {
  18. const a = 20;
  19. const b = 30;
  20. return a + b;
  21. }

箭头函数可以替换函数表达式,但是不能替换函数声明

其次还有一个至关重要的一点,那就是箭头函数中,没有this。如果你在箭头函数中使用了this,那么该this一定就是外层的this。

也正是因为箭头函数中没有this,因此我们也就无从谈起用call/apply/bind来改变this指向。记住这个特性,能让你在react组件之间传值时少走无数弯路。

  1. var person = {
  2. name: 'tom',
  3. getName: function() {
  4. return this.name;
  5. }
  6. }
  7.  
  8. // 我们试图用ES6的写法来重构上面的对象
  9. const person = {
  10. name: 'tom',
  11. getName: () => this.name
  12. }
  13.  
  14. // 但是编译结果却是
  15. var person = {
  16. name: 'tom',
  17. getName: function getName() {
  18. return undefined.name;
  19. }
  20. };

在ES6中,会默认采用严格模式,因此this也不会自动指向window对象了,而箭头函数本身并没有this,因此this就只能是undefined,这一点,在使用的时候,一定要慎重慎重再慎重,不然踩了坑你都不知道自己错在哪!这种情况,如果你还想用this,就不要用使用箭头函数的写法。

  1. // 可以稍做改动
  2. const person = {
  3. name: 'tom',
  4. getName: function() {
  5. return setTimeout(() => this.name, 1000);
  6. }
  7. }
  8.  
  9. // 编译之后变成
  10. var person = {
  11. name: 'tom',
  12. getName: function getName() {
  13. var _this = this; // 使用了我们在es5时常用的方式保存this引用
  14.  
  15. return setTimeout(function () {
  16. return _this.name;
  17. }, 1000);
  18. }
  19. };

先记住箭头函数的写法,并留意箭头函数中关于this的特殊性,更过实践与注意事项我们在封装react组件时再慢慢来感受。

还有就是  原函数中  arguments (实参) 在箭头函数中是是用不了的。

四,promise  (承诺)

他就是一个对象,主要是用来处理异步数据的。

在promise中,有三种状态:

pending(等待,处理中)   -->  1.resolve(完成)   2.rejected(失败,拒绝)。

又,这三种状态的变化只有两种模式,并且一旦状态改变,就不会再变:

  1、异步操作从pending到resolved;

  2、异步操作从pending到rejected;

好了,既然它是属于ES6规范,我们再通过chrome,直接打印出Promise,看看这玩意:

恩,一目了然,Promise为构造函数,欧克,这样通过它,我们就可以实例化自己的Promise对象了,并加以利用。 

Promise对象中的then方法

可以接收构造函数中处理的状态变化,并分别对应执行。then方法有2个参数,第一个函数接收resolved状态的执行,第二个参数接收reject状态的执行。

  1. function fn(num) {
  2. return new Promise(function(resolve, reject) {
  3. if (typeof num == 'number') {
  4. resolve();
  5. } else {
  6. reject();
  7. }
  8. }).then(function() {
  9. console.log('参数是一个number值');
  10. }, function() {
  11. console.log('参数不是一个number值');
  12. })
  13. }
  14.  
  15. fn('hahha');
  16. fn(1234);

then方法的执行结果也会返回一个Promise对象。因此我们可以进行then的链式执行,这也是解决回调地狱的主要方式。

  1. function fn(num) {
  2. return new Promise(function(resolve, reject) {
  3. if (typeof num == 'number') {
  4. resolve();
  5. } else {
  6. reject();
  7. }
  8. })
  9. .then(function() {
  10. console.log('参数是一个number值');
  11. })
  12. .then(null, function() {
  13. console.log('参数不是一个number值');
  14. })
  15. }
  16.  
  17. fn('hahha');
  18. fn(1234);

then(null, function() {}) 就等同于catch(function() {})

catch的用法

我们知道Promise对象除了then方法,还有一个catch方法,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调,用法是这样:
  1. getNumber()
  2. .then(function(data){
  3. console.log('resolved');
  4. console.log(data);
  5. })
  6. .catch(function(reason){
  7. console.log('rejected');
  8. console.log(reason);
  9. });

效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。请看下面的代码:

  1. getNumber()
  2. .then(function(data){
  3. console.log('resolved');
  4. console.log(data);
  5. console.log(somedata); //此处的somedata未定义
  6. })
  7. .catch(function(reason){
  8. console.log('rejected');
  9. console.log(reason);
  10. });
 
在resolve的回调中,我们console.log(somedata);而somedata这个变量是没有被定义的。如果我们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了。但是在这里,会得到这样的结果:
 
 
也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能。

all的用法

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。我们仍旧使用上面定义好的runAsync1、runAsync2、runAsync3这三个函数,看下面的例子:
 
  1. Promise
  2. .all([runAsync1(), runAsync2(), runAsync3()])
  3. .then(function(results){
  4. console.log(results);
  5. });
用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。所以上面代码的输出结果就是:
 
有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很酷?有一个场景是很适合用这个的,一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化。

race的用法

all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,这就是race方法,这个词本来就是赛跑的意思。race的用法与all一样,我们把上面runAsync1的延时改为1秒来看一下:
  1. Promise
  2. .race([runAsync1(), runAsync2(), runAsync3()])
  3. .then(function(results){
  4. console.log(results);
  5. });
这三个异步操作同样是并行执行的。结果你应该可以猜到,1秒后runAsync1已经执行完了,此时then里面的就执行了。结果是这样的:

ES6--JavaScript的第六个版本的更多相关文章

  1. JavaScript学习总结(六)——前端模块化开发

    早期的javascript版本没有块级作用域.没有类.没有包.也没有模块,这样会带来一些问题,如复用.依赖.冲突.代码组织混乱等,随着前端的膨胀,模块化显得非常迫切. 前端模块化规范如下: 一.前端模 ...

  2. Javascript多线程引擎(六)

    Javascript多线程引擎(六) 经过三个月的时间, Javascript 引擎已经完成beta版本(还不支持多线程特性, 预计下个星期就可以支持了, 现阶段还在进行测试基本JS单元功能), 并且 ...

  3. es6 javascript对象方法Object.assign()

    es6 javascript对象方法Object.assign() 2016年12月01日 16:42:34 阅读数:38583 1  基本用法 Object.assign方法用于对象的合并,将源对象 ...

  4. JavaScript获取浏览器类型与版本

    从网上找到一段使用JavaScript判断浏览器以及浏览器版本的比较好的代码,在此记录一下: <script type="text/javascript"> var S ...

  5. JavaScript进阶(十一)JsJava2.0版本

    JavaScript进阶(十一)JsJava2.0版本 2007年9月11日,JsJava团队发布了JsJava2.0版本,该版本不仅增加了许多新的类库,而且参照J2SE1.4,大量使用了类的继承和实 ...

  6. JavaScript学习总结(十八)——JavaScript获取浏览器类型与版本

    从网上找到一段使用JavaScript判断浏览器以及浏览器版本的比较好的代码,在此记录一下: 1 <script type="text/javascript"> 2 v ...

  7. ES6:JavaScript 新特性

    我相信,在ECMAScript.next到来的时候,我们现在每天都在写的JavaScript代码将会发生巨大的变化.接下来的一年将会是令JavaScript开发者们兴奋的一年,越来越多的特性提案将被最 ...

  8. javaScript事件(六)事件类型之滚轮事件

    滚轮事件其实就是一个mousewheel事件,这个事件跟踪鼠标滚轮,类似Mac的触屏版. 一.客户区坐标位置 鼠标事件都是在浏览器视口的特定位置上发生的.这个位置信息保存在事件对象的clientX和c ...

  9. How Javascript works (Javascript工作原理) (六) WebAssembly 对比 JavaScript 及其使用场景

    个人总结: 1.webassembly简介:WebAssembly是一种用于开发网络应用的高效,底层的字节码.允许在网络应用中使用除JavaScript的语言以外的语言(比如C,C++,Rust及其他 ...

  10. ES6学习笔记(六)数组的扩展

    1.扩展运算符 1.1含义 扩展运算符(spread)是三个点(...).它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列. console.log(...[1, 2, 3]) // ...

随机推荐

  1. flask框架--cookie,session

    今天我又给大家分享一下怎么用flask框架来实现像淘宝购物车一样存储数据,并且把存储的数据删除,这个方法可以用两个方法都可以做成,一个是cookie,另一个是session. session是依赖于c ...

  2. [视频]K8飞刀 ms15022 office漏洞演示动画

    [视频]K8飞刀 ms15022 office漏洞演示动画 https://pan.baidu.com/s/1eQnV8qQ

  3. AndroidStudio打包apk,安装出现签名冲突--解决办法

    Android UiAutomator2项目部署到jenkins上,实现自动打包,并自动push&安装到设备上 遇到问题: 可成功实现自动打包并push到设备上后,install -r 的时候 ...

  4. @RequestParam注解

    SpringMVC的参数指定注解:@RequestParam,有下面四个方法:   value 参数绑定,value里写的是URL里参数名称 name 同上 required 是否必需参数,默认为tr ...

  5. Apache本地配置虚拟域名

    转载+修改 例:虚拟域名为 aaa.com 端口为默认80 index.html所在目录  D:/wamp/www/web 不用解析域名,使用虚假的域名也可以 apache安装完默认是不开启虚拟服务器 ...

  6. 开发工具 -- Eclipse快捷键

    [ALT+/]此快捷键为用户编辑的好帮手,能为用户提供内容的辅助,不要为记不全方法和属性名称犯愁,当记不全类.方法和属性的名字时,多体验一下[ALT+/]快捷键带来的好处吧.   [Ctrl+O]显示 ...

  7. postgresql逻辑结构--用户及权限管理(七)

    一.用户和角色 二.创建用户和角色 三.权限管理 四.

  8. vue 项目其他规范

    列表 vuex数据管理 * 数据模块化:vuex数据管理-数据模块化 数据适配:vuex数据管理-数据适配 数据共享:vuex数据管理-数据共享 路由优化 keep-alive组件设置 保留滚动位置 ...

  9. 深入出不来nodejs源码-timer模块(JS篇)

    鸽了好久,最近沉迷游戏,继续写点什么吧,也不知道有没有人看. 其实这个node的源码也不知道该怎么写了,很多模块涉及的东西比较深,JS和C++两头看,中间被工作耽搁回来就一脸懵逼了,所以还是挑一些简单 ...

  10. 出现HTTP 错误 404.0 - Not Found的解决方法

    1.修改配置文件<system.webServer><modules runAllManagedModulesForAllRequests="true" /> ...