ES6 常用功能总结

ES6 有很多新特性,带来了很多方便,今天来稍微总结一下比较常用的。本篇文章中的所有示例都使用 rollup 进行编译。

1. let / const

es6 以前,都是用 var 关键字来标识,这样有个变量提升的坑。在 es6 中,添加了 let 和 const 两个关键字,let 定义变量,const 定义常量,并且添加了块级作用域。看下用法:
let:

let a = 1
a = 100

编译结果:

var a = 1;
a = 100;

const:

const b = 2

编译结果:

var b = 2;

再看一个:

const b = 2
b = 200

出错:

SyntaxError: es6-demo.js: "b" is read-only

说 b 是只读,说明 const 定义的是常量。在实际项目中,如有不希望别人改的变量,就可以用 const 定义,也是很方便了。

顺便看一下var的变量提升:

console.log(a); // undefined
var a = 100

在定义之前输出变量 a ,是 undefined,他其实相当于这样子写:

var a 
console.log(a);
a = 100

es6 以前,js 引擎将所有的变量都提到最前面,初始化为 undefined。

2. 多行字符串 / 模板变量

在 es6 之前,字符串的拼接是这样的:

var name = "李四"
var age = 18
var myIntro = ''
myIntro += 'my name is ' + name + ', '
myIntro += 'my age is ' + age

在 es6 中,可以使用模板字符串 `` 和模板变量 ${ } :

const name = "李四"
const age = 18
const myIntro = `my name is ${name}, 
                 my age is ${age}
                `

这样使用起来很方便,避免字符串拼接中出现的不必要的错误,而且更简单简洁,最重要的是人易懂!需要注意的是 ${ } 要和 `` 一起使用不然不会被解析。

3. 解构赋值

顾名思义,就是先解构,再赋值!
比如先定义一个对象和数组:

var obj = {
    a: 1,
    b: 2,
    c: 3
}
var arr = [1,2,3]

在 es6 以前,这样获取属性的值:

obj.a
obj.b
arr[i]

在 es6 中:

const {a,c} = obj
const [x,y,z] = arr

很简单就可以获取对象的属性和数组的值,看下编译得结果:

var obj = {
  a: 1,
  b: 2,
  c: 3
};
var a = obj.a,
    c = obj.c;
var arr = [1, 2, 3];
var x = arr[0],
    y = arr[1],
    z = arr[2];

看明白了吗?
就是使用 const {a,c} = obj 这种方式获取对象的属性的方法时,大括号中的变量对象对象的属性,使用 const [x,y,z] = arr 这种方式获取数组的元素,中括号中的变量的索引对应真正数组的索引,即:x 是第一个,对应 arr 数组中的第一个,z 是数组的第三个,对应 arr 数组的第三个。

4. 块级作用域

在 es6 以前:

var obj = {
    a: 1,
    b: 2,
    c: 3
}
for (var item in obj) {
    console.log(item);
}
console.log(item);

变量 item 其实是在循环外部,咱们预想是访问不到的,但是实际是可以访问到的,以上写法相当于将 var item 提到最前面,就好理解了,这样子的话变量 item 相当于是在全局都可以访问的,这与我们的预期是相违背的。
再来看看 es6 中:

const obj = {
    a: 1,
    b: 2,
    c: 3
}
for (let key in obj) {
    console.log(key);
}
console.log(key);

因为有块级作用域的概念,所以循环中的 key 变量只在循环中能使用,咱们编译一下:

var obj = {
    a: 1,
    b: 2,
    c: 3
};
for (var _key in obj) {
    console.log(_key);
}
console.log(key);

很明显,循环里面的 key 和 外面的 key 不是同一个东西!

5. 函数默认参数

首先来设定一个场景:有一个函数 test ,可能给它传一个参数,也可能传两个,传一个参数时,第二个参数给个默认值,在 es6 以前这样判断:

function test (a, b) {
    if (b == null) {
        b = 0
    }
}

但是在 es6 中写法非常简单:

function test (a, b = 0) {
    // ...
}

咱们编译一下,康康他到底是个啥:

function test(a) {
  // ...
  var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
}

原来如此!es6的写法也就是在内部做了一个判断:如果参数的个数大于1,并且第二个参数不是undefined,那么就将第二个参数赋值给 b,否则b = 0。这个跟上面的 es6 之前的判断是一致的,这种写法特别简单、易读!

6. 箭头函数

用 map 遍历数组
es6 以前:

var arr = [100, 200, 300]
arr.map(function (item) {
    return item + 1
})

es6 写法:

const arr = [100, 200, 300]
arr.map(item => item + 1)

当函数只有一个参数,并且函数体中只有一行代码时,可以简写成上面的形式,参数有多个,函数体中超过一行这样写:

arr.map((item,index) => {
    console.log(index, item);
    return item + 1
})

编译看一下:

var arr = [100, 200, 300];
arr.map(function (item) {
  return item + 1;
});
arr.map(function (item, index) {
  console.log(index, item);
  return item + 1;
});

好吧,,其实就是把箭头函数转换成普通函数。。
你以为箭头函数到这儿就完了???
this 指向:
运行下面脚本:

function test () {
    console.log('test', this); 
}
test.call({a: 100})

结果:

test {a: 100}

在其中加一个map:

