Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

30分钟上手es6 - 掘金 #36

Open
xiaodongxier opened this issue Jun 17, 2020 · 0 comments
Open

30分钟上手es6 - 掘金 #36

xiaodongxier opened this issue Jun 17, 2020 · 0 comments

Comments

@xiaodongxier
Copy link
Owner

1、前注

关于 es6 的详细说明,可以参照我的系列文章es6 从入门到熟练,或者阮一峰的ECMAScript 6 入门

我的系列文章,是在阮一峰的基础上,增加了更多适合初中级开发者的内容(包括大量的示例代码和解释),以降低学习难度,丰富说明。

本文是对 es6 整体的回顾,结合我的实际开发经验,对 es6 的一个小结。

为了精炼内容,es6 里不常用的内容已经去掉,而对常用、重要的 es6 知识,附上简单的代码说明,并另附有详细说明的博文链接,方便初中级开发者理解。

2、开发环境

关键字:IE9、Babel、Babel 的垫片、脚手架

首先,使用 es6 的前提是最低 IE9,如果你需要兼容 IE8,建议放弃 es6,专心使用神器 jQuery。

其次,如果需要使用 es6 来编写,那么你需要Babel转码器用于将你的 es6 代码转换为 es5 代码,用于兼容只能使用 es5 的环境。否则对于只能运行 es5 的环境(例如 IE9),是无法运行 es6 代码的。

第三,由于 Babel 在默认情况下,并不是全部转换的,如以下说明:

Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转码。

因此,我们需要垫片,一般情况下可以用babel-polyfill,也可以用babel-runtime,这两个有所差异。

babel-polyfill 会污染全局对象,即会对例如 Object 这样的对象添加方法,而 babel-runtime 只会转换 es6 语法的代码,但如果你需要调用 Object.assign 这样的方法,那就不行了。

由于细节很多,因此这里给几个参考链接吧:

Babel 全家桶

babel 的 polyfill 和 runtime 的区别

看完以上两个,可能会觉得应该同时使用这两个,然而并不需要,看下面这个链接:

transform-runtime 会自动应用 polyfill,即便没有使用 babel-polyfillconanliu17 Dec 2016提交的 issues。

如果你使用的 Vue.js,那么可以直接 fork 我的脚手架,然后当做自己的脚手架使用。

附脚手架链接:vue-scaffold,如果可以,给个 star 喔~~

如果你用的不是 Vue.js,那么可以去搜一下你所使用的框架的脚手架,然后拿来使用。如果找不到,可以找使用带脚手架的该框架项目,然后 down 下来,删除对方的项目只取壳来用即可。(如果有许可,记得阅读一下许可看能不能这么干)

3、let 和 const

既然有 let 和 const 了,那么推荐优先使用这两个。

一般情况下,let 可以直接替代 var,对于常量,可以用 const。

这不是必须的,但用这 2 个可以帮你规范写代码的习惯,所以还是强烈推荐的。

比较蛋疼的是,用 webtorm,let 有时候不会高亮,只有 var 和 const 是高亮的。这个可能是用的风格的问题,我也不太确定。解决方案我自己是没找到,凑合用吧。

另外,let 和 var 之间的一个重要区别是变量提升,所以如果你写代码不太规范的话,可能会报错,好好检查一下吧。

另外,阮一峰推荐将函数设置为常量,就像这样子:

const add = function (a, b) {
    return a + b
}
复制代码

我觉得挺有道理的,推荐。

4、字符串

既然用 es6,当然要用反引号这个高大上的东西了。

详细用法推荐我自己的博客:ECMAScript 6(7)模板字符串

最基本的用法,可以直接用反引号替代普通的引号(单引号和双引号)

例如:

let a = 'ab'
// 可以直接用以下替换
let a = `ab`
复制代码

而且一般情况下,简单需求不用再拼接字符串了~(另外,反引号也可以像普通字符串那样拼接)

如:

let str = '20004604';
let html = 'my QQ is ' + str;

//用以下替换
let str = '20004604';
let html = `my QQ is ${str}`;
复制代码

简单暴力省事。

5、解构赋值

最大的好处是简化了写法,如代码:

let obj = {
    a: 1,
    b: 2
}

//old
let a = obj.a;
let b = obj.b;

// es6
let {a, b} = obj
复制代码

除了对象之外,还有数组也可以解构赋值,别忘了。

6、对象

es6 的对象,比早期版本的写起来舒服很多。

例如:

  1. 对象属性是函数的时候可以简写;
  2. setter 和 getter 的简写;
  3. 通过Object.assign()来合并对象,实现继承或添加属性效果;
  4. 可以用属性名表达式;
  5. 可以用变量名只要作为对象的属性名,并且变量的值可以自动成为对象该属性名的值;

列一些常见写法:

