资讯专栏INFORMATION COLUMN

ES6学习总结(1)

happyfish / 1721人阅读

摘要:返回一个对象,遍历对象自身和继承的所有可枚举属性不含,与相同和在红宝书中就已经提到过属性,表示的是引用类型实例的一个内部指针,指向该实例的构造函数的原型对象。

半个月前就决定要将ES6的学习总结一遍,结果拖延症一犯,半个月就过去了,现在补起来,惭愧惭愧。

阮一峰的《ES6标准入门》这本书有300页左右,除了几个新的API和js语法的扩展,真正有价值的内容并不多。所谓“存在即合理”,每部分的内容都对相应的人群有价值,我将结合我在实习中接触过的ES6的部分和我的个人理解来对这本书的内容做个总结。如有不足或者谬误指出,还请拍砖。

总结主要从三个方面展开:1. 语法扩展;2. ES6新API; 3. 模块化方案。

1. 语法扩展 (1)let、const与块级作用域

let、const声明的标识符仅在块级作用域内有效,块级作用域就是大括号({})包括的区域。const与let的差别在于let声明的是变量,const声明的是常量。

let、const不存在变量提升

// let/const不存在变量提升
console.log(foo);   // undefined
var foo = "abc";

console.log(lish);  // 未定义报错,ReferenceError
let lish = "hello";

let、const暂时性死区:已经声明了变量,在声明变量之前,该变量都是不可用的。在局部作用域中,即使已经存在全局的同名变量,还是无法在局部声明之前访问该变量,有点绕,直接上代码。

// 暂时性死区
var num = 1;
const add = () => {
    console.log(num);
    let num;
}
add();      // 报错,num is not defined

let、const不允许重复声明,在ES5中,用var声明的变量我们可以用var重复声明,就好像重新赋值一样,但是用let、const重复声明会报错。

(2)解构赋值

解构赋值说白了就是“模式匹配”,从等号两边对应的位置取出对应的值

数组的解构赋值

let [a, b, c] = [1, 2, 3];
// 从对应位置拿出对应值,相当于我们声明了三个变量
// let a = 1, b = 2, c = 3;

需要指出一点,只要某种数据具有Iterator接口,就可以采用数组形式的解构赋值(其中就包括字符串),Iterator我们会在总结(2)提到。 举个例子

// Set是ES6新增的一种数据类型,拥有Iterator接口
let [x, y, z] = new Set([1, 2, 3]); // x = 1, y = 2, z = 3

解构允许指定默认值。默认值有undefined触发

let [x = 1, y = 2] = [44]; // x = 44, y = 2;
let [x = 1, y = 2] = [44, undefined]; // 与上面等价

对象的解构赋值

只要我们知道这是模式匹配,就比较好理解了,先通过键找到对应的值,将值赋给等式左边相同键名对应的变量,{foo}就是{foo: foo}的简写形式,这属于对象的扩展,接下来会提到。

let { foo, bar } = { foo: "hello", bar: "world" };  // foo = "hello", bar = "world"
let {foo: val, bar} =  { foo: "hello", bar: "world" };  // val = "hello", bar = "world";

和数组解构类似,也可以为对象解构赋默认值let {x = 3} = {};,默认值由undefined触发。
需要注意的一点是,大括号不能出现在行首,会被解析为代码块,比如{x} = {x: 1};

函数参数的解构赋值

参数可以为数组或对象
比如:我们为函数直接传一个数组,在函数内部我们直接可以使用解构后的值

