ES6扫盲

时间:2023-03-09 08:10:43
ES6扫盲

原文阅读请点击此处

一、let和const

ES6扫盲
{
    // let声明的变量只在let命令所在的代码块内有效
    let a = 1;
    var b = 2;
}

console.log(a);     // 报错: ReferenceError: a is not defined
console.log(b);
ES6扫盲
// for循环的技术器就很适合let命令
for (let i = 0; i < 3; i++) {
    console.log(i);
}

console.log(i); // ReferenceError: i is not defined
ES6扫盲
// 这里的i是var声明的,在全局范围内有效,素偶一每一次循环,新的i值都会覆盖旧值,导致最后输出的是最后一轮的i值
for (var i = 0; i < 10; i++) {
    a[i] = function() {
        console.log(i);
    }
}

a[6](); // 10

var b = [];

// 使用let声明的变量尽在块级作用域内有效,所以每一次循环的j其实都是一个新的变量,于是最后输出6
for (let j = 0; j < 10; j++) {
    a[j] = function() {
        console.log(j);
    }
}

b[6]();
ES6扫盲
ES6扫盲
// let不像var那样会发生"变量"提升的现象
// 但是经过babel转换器转换之后,还是存在变量提升的现象
// ES6明确规定,如果区块中存在let和const命令,则这个区块中对这些命令声明的变量从一开始就形成封闭作用域.只要在声明这些变量之前就使用这些变量,就会报错
{
    console.log(foo);   // ReferenceError
    let foo = 2;
}

// 块级作用域
{
    // 块级作用域的出现使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了
    // IIFE写法
    (function() {
        var tmp = 'a';
        // ...
    })();

    // 块级作用域写法
    {
        let tmp = 'a';
        // ...
    }

    // 因此,我们可以使用块级作用域来划分业务模块,以及避免全局变量

}

{
    let a = 'secret';

    function f() {
        return a;
    }
}

f();    // 报错
ES6扫盲
// const声明的常量不得改变值
// const一旦声明常量,就必须立即初始化,不能留到以后赋值
// const的作用域与let命令相同:只在声明所在的块级作用域内有效
// const命令声明的变量也不提升,只能声明后使用
const foo = 'AAA';
foo = 'BBB';    // 编译不通过
ES6扫盲
{
    // 跨模块常量
    // constants.js
    //export const A = 1;
    //export const B = 2;
    //export const C = 3;

    // test1.js模块
    //import * as constants from 'constants';

}
ES6扫盲
// 全局对象的属性
var a = 1;
console.log(window.a);  // 1

let b = 2;
console.log(window.b);  // undefined

二、字符串

ES6扫盲
{
    // 使用for of循环字符串
    for (let c of 'foo') {
        console.log(c);
    }

    let s = 'Hello world!';

    // 使用字符串的startsWidth/endsWidth/includes方法
    console.log(s.startsWith('Hello')); // true
    console.log(s.endsWith('!'));   // true
    console.log(s.includes('e'));   // true

    // 这三个方法都支持第二个参数,表示开始搜索的位置
    s.startsWith('world', 6);   // true

    let person = {
        name: 'king',
        age: 20
    };

    // 模板字符串
    // 所有的空格和缩进都会被保留在输出中
    let str = (`
        The name is ${person.name}.
        The age is ${person.age}.
    `);

    console.log(str);

}
ES6扫盲

三、函数

ES6扫盲
// 函数参数的默认值
function log(x, y = 'world') {
    console.log(x, y);
}

log('hello');   // 可以省略尾部参数的默认值

function f(x = 1, y) {
    return [x, y];
}

f();    // [1, undefined]
f(2);   // [2, undefined]
f(, 1); // 报错, 编译无法通过

// rest参数
function add(...values) {
    let sum = 0;

    for (let val of values) {
        sum += val;
    }

    return sum;
}

console.log(add(2, 5, 3));   // 10

const sortNumbers = function() {
    let arr = Array.prototype.slice.call(arguments);
    return arr.sort();
};

const sortNumbers = function (...numbers) {
    return numbers.sort();
};

sortNumbers(3, 1, 2);

// rest参数必须是参数列表中的最后一个
const push = function(array, ...items) {
    items.forEach(function(item) {
        array.push(item);
    });
};

let a = [];

console.log(push(a, 3, 1, 2));
ES6扫盲

四、对象

ES6扫盲
// Object.assign方法用来将源对象的所有可枚举属性复制到目标对象
let target = {
    a: 1
};

// 后边的属性值,覆盖前面的属性值
Object.assign(target, {
    b: 2,
    c: 3
}, {
    a: 4
});

console.log(target);

// 用处1 - 为对象添加属性
class Point {
    constructor(x, y) {
        Object.assign(this, {x, y});
    }
}

//let p = new Point(1, 2);
//
//console.log(p); // Point {x: 1, y: 2}