let obj = {
    // 对象属性是函数的时候可以简写
    a(){
        console.log('对象属性是函数的时候可以简写')
    },
    // setter和getter的简写;
    get b() {
        return this._b
    },
    set b(val) {
        this._b = val
    }
}


let c = '添加了一个c'
// 通过``Object.assign()``来合并对象,实现继承或添加属性效果
// 可以用变量名只要作为对象的属性名,并且变量的值可以自动成为对象该属性名的值
Object.assign(obj, {
    c
})

// 可以用属性名表达式
let d = "abcd"
obj[d.replace(/abc/, '')] = '属性名表达式'
复制代码

7、数组

最常用的就两个:

  1. 扩展运算符...
  2. 将类数组的转为数组的Array.from()

如代码:

function getArgs() {
    let foo = [...arguments]
    console.log(foo)
    let bar = Array.from(arguments)
    console.log(bar)
}
getArgs(1, 2, 3)
// [1, 2, 3]
// [1, 2, 3]
复制代码

需要注意的一个特性:

  1. es5 在面对,通过Array(5)这样生成带空位的数组时,处理他的时候会跳过空位数组的空位
  2. es6 在同样情况下,因为使用遍历器接口,所以会进行处理(视为 undefined),而不是跳过;

8、函数

函数常用特性有以下几个:

  1. 箭头函数:特点是 this 永远指向声明时的父级作用域,写起来比普通函数简单;

  2. bind:可以给函数绑定 this,并将这个绑定后的函数返回(不影响原函数);

  3. rest 函数:即函数参数使用例如function test(..args){}这样的,这个返回的是一个数组,而不是类数组。

  4. 参数默认值:一般带默认值的参数,放在参数列表的后面。

    function test(a, b = 3) {
    console.log(a, b)
    console.log(this)
    }
    test.bind('Is this')(1)
    // 1 3
    // Is this

    function test2(...args) {
    console.log(args.length)
    }
    test2(1, 2, 3, 4, 5)
    // 5
    复制代码

9、Set 和 Map

Set 结构最大的特点是去重,Map 结构最大的特点是 kv 结构。

Set:

Set 和数组类似,可以存储元素,但是 Set 不能存储相同的值。

非引用类型变量来说,就是值相等;对于引用类型变量来说,指地址相等(而不是值相等)。详细情况请点击Set 类型和 WeakSet查看。

至于去重,一般是对数组使用。先作为参数生成一个 Set 类型变量,再利用扩展运算符变回数组,去重完成,完美。

利用扩展运算符,调用 Set 的迭代器接口

// 去重
let foo = new Set([1, 2, 3, 3, 3])
console.log([...foo]);  // [1, 2, 3]
复制代码

Map:

Map 结构和对象非常类似,不过最大的区别在于,Map 结构可以用其他类型作为 key,例如数组、对象等。

Map 可以参照这篇博客Map 和 WeakMap

示例代码:

let zhang = {
    firstName: "王"
}
let property = {
    gender: "男"
}
let foo = new Map()
foo.set(zhang, property)
foo.has(zhang)  // true
foo.get(zhang)  // {gender: "男"}
复制代码

10、Promise

Promise 是 es6 的精华之一,他非常适用于异步处理。

Promise 对象在使用的时候,分为两部分,第一部分是new Promise这一步,第二部分是对返回的 Promise 实例进行处理的内容。

因为是通过执行resolvereject来改变 Promise 的状态,从而决定执行 then 的时机的(类似回调函数),以及执行的哪一个。因此写起来和回调函数相近,但是可以连写,避免回调地狱的情况。

关于 Promise 的详细介绍请阅读Promise(1) 基础知识及之后三篇博客

如示例代码(对比普通 ajax 和 promise)(另注:为了方便理解,仿 jQuery 的写法,并且没有用 jQuery 的$.ajax().then()这种写法)

// 模拟ajax
function ajax (options) {
    setTimeout(function () {
        options.success(options.url)
    }, 1000)
}

// old
let foo = function (callback) {
    ajax({
        url: "/1",
        success: function (result) {
            callback(result)
        }
    })
}
let foo2 = function (result) {
    console.log(result)
    return function (callback) {
        ajax({
            url: "/2",
            success: function (val) {
                callback(val)
            }
        })
    }
}
// 核心,调用的时候如果是连续请求的话,基本要写成回调地狱了
foo(function (result) {
    foo2(result)(function (val) {
        console.log(val)
    })
})

// Promise
let bar = function () {
    return new Promise((resolve, reject) => {
        ajax({
            url: "/1",
            success: function (result) {
                resolve(result)
            }
        })
    })
}
let bar2 = function (result) {
    console.log(result)
    return new Promise((resolve, reject) => {
        ajax({
            url: "/2",
            success: function (val) {
                resolve(val)
            }
        })
    })
}
// 核心,then连写即可
bar().then(function (result) {
    return bar2(result)
}).then(function (result) {
    console.log(result)
})
复制代码

