ES6--JavaScript的第六个版本 javascript预编译的过程

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

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

如果对变量提高不怎么了解的话能够去参考个人其余文章   javascript预编译的过程 。html

什么是块级做用域腻?java

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

在es6以前,咱们想保护一个变量怎么办,将其放在一个当即执行函数里面,写在局部做用域中。react

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

在ES6中,咱们想保护一个变量 只要 写在花括号中就行了。面试

{chrome

  let   a=10;数组

}promise

下面有两个例子:

{
    let a = 20;
}

console.log(a);  // a is not defined
//写在块级做用域中的内容会被保护起来,因此会打印  a is not defined

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

{
let _a = 20;
}
console.log(a); // a is not defined


// ES5
console.log(a); // undefined
var a = 20;
//  在es5中var一个变量 会变量提高
//  如同
//  var a;
//  console.log(a); //从上向下顺序执行 固然会打印 undefined 的捏。
//  a=20


// ES6 
console.log(a); // a is not defined 
let a = 20;
//变量不会提高 打印时固然是 a is not defined

固然,你的代码编译成为了ES5以后,仍然会存在变量提高,所以这一点只须要咱们记住便可。

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

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

这里须要注意,正由于该值为一个引用,只须要保证引用不变就能够,咱们仍然能够改变该引用所指向的对象。当咱们试图改变const声明的变量时,则会报错。

写几个例子,你们能够仔细揣摩一下:

let a = null;
a = 20;
const obDev = {
    a: 20,
    b: 30
}

obDev.a = 30;

console.log(obDev); // Object {a: 30, b: 30}
const fn = function() {}
const a = obDev.a;
... ...

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

 

二,解构赋值

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

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

let options = {
    repeat: true,
    save: false
};
// 从对象中提取数据
let repeat = options.repeat,
save = options.save;

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

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

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

 对象解构:

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

let node = {
    type: "Identifier",
    name: "foo"
};
let { type, name } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
let node = {
    type: "Identifier",
    name: "foo"
},
type = "Literal",
name = 5;
// 使用解构来分配不一样的值
({ type, name } = node);
console.log(type); // "Identifier"
console.log(name); // "foo"

 

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

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

数组解构:

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

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

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

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

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

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

混合解构:

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

let node = {
    type: "Identifier",
    name: "foo",
    loc: {
        start: {
            line: 1,
            column: 1
        },
        end: {
            line: 1,
            column: 4
        }
    },
    range: [0, 3]
};
let {
    loc: { start },
    range: [ startIndex ]
} = node;
console.log(start.line); // 1
console.log(start.column); // 1
console.log(startIndex); // 0

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

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

 

3、 箭头函数的使用

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

首先是写法上的不一样:

// es5
var fn = function(a, b) {
    return a + b;
}

// es6 箭头函数写法,当函数直接被return时,能够省略函数体的括号
const fn = (a, b) => a + b;

// es5
var foo = function() {
    var a = 20var b = 30;
    return a + b;
}

// es6
const foo = () => {
   const a = 20;
   const b = 30;
   return a + b;
}

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

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

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

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;
    }
};

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

// 能够稍作改动
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);
    }
};

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

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

 

四,promise  (承诺)

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

在promise中,有三种状态:

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

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

  一、异步操做从pending到resolved;

  二、异步操做从pending到rejected;

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

 

恩,一目了然,Promise为构造函数,欧克,这样经过它,咱们就能够实例化本身的Promise对象了,并加以利用。 

Promise对象中的then方法

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

function fn(num) {
    return new Promise(function(resolve, reject) {
        if (typeof num == 'number') {
            resolve();
        } else {
            reject();
        }
    }).then(function() {
        console.log('参数是一个number值');
    }, function() {
        console.log('参数不是一个number值');
    })
}

fn('hahha');
fn(1234);

 

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

function fn(num) {
    return new Promise(function(resolve, reject) {
        if (typeof num == 'number') {
            resolve();
        } else {
            reject();
        }
    })
    .then(function() {
        console.log('参数是一个number值');
    })
    .then(null, function() {
        console.log('参数不是一个number值');
    })
}

fn('hahha');
fn(1234);

 

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

catch的用法

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

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

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此处的somedata未定义
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});
 
在resolve的回调中,咱们console.log(somedata);而somedata这个变量是没有被定义的。若是咱们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了。可是在这里,会获得这样的结果:
 
 
也就是说进到catch方法里面去了,并且把错误缘由传到了reason参数中。即使是有错误的代码也不会报错了,这与咱们的try/catch语句有相同的功能。

all的用法

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

race的用法

all方法的效果其实是「谁跑的慢,以谁为准执行回调」,那么相对的就有另外一个方法「谁跑的快,以谁为准执行回调」,这就是race方法,这个词原本就是赛跑的意思。race的用法与all同样,咱们把上面runAsync1的延时改成1秒来看一下:
Promise
.race([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});
这三个异步操做一样是并行执行的。结果你应该能够猜到,1秒后runAsync1已经执行完了,此时then里面的就执行了。结果是这样的:
相关文章
相关标签/搜索