const add = ([x, y]) => {
    return x + y;
}
console.log(add([1, 2]);    // 3
console.log(add.length);    // 1

上面代码中,比较好理解的是add.length,代表函数参数的个数,但是我们为参数指定默认值时, length为0,原因就是函数的length属性为没有指定默认值的参数的个数,而且不包括rest参数

const add = ([x, y] = [0, 0]) => {
    return x + y;
}
console.log(add());    // 0
console.log(add.length);    // 0
(3)模板字符串

字符串扩展中,最有用的就是模板字符串,用反引号(`)标识,模板字符串中可以使用变量,省去了用连接符来拼接字符串的麻烦。

let foo = "hello";
console.log(`${foo} world!`);   // hello world

看了上面,相信大家都会在模板字符串中使用变量了,就是讲变量写在${}中就行了。
另外,模板字符串中还可以使用表达式(包括函数调用,运算等),同样,将表达式写在${}中就行了。

(4)扩展运算符(...)与 rest参数

扩展运算符
作用:将一个数组(姑且先说是数组)转换为用逗号分隔的参数序列。

console.log([1, 2, ...[2, 3, 4]]); // [1, 2, 2, 3, 4]
console.log(..."hello"); // h e l l o

// generator函数
let go = function* () {
    yield 1;
    yield 2;
    yield 3;
};
console.log(...go());   // 1 2 3

问题来了:扩展运算符可以作用于哪些数据类型呢?

回答这个问题很关键,直接说答案吧:拥有Iterator接口的数据类型

rest参数

阮大神的书上原话:rest参数(...变量名),用于获取函数多余的参数,放进一个数组中。

const add = function (arg1, ...values) {
    return values;
};
console.log(add(1, 3, 5, 7));   // [3, 5, 7]

个人之见,这句话有点问题,rest参数好比是扩展运算符的逆运算,怎么能只用于获取函数多余的参数呢,事实证明,rest不仅仅用于获取函数参数。
我们看上的函数定义部分的参数和函数调用部分的参数,这不就是一个模式匹配吗?arg1对应第一个参数,...values对应剩余的参数。说道模式匹配,不得不说解构赋值,那么我们在解构赋值中应用rest参数呢,下面我们试试看

let values, num;
[num, ...values] = [1, 3, 5, 7];
console.log(values);    // [3, 5, 7]

实验证明rest参数也可以用于解构赋值中,要注意的是rest参数只能放在其他参数的后面,否则rest参数无法判断里面参数的个数

(5)数组的扩展

Array.from()
作用: 将类似数组的对象和可遍历的对象(拥有Iterator接口)转为真正的数组

所谓类似数组的对象,关键在于有没有一个length属性表示对象中属性的个数

var obj = {0: "hello", "sayName": "hi", 1: "world", length: 2};
console.log(Array.from(obj));   //["hello", "world"];

上面转换的只是键名为数字的属性

Array.of()
作用:将一组数值转换为数组,主要目的为了弥补Array构造函数不能识别长度还是值。

实例方法find()和findIndex()
find()找到第一个符合条件的数组成员,参数为函数,返回true表示找到

findIndex(),与find()类似,返回的是数组的索引。

遍历方法entries()、keys()、values()
都返回遍历器对象,便利器对象可用for...of进行遍历

for(let index of ["a", "b"].keys()) {
    console.log(index);
}
// 0
// 1

for(let value of ["a", "b"].values()) {
    console.log(value);
}
// a
// b

for(let [index, value] of ["a", "b"].entries()){
    console.log(index, value);
}
// 0 a
// 1 b

includes()
作用:判断数组是否包含某个值,属于ES7,客户端使用要使用Babel进行转码,为了弥补indexOf()的不足,indexOf()内部使用“===”进行判断,对于NaN的判断会出错,而includes()解决了这个问题

console.log([NaN, NaN].indexOf(NaN));   // -1
console.log([NaN, NaN].includes(NaN));  // true
(6)函数的扩展

函数参数的解构赋值,默认值在第一部分已经提到,这里就不再总结,在函数扩展这一部分,我主要总结箭头函数的使用
ES6中可以使用箭头函数定义函数,方式如下:

const f = (arg1) => {
    return arg1 * 2;
}
const f = arg1 => arg1*2;     // 等同于上面的定义

当参数只有一个时,可以省略括号,当函数体只有return语句一条语句时,可以省略大括号和return关键字

使用箭头函数需要注意的几点

箭头函数不可以当做构造函数

不可以使用arguments对象

不可以使用yield命令,不能用作generator函数

箭头函数没有自己的this,导致内部的this就是外层代码的this,所以不能用作构造函数,也不能使用call()、apply()、bind()改变this的指向

(7)对象的扩展

属性的简洁表示法

变量名转属性名

let foo = "hello";
let obj = {foo: foo};

// 简写形式
let foo = "hello";
let obj = {foo};    // 变量名直接转为属性名

方法的简写

let obj = {
    sayName: function() {
        return "hello";
    }
}

// 简写形式
let obj = {
    sayName() {
        return "hello";
    }
};

ES6允许字面量定义对象时使用表达式作为对象的属性名
将表达式放在中括号中,但是属性名表达式与简洁表示法不能同时使用

let obj = {
    ["a" + "bc"]: 123
}

Object.is()
用来比较两个值是否严格相等,与“===”行为基本一致,不同之处在于: +0不等于-0,NaN等于自身

Object.assign()
将源对象的所有可枚举的自有属性复制(浅复制)到目标对象Object.assign(target, source1, source2 ...),返回target,可以处理数组,将其视为对象

let target = {a: 1};
let source1 = {b: 2};
let source2 = {c: 3};
Object.assign(target, source1, source2);
// target => {a: 1, b: 2, c: 3};

一般情况下,我们不希望改变对象,只希望合并对象的属性,那么可以将target设为空对象{}, Object.assign({}, source1, source2);

需要注意的是Object.assign()执行的是浅复制, 如果源对象的属性为引用类型,那么target中对应的属性和源对象中的属性引用同一个值

let source1 = {b: {test: "hello world"}};
let target = Object.assign({}, source1);

console.log(target.b);  // { test: "hello world" }

source1.b.test = 111111;
console.log(target.b);  // {test: 111111}

对象的属性的遍历

for...in 循环遍历对象自身和继承的可枚举属性(不含Symbol属性),Symbol是ES6中的一种新的数据类型,表示一个独一无二的值,在第二大部分,我将会讲到这个类型。
Object.keys(obj) 返回数组,包括对象自身的所有可枚举属性(不含Symbol属性)
Object.getOwnPropertyNames(obj) 返回数组,包含对象自身的所有属性(包括不可枚举属性,不包含Symbol属性)

说白了,就是ES5中遍历方法都无法获取到Symbol属性

Object.getOwnPropertySymbols(obj)返回数组,包含自身的所有Symbol属性
Reflect.ownKeys(obj)返回一个数组,包含对象自身的所有属性(包括不可枚举和Symbol), Reflect是ES6中的新API,其目的之一就是将Object对象上一些属于语言层面的方法放到该对象身上,并修改某些方法,让其变得更合理。
Reflect.enumerate(obj)返回一个Iterator对象,遍历对象自身和继承的所有可枚举属性(不含Symbol),与for...in相同

__proto__和Object.setPrototype()

在红宝书中就已经提到过__proto__属性,表示的是引用类型实例的一个内部指针,指向该实例的构造函数的原型对象。一般不建议我们直接使用,因为这是一个非标准属性,但是所有浏览器都部署了这个属性,node中也可以使用

// 利用__proto__实现继承
function Animal(name) {
    this.name = name;
}
Animal.prototype.sayName = function() {
    console.log(this.name);
};

function Dog(name, leg) {
    Animal.call(this, name);
    this.leg = leg;
}
Dog.prototype.__proto__ = Animal.prototype;
Dog.prototype.constructor = Dog;

var dog = new Dog("Genkle", 4);
dog.sayName();  // Genkle

Object.setPrototype(object, prototype)用于设置一个对象的prototype对象,相当于object.__proto__ = prototype,是ES6中设置原型对象的方法。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82388.html

相关文章

  • ES6学习总结(二)

    摘要:关于的学习总结昨天写了第一篇,主要是关于变量声明关键字和,新增类型以及模板字符串,今天准备写第二篇,里面的函数部分,新增了箭头函数,参数以及参数默认值。,这次我们在调用函数大的时候,两个参数全部传递了值,因此返回。 关于ES6的学习总结,昨天写了第一篇,主要是关于变量声明关键字let和const,新增类型Symbol以及模板字符串,今天准备写第二篇,ES6里面的函数部分,ES6新增了箭...

    microelec 评论0 收藏0
  • ES6解构赋值学习总结

    摘要:提供了解构赋值的方式,这样子在赋值多个变量或者进行注释时可以方便很多,不同场景下也有很多新的应用,个人常使用的有数组的解构赋值,对象的解构赋值和函数参数的解构赋值,函数参数的解构赋值之前总结过,这里写下数组的和对象的简单总结吧函数参数的解构 ES6提供了解构赋值的方式,这样子在赋值多个变量或者进行注释时可以方便很多,不同场景下也有很多新的应用,个人常使用的有数组的解构赋值,对象的解构赋...

    levinit 评论0 收藏0
  • ES6学习总结(三)

    摘要:不同于其他面向对象语言,以前的中中没有类的概念,主要是通过原型的方式来实现继承,中引入了原型链,并且将原型链用来实现继承,其核心是利用原型使得一个对象继承另一个对象的方法和属性,中原型继承的关键是将一个实例的原型对象指向另一个实例,因此前一 不同于其他面向对象语言,ES6以前的JavaScript中中没有class类的概念,主要是通过原型的方式来实现继承,JavaScript中引入了原...

    baoxl 评论0 收藏0
  • ES6学习总结(一)

    摘要:可以通过调用方法将创建一个新的类型的值,这个值独一无二,不与任何值相等。还可以使可扩展,在中,表达式被标准化为构造函数的一个方法,这意味着它是可扩展的。 前端发展的太快了,快到ES6,ES7出来之后,今年已经是ES8了,但是纵然前端发展很快,我们除了马不停蹄的学习新的技术之外,也要沉下心来,好好的潜心磨砺自己,本文是整理了自己学习ES6之后相关的知识要点,寄希望于书之于笔,一来自己可以...

    _ipo 评论0 收藏0
  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<