显然,then 连写比回调函数的写法要方便一些。

如果面对的是特殊需求,比如是多个 ajax 请求全部完成后,再执行执行函数,那么 Promise 的优势会更大一些,而非 Promise 写法要麻烦很多。

甚至如果要对错误进行处理,那么 Promise 写法会更方便。

不过这里只是小结,就不细说了。

11、class

class 是好东西。

有了 class 后,写构造函数、写类的继承的难度,下降了很多很多。

先附我的博文class(1) 基本概念,以及之后 5 篇博文。

由于很简单,给一个示例大约就能理解这个是怎么用的:

class Foo {
    constructor () {
        console.log('this is constructor')
        this.defaultValue = '变量要在构造函数里赋值,而不能直接声明'
    }

    log () {
        console.log('log')
    }
}

let foo = new Foo() // this is constructor
foo.log()   // log
foo.defaultValue   // "变量要在构造函数里赋值,而不能直接声明"
复制代码

12、es6 模块

es6 的模块不同于以往的 CommonJS(node 用,服务器环境),AMD(RequireJS 的规范,浏览器环境,依赖前置)、CMD(SeaJS 定义的规范,浏览器环境,依赖就近)。

他的特点有两个:

  1. 编译时加载,因此可以做静态优化;
  2. 模块的引用进来的,都是值的引用,而非值的拷贝。

缺点是:

  1. 浏览器环境下不支持,node 环境下支持的也比较差;
  2. 必须考 babel 转码后才可以正常使用,因此对某些符合规范的特性支持的不是很好;

详细说明阅读这篇博客:es6 的 import 和 export,另外三个规范阅读这篇博客AMD、CMD、CommonJS

基本使用方式如示例代码:

// foo.js
let foo = 'foo'
export default foo

// bar.js
import foo from 'foo'
console.log(foo)
复制代码

13、async 函数

这个并不是 es6 的,而是 es2017(又称 es8)的内容。

可以认为 async 函数是 Generator 函数的语法糖,详细说明参照这篇博客:async 函数

他的前置知识比较多,包括 Iterator 遍历器、Generator 状态机、Thunk 函数(自动执行 Generator 函数)。

简单的说,假如有多个异步请求,你需要让这些起步请求依次执行,例如在执行完前一个之后,再执行后一个。那么你就需要 async 函数了。

async 函数可以让你写这种请求如同写同步函数一样简单(对比【10】中的 Promise 更简单)。

以下示例是基于【10】中的代码,在最后一步执行的时候,改用 async 函数来完成

// 模拟ajax
function ajax (options) {
    setTimeout(function () {
        options.success(options.url)
    }, 1000)
}

// Promise
let bar = function () {
    return new Promise((resolve, reject) => {
        ajax({
            url: "/1",
            success: function (result) {
                resolve(result)
            }
        })
    })
}
let bar2 = function (result) {
    console.log(result)
    return new Promise((resolve, reject) => {
        ajax({
            url: "/2",
            success: function (val) {
                resolve(val)
            }
        })
    })
}

async function foo () {
    let result1 = await bar()
    let result2 = await bar2(result1)
    return result2
}

foo().then(result => {
    console.log(result)
})
复制代码

可以发现,async 让连续异步调用像写同步函数一样简单。

14、ESLint

规范化开发,建议还是用 ESLint 来帮忙检查吧。不会这个怎么行?

ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。

这里直接给一个阮一峰写的文章,等以后我再单独补一篇详细用法的博客。

ESLint 的使用

15、小结

es6 常用内容基本就以上 13 点。

虽然 es6 实际包括了很多知识,例如:

  1. string 方面增加了对 utf16 字符的更好的支持;
  2. number 方面增加了对最大值、最小值、以及合理误差误差值的处理等;
  3. Symbol 产生唯一变量;
  4. Proxy 的代理;
  5. 遍历器,状态机等等。

但实际常用的就以上这些,如果只是日常使用的话,熟悉以上内容足够了。

但若要做的更好,那么应该深入学习,es6 新增的很多内容,是将传统后端语言的一些很好的思想,搬到 JavaScript 来,让 js 规范化。

对于专精于前端的同学,学习 es6 的过程中,可以学习到这些来自于其他语言的精华,因此建议至少完整的看一遍,勿要只满足于常用的这些 API。

我的技术交流 QQ 群:387017550,感兴趣的欢迎加入和我进行讨论,群里有很多 BAT 的同学,也会解答常见问题。
https://juejin.im/post/5ee9a1556fb9a0586320a395?utm_source=gold_browser_extension

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant