前端相关

ES6 一些重要概念

2017-10-22  本文已影响9人  DeeJay_Y

一,let/const的理解

方应航:我用了两个月的时间才理解 let

let 添加了for if等块级作用域

通过例子来分析:

for (var i = 0; i < 10; i ++) {
    console.log(i)
}
console.log(i) //10

对于var来说,js只有函数作用域,所以上述for循环中的i就是全局的i,输出为10

for循环中换为let声明后:

for (let i = 0; i < 10; i ++) {
    console.log(i)
}
console.log(i) //此时i为undefined

let将for的{}也视为一个作用域,外部拿不到这个i,为undefined

那么使用let之后具体内部的实现通过babel编译后为:

for (var _i = 0; _i < 10; _i ++) {
    console.log(_i)
}
console.log(i)

可以看到是通过替换变量名称实现的这个效果。

同样的,对于if判断,lt也实现了对应的块级作用域

一样,先看常规var声明的例子:

function fn () {
    var n = 0
    if (true) {
        var n = 1
    }
    console.log(n) //1
}
fn()

由于if没有块级作用域,所以输出的是n为1

那么将var替换为let之后的例子:

function fn () {
    let n = 0
    if (true) {
        let n = 1
    }
    console.log(n) // 0
}
fn()

使用let之后,if中的n就变为if块级作用域中的局部n,所以输出的n还是fn函数的n,不是if中的n

通过babel编译之后的代码为:

function fn () {
    var n = 0 // 这里的let编译成var 因为es5本身函数就是作用域
    if (true) {
        var _n = 1 //这里的let就是通过替换变量生成了if的作用域
    }
    console.log(n) // 0
}
fn()

这样就很能理解let到底做了什么事

const 定义常量

const指令定义一个常量

const b = 0;

// b = 1; //报错 常量不能再进行改写 为read-only

const定义对象

const c = {
    a: 1
}
c.a = 2 // 不报错 因为JS中的对象仅仅是个指针,c中存储的地址并没有改变
c = {
    a: 2
} // 此时就会报错 因为地址变为了另外一个对象的地址

二,解构赋值 (Destructing)

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值。

基本用法

let a = 1,
    b = 2,
    c = 3
//编译为
var a = 1,
    b = 2,
    c = 3

上述例子使用解构赋值:

let [a, b, c] = [1, 2, 3]
//编译为
var a = 1,
    b = 2,
    c = 3

对于对象的解构赋值

let { d,e } = { d: 1,e: 2 }
//编译为
var _d$e = { d: 1,e: 2 },
    d = _d$e.d,
    e = _d$e.e;

所以对象的解构赋值是按照这个key的名字来进行的。

进行深度解构:

let obj = {
    p: [
        'Hello',
        {y: 'World'}
    ]
};
let { p: [x, { y }] } = obj;
// 编译为
var obj = {
    p: ['Hello', { y: 'World' }]
};
var _obj$p = _slicedToArray(obj.p,2),
    x = _obj$p[0],
    y = _obj$p[1].y;

先克隆这个数据结构,按照数据结构进行层级展开,进行赋值。

解构赋值的应用

//解构赋值的应用

function add ([x,y]) {
    return x + y;
}
add([1,2]);  //3
// 编译为
function add(_ref) {
    var _ref2 = _slicedToArray(_ref, 2),
        x = _ref2[0],
        y = _ref2[1];

    return x + y;
}
add([1, 2]); //3

在ES6中,我们可以通过解构赋值,进行function ([x,y])的传参,如果是ES5,那么只能写成function(x,y)

字符串的解构赋值

// 字符串的解构赋值
const [a,b,c,d,e] = 'hello';
 -----------------------------
var _hello = 'hello',
    _hello2 = _slicedToArray(_hello, 5),
    a = _hello2[0],
    b = _hello2[1],
    c = _hello2[2],
    d = _hello2[3],
    e = _hello2[4];

rest解构

const {p,...rest } = {p: 1,a: 2,c: 3}
---------------------------------------

    var _p$a$c = { p: 1, a: 2, c: 3 },
    p = _p$a$c.p,
    rest = _objectWithoutProperties(_p$a$c, ["p"]);

三,函数扩展

函数参数的默认值

function log (x,y) {
    x = x ? x : true; //x没有就拿true
}

上述函数中设置默认值为true的方法

下面例子就是设置参数y的默认值为 'world'的方法

function log (x, y = 'world') { // 这就实现了 y=y?y:'world'
    console.log(x, y)
}
-----------------------
function log(x) {
    var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'world';

    console.log(x, y);
}

rest参数

ES6中引入了rest参数,用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

举个求和函数的例子

function add (...values) {
    let sum = 0;
    for (var val of values) {
        sum += val
    }
    return sum;
}
add (2,5,3);

通过一个简单的例子来理解values:

function add (...values) {
    let sum = 0;
    console.log(values)
}
add (2,5,3);
-------------------------------------
function add() { // 这边是没有传参的,而是通过下面的for循环遍历arguments得到的values
    var sum = 0;

    for (var _len = arguments.length, values = Array(_len), _key = 0; _key < _len; _key++) {
        values[_key] = arguments[_key];
    }

    console.log(values);
}
add(2, 5, 3);