function test () {
    console.log('test', this);
    var arr = [1,2,3]
    arr.map(function (item) {
        console.log(item, this);
    })
}
test.call({a: 100})

结果:
在这里插入图片描述
很明显,map 中的 this 是 window 对象。这是 js 的一个大坑,在这个情况下,通过 call 绑定了一个 this 是 { a: 100 } 这个对象,但是 map 中的 this 却是 window 。这是因为 this 的取值是在其调用时决定的,而 map 中的函数相当于调用一个普通函数,而普通函数的作用域在全局,其 this 就指向了 window 。而箭头函数韩浩的解决了这个问题,箭头函数中的 this 与其上一个作用域的 this ,在此处中,map 中的 上一级作用域是 test 函数,而 test 函数的 this 是 { a: 100 } 对象。

function test () {
    console.log('test', this);
    var arr = [1,2,3]
    arr.map( item => {
        console.log(item, this);
    })
}
test.call({a: 100})

结果:
在这里插入图片描述

7. 模块化

在现在多个人开发同一个项目很常见,每个人负责不同的模块,还有可能会几个人使用同一个模块,在这种情况下,模块化就很重要!其实使用起来也很简单,比如说有模块A、B、C三个 js 文件,各自在其中定义好自己的代码,使用 export 关键字导出自己的东西,别人使用时用 import 关键字引用即可,模块化的处理工具有 webpack、rollup 等。如果你有兴趣,详情可以看看我的上一篇博客ES6 模块化如何使用,开发环境如何打包,欢迎指正。

8. class

标题是 class, 但是实际上应该说是构造函数:
es6以前,js 构造函数:

function MathPlus (x, y) {
    this.x = x
    this.y = y
}
MathPlus.prototype.getAddResult = function () {
    return this.x + this.y
}
var testAdd = new MathPlus(1,2)
console.log(testAdd.getAddResult()); // 3

es6 中 class 写法:

class MathPlus {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    getAddResult() {
        return this.x + this.y
    }
}
const testAddEs6 = new MathPlus(3,4)
console.log(testAddEs6.getAddResult()); // 7

js 定义构造函数和 es6 中的 class 的所实现目的一样的,但是 class 就看起来更加清晰、易懂!在 js 的构造函数中,在其原型上定义方法从而实现,而 class 中直接在 { } 中写函数就可以直接实现这个类中有这个函数了。
本质:
其实 class 的本质其实是一个语法糖
理解起来就是,二者实现的东西是一样的,只是有个的写法更简洁、易读、易懂。对应一下,其实 class 所实现的东西和 js 的构造函数是一样的,class 的实现原理和 js 构造函数是一样的,都是通过原型实现的。

console.log(typeof MathPlus); // function
console.log( MathPlus.prototype === testAddEs6.__proto__ ); // true
console.log( MathPlus === MathPlus.prototype.constructor ); // true

以上的结果和 js 的构造函数是一致的,MathPlus 这个 class 的本质是一个function ,其实例 testAddEs6 有一个隐式原型 __proto__ ,并且和 MathPlus 的 prototype 三等。
关于继承:
js 实现继承:

function Math (x, y) {
    this.x = x
    this.y = y
}
Math.prototype.getSubResult = function () {
    return this.x - this.y
}

// 通过绑定原型,实现继承
MathPlus.prototype = new Math()

var testAdd = new MathPlus(1,2)
// console.log(testAdd.getAddResult());
console.log(testAdd.getSubResult()); // -1

class 继承:

class Math {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    getSubResult() {
        return this.x - this.y
    }
}
class MathPlus extends Math {
    constructor(x, y) {
        super(x, y) // 重点!!!自动实现父类的 constructor
        this.x = x
        this.y = y
    }
    getAddResult() {
        return this.x + this.y
    }
}
const testAddEs6 = new MathPlus(3,4)
console.log(testAddEs6.getAddResult()); // 7
console.log(testAddEs6.getSubResult()); // -1
console.log(testAddEs6.__proto__ === MathPlus.prototype); // true
console.log(MathPlus.prototype.__proto__ === Math.prototype); // true

看到结果其实就更能体现 class 实际上是通过原型实现的!

9. promise

在项目中涉及到网络请求资源时,就要调用后端提供的接口,常用的jQuery、axios、fetch等,我用的最多的就是 axios ,jQuery 也用过,不过很少。。,说正题!调用接口之后就有回调函数,成功与否都有相应的回调函数,这个是异步的,但是当请求比较复杂时,会出现回调地狱(callback hell),比如说:根据接口1 获取 data1 ,在其中调用接口2 获取data 3…,用 promise 就很简单,至少看起来不会那么复杂。
首先我们先封装一个根据接口 URL 获取数据的通用方法 getData :

function getData(url) {
    return new Promise((resolve, reject) => {
        $.ajax({
            url,
            success: (data) => {
                resolve(data)
            },
            error: (data) => {
                reject(data)
            }
        })
    })
}

getData 函数中,返回了一个 promise 实例,resolve 表示成功时调用的回调函数,reject 表示失败时调用的回调函数。那么,对于上面比较复杂的情况就可以写成:

getData(url1).then(data1 => {
    console.log(data1);
    return getData(url2)
}).then(data2 => {
    console.log(data2);
    return getData(url3)
}).catch(err => {
    console.log(err);
})

是不是很清晰?每一次回调 then 中的 data 来自上一层的数据返回。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值