// 用处2 - 为对象添加方法
Object.assign(Point.prototype, {
    getX() {
        return this.x;
    },
    setX(x) {
        this.x = x;
    }
});

let p = new Point(1, 2);

console.log(p.getX());  // 1

// 用处3 - 克隆对象
function clone(origin) {
    return Object.assign({}, origin);
}
ES6扫盲

五、Set和Map

ES6扫盲
  // Set里面的成员的值都是唯一的,没有重复的值,Set加入值时不会发生类型转换,所以5和"5"是两个不同的值.
    let s = new Set();

    [2, 3, 5, 4, 5, 2, 2].map(function(x) {
        s.add(x);
    });

    //for (let i of s) {
    //    console.log(i);
    //}

    console.log([...s]);

    console.log(s.size);

    // 数组去重
    function dedupe(array) {
        return Array.from(new Set(array));
    }

    console.log(dedupe([1, 2, 2, 3]));  // 1, 2, 3
ES6扫盲
ES6扫盲
{

    // Map类似于对象,也是键值对的集合,但是"键"的范围不限于字符串,各种类型的值(包括对象)都可以当做键.
    // 也就是说,Object结构提供了"字符串--值"的对应,Map的结构提供了"值——值"的对象,是一种更完善的Hash结构实现.

    var m = new Map();

    var o = {
        p: 'Hello World'
    };

    m.set(o, 'content');
    m.get(o);   // content

    m.has(o);    // true
    m.delete(o);    // true
    m.has(o);   // false

    m.set(o, 'my content').set(true, 7).set('foo', 8);

    console.log(m);

    // Map/数组/对象 三者之间的相互转换
    console.log([...m]);

}
ES6扫盲

六、Iterator和Generator

ES6扫盲
{
    // 是一种接口,为各种不同的数据结构提供统一的访问机制.任何数据结构,只要不输Iterator接口,就可以完成遍历操作.
    // 可供for...of循环消费

    const arr = ['red', 'green', 'blue'];

    let iterator = arr[Symbol.iterator]();

    for (let v of arr) {
        console.log(v); // red green blue
    }

    for (let i of iterator) {
        console.log(i);
    }

    // for of 循环可以代替数组对象的forEach方法, 同样可以替代对象的for in循环

}
ES6扫盲
ES6扫盲
{
    function * foo() {
        yield 1;
        yield 2;
        yield 3;
        yield 4;
        yield 5;
        return 6;
    }

    for (let v of foo()) {
        console.log(v);
    }

}
ES6扫盲

七、Promise和async

ES6扫盲
{
    let getJSON = function (path, param) {

        return new Promise(function(resolve, reject) {
            let async = typeof param.async == 'undefined' ? true : param.async;
            //let deferred = $.Deferred();

            param = param || {};
            param.data.auth_token = lib.getToken();

            window.loading();

            $.ajax({
                url: path,
                data: param.data,
                type: 'POST',
                dataType: 'json',
                async: async,
                timeout: 15000,
                success: function (data) {
                    window.unloading();
                    if (data.code == 0) {
                        resolve.apply(this, [data]);
                    } else {
                        reject.apply(this, [data]);
                        lib.alert(data.msg, '我知道了');
                    }
                },
                error: function (xhr, type) {
                    window.unloading();
                    reject.apply(this, ['网络异常, 请稍候再试']);
                    lib.alert('网络异常, 请稍候再试');
                }
            });
        });

    };

    getJSON('/xxx.json').then(function(rep) {

    }).catch(function(rep) {

    });

}
ES6扫盲
ES6扫盲
{

    function timeout(ms) {
        return new Promise(function(resolve) {
            setTimeout(resolve, ms);
        });
    }

    async function asyncPrint(value, ms) {
        let promise = await timeout(ms);
        console.log(value);
    }

    asyncPrint('Hello world !', 1000);

}
ES6扫盲

八、class

ES6扫盲
{
    class Point {

        static classMethod() {
            return 'classMethod...';
        }

        // constructor方法是类的默认方法,通过new命令生成对象实例时自动调用该方法.
        // 一个类必须有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }

        toString() {
            return '(' + this.x + ', ' + this.y + ')';
        }

        get prop() {
            return 'getter';
        }

        set prop(value) {
            console.log('setter:' + value);
        }

    }

    // 静态属性的处理,只能用下面这种方式
    Point.foo = 1;
    console.log(Point.foo); // 1

    // 继承
    class ColorPoint extends Point {

        constructor(x, y, color) {
            // super方法必须被调用, 否则编译不通过
            // 如果super在赋值属性this.xx = xx,后边调用,会报错'this' is not allowed before super()
            super(x, y);
            this.color = color;
        }

        toString() {
            return 'The color is ' + this.color + ' and the point is ' + super.toString();
        }

    }

    var p = new ColorPoint(1, 2, 'red');
    console.log(p.toString());
    p.prop = 1;
    p.prop;

    console.log(Point.classMethod());

    // 父类的静态方法可以被子类继承
    console.log('ColorPoint.classMethod(): ' + ColorPoint.classMethod());

}
ES6扫盲

九、Module

ES6扫盲
{
    // module
    /**
     * 优势:
     *  1. ES6可以在编译时就完成模块编译,效率要比commonJs模块的加载方式高
     *  2. 不再需要UMD模块格式,将来服务器端和浏览器都会支持ES6模块格式.目前,通过各种工具库其实已经做到了这一点
     *  3. 将来浏览器的新API可以用模块格式提供,不再需要做成全局变量或者navigator对象的属性
     *  4. 不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供
     */

}
ES6扫盲
ES6扫盲
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};

// export命令除了输出变量,还可以输出函数或类(class)。
export function multiply (x, y) {
    return x * y;
};

// export输出的变量就是本来的名字,但是可以使用as关键字重命名。

function v1() {
    //...
}
function v2() {
    //...
}

export {
    v1 as streamV1,
    v2 as streamV2,
    v2 as streamLatestVersion
};

// import
// main.js

import {firstName, lastName, year} from './profile';

// 重命名
import { lastName as surname } from './profile';

// import命令具有提升效果,会提升到整个模块的头部,首先执行。
foo();
import { foo } from 'my_module';

// 仅仅执行lodash模块,但是不输入任何值。
import 'lodash';

// export default
// 为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

// export-default.js
export default function () {
    console.log('foo');
}

// import-default.js
// 需要注意,这时import命令后面,不使用大括号。
import customName from './export-default';
customName(); // 'foo'

// export default命令用在非匿名函数前,也是可以的。
// export-default.js
export default function foo() {
    console.log('foo');
}

// 或者写成

function foo() {
    console.log('foo');
}

export default foo;
ES6扫盲
十、编程风格
ES6扫盲
// 1. let取代var


// 2. 全局常量
// 在let和const之间,建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常量。
// const声明常量还有两个好处,一是阅读代码的人立刻会意识到不应该修改这个值,二是防止了无意间修改变量值所导致的错误。
// 所有的函数都应该设置为常量。

// bad
var a = 1, b = 2, c = 3;

// good
const a = 1;
const b = 2;
const c = 3;

// best
const [a, b, c] = [1, 2, 3];


// 3. 字符串
// 静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。


// 4. 对象
//对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。
// bad
const a = {};
a.x = 3;

// if reshape unavoidable
const a = {};
Object.assign(a, { x: 3 });

// good
const a = { x: null };
a.x = 3;

// 对象的属性和方法,尽量采用简洁表达法,这样易于描述和书写。

var ref = 'some value';

// bad
const atom = {
    ref: ref,

    value: 1,

    addValue: function (value) {
        return atom.value + value;
    },
};

// good
const atom = {
    ref,

    value: 1,

    addValue(value) {
        return atom.value + value;
    },
};


// 5. 数组
//使用扩展运算符(...)拷贝数组。

// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++) {
    itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];

//使用Array.from方法,将类似数组的对象转为数组。

const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);

// 6. 函数
//不要在函数体内使用arguments变量,使用rest运算符(...)代替。因为rest运算符显式表明你想要获取参数,而且arguments是一个类似数组的对象,而rest运算符可以提供一个真正的数组。

// bad
function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
}

// good
function concatenateAll(...args) {
    return args.join('');
}

//使用默认值语法设置函数参数的默认值。

// bad
function handleThings(opts) {
    opts = opts || {};
}

// good
function handleThings(opts = {}) {
    // ...
}

// 7. 模块
//首先,Module语法是JavaScript模块的标准写法,坚持使用这种写法。使用import取代require。

// bad
const moduleA = require('moduleA');
const func1 = moduleA.func1;
const func2 = moduleA.func2;

// good
import { func1, func2 } from 'moduleA';

//使用export取代module.exports。

// commonJS的写法
var React = require('react');

var Breadcrumbs = React.createClass({
    render() {
        return <nav />;
    }
});

module.exports = Breadcrumbs;

// ES6的写法
import React from 'react';

const Breadcrumbs = React.createClass({
    render() {
        return <nav />;
    }
});

export default Breadcrumbs
//如果模块只有一个输出值,就使用export default,如果模块有多个输出值,就不使用export default,不要export default与普通的export同时使用。

//不要在模块输入中使用通配符。因为这样可以确保你的模块之中,有一个默认输出(export default)。

// bad
import * as myObject './importModule';

// good
import myObject from './importModule';
//如果模块默认输出一个函数,函数名的首字母应该小写。

function makeStyleGuide() {
}

export default makeStyleGuide;
//如果模块默认输出一个对象,对象名的首字母应该大写。

const StyleGuide = {
    es6: {
    }
};

export default StyleGuide;