...values前面还有参数的情况

function add (a,...values) {
    console.log(values)
}
add (2,5,3); //[5,3]
-------------------------------------------
function add(a) { //前面写定参数的话,那么参数就传入
    for (var _len = arguments.length, values = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {   //此时的_key序数号为1
        values[_key - 1] = arguments[_key];
    }

    console.log(values);
}
add(2, 5, 3);

其他更多传入的参数同理。

扩展运算符

扩展运算符就是三个点(...),好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列

箭头函数

ES6允许使用箭头 =>定义函数

var f = v => v;
------------------------
var f = function f(v) {
  return v;
};
var f1 = v => {
    return v
}
-------------------------
var f1 = function f1(v) {
    return v;
};

箭头函数中的注意点

var f1 = () => {
    console.log(this) //undefined
}
var f2 = function () {
    console.log(this)  //window
}
//es6和es5的函数比较
---------------------------------------
var f1 = function f1() {
    console.log(undefined); //undefined
};
var f2 = function f2() {
    console.log(this); //window
};

这里的箭头函数中的this指向的是当前作用域所在的上下文的this。再看一个例子

var f3 = function () {
    return () => {
        console.log(this)
    }
}
-----------------------------
var f3 = function f3() {
    var _this = this;

    return function () {
        console.log(_this);
    };
};

从这个例子分析,当前箭头函数的作用域为自身的这个{},这个{}所在的上下文即为f3函数的作用域的,所以这个箭头函数的this为f3中的this,即上一级的this

绑定this

箭头函数可以绑定this对象,大大减少了显式绑定this对象的写法(call、apply、bind)。
函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

Promise对象

let p = new Promise( (resolve,reject) => {
    resolve(1)
})
console.log(p)
// p ===> fulfilled 1
// Promise的状态 fulfilled pending rejected
// Promise的值  有值 一直为空 有值
let p = new Promise( (resolve,reject) => {
    resolve(1)
})
console.log(p)
// // p ===> fulfilled 1
// // Promise的状态 fulfilled pending rejected
// // Promise的值  有值 一直为空 有值

let p1 = p.then(val => { //如果是then 那么这个函数作为resolve进行执行
    console.log(val) //1
})
let p2 = p.then(val => {
    console.log(val) //1  再调用一次 值还是1 只要状态不是pending时,这个状态就永久的保持并且不会改变了
//    所以再次调用也不会改变p,只是将其值输出
})

只要p的状态不是pending,而是确定有值了,这边p无论调用多少次,都不会改变。

let p = new Promise( (resolve,reject) => {
    // resolve(1);
    reject(2);
})
let p3 = p.catch(val => { //catch时,作为reject执行
    console.log(val) //2
})
let p = new Promise((resolve,reject) => {
    resolve(1)
})
let p1 = p.then(val => { //如果then里面的回调函数return出来一个值,那么这个值作为p1的promise value,并且状态变为fulfilled
    return 2
//    
})

如果then里面的回调函数return出来一个值,那么这个值作为p1的值,并且状态变为fulfilled。如果在回调中不写return,那么就把函数默认return 的undefined作为p1的值。

Iterator(遍历器)

Iterator的作用:


// const s = (...rest) => {
//     for(let val of rest) { // 可以遍历数组
//         console.log(val)
//     }
// }
// s(1,2,3,4)

// const s = function() {
//     for(let val of arguments) {  //可以遍历 类数组对象
//         console.log(val)
//     }
// }
// s(1,2,3,4)

// const s = {
//     a: 1,
//     b: 2
// }
// for (let val of s) { //报错
//     console.log(val)
// }

class

class Test {
    constructor () {
        this.a = 'a';
        this.b = 'b'
    }
    c () {
        console.log('C')
    }
}
--------------------------------
var Test = function () {
    function Test() {
        _classCallCheck(this, Test);

        this.a = 'a';
        this.b = 'b';
    }

    _createClass(Test, [{
        key: 'c',
        value: function c() {
            console.log('C');
        }
    }]);

    return Test;
}();

上述例子,es5的写法为:

//es5的写法为:
var Test = function () {
    this.a = 'a';
    this.b = 'b';
}
Test.prototype.c = function () {
    console.log('c');
}

module

先将export import关键字处理为require函数(nodejs),在浏览器环境下没有require,只能通过webpack来进行处理require函数

Generator (原则上在Nodejs中使用)

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)

function* helloWorldGenerator () {
    yield 'hello'; // 碰到yield会停止执行,需要手动next进行执行
    yield 'world';
    return 'ending'
}
var hw = helloWorldGenerator()
let a = hw.next();
let b = hw.next();
let c = hw.next();
console.log(a,b,c)
//{ value: 'hello', done: false } //done 为false代表还可以往下next()
// { value: 'world', done: false }
// { value: 'ending', done: true }
上一篇下一篇

猜你喜欢

热点阅读