ES6新增特性(二)

七、解构

7.1 解构的实用

在 ECMAScript 5 或更早的版本中,从对象或数组中获取特定的数据并赋值给本地变量需要书写很多并且相似的代码。例如:

let options = {
        repeat: true,
        save: false
   };

// 从对象中提取数据

let repeat = options.repeat,
    save = options.save;

这段代码反复地提取在 options 上存储地属性值并将它们传递给同名的本地变量。虽然这些看起来不是那么复杂,不过想象一下如果你的一大批变量有着相同的需求,你就只能一个一个地赋值。而且,如果你需要从对象内部嵌套的结构来查找想要的数据,你极有可能为了一小块数据而访问了整个数据结构。

这也是 ECMAScript 6 给对象和数组添加解构的原因。当你想要把数据结构分解为更小的部分时,从这些部分中提取数据会更容易些。很多语言都能使用精简的语法来实现解构操作。ECMAScript 6 解构的实际语法或许你已经非常熟悉:对象和数组字面量。

7.2 对象解构

7.2.1 对象解构的基本形式

对象结构的语法就是在赋值语句的左侧使用类似对象字面量的结构。

let node = {
        type: "Identifier",
        name: "foo"
    };
//这里就相当于声明了两个变量: type = node.type;  name:node.name
let { type, name } = node;

console.log(type);      // "Identifier"
console.log(name);      // "foo"

在上面的结构中必须要初始化。否则会出现语法错误。

// 语法错误!
var { type, name };

// 语法错误!
let { type, name };

// 语法错误!
const { type, name };

7.2.2 解构赋值表达式

如果声明的变量想改变他们的值,也可以使用解构表达式。

<script type="text/javascript">
    let node = {
      type: "Identifier",
      name: "foo"
    },
    type = "Literal",
    name = 5;

  //注意:此处必须要在圆括号内才能使用解构表达式
  ({type, name} = node);

  console.log(type);      // "Identifier"
  console.log(name);      // "foo""
</script>

7.2.3 对象解构时的默认值

如果赋值号右边的对象中没有与左边变量同名的属性,则左边的变量会是 undefined

let node = {
        type: "Identifier",
        name: "foo"
    };
//因为node中没有叫value的属性,所以valued的值将会是undefined
let { type, name, value } = node;

console.log(type);      // "Identifier"
console.log(name);      // "foo"
console.log(value);     // undefined

不过我们也可以手动指定他的默认值。(这个和函数的参数默认值很像)

<script type="text/javascript">
    let node = {
        type: "Identifier",
        name: "foo"
    };
    //手动添加value的默认值为3
    let { type, name, value = 3} = node;

    console.log(type);      // "Identifier"
    console.log(name);      // "foo"
    console.log(value);     // 3
</script>

7.2.4 赋值给不同的变量名

在前面的操作中,都是把对象的属性值,赋值给同名变量。

其实也可以赋值给不同名的变量。

<script type="text/javascript">
    let node = {
        type: "Identifier",
        name: "foo"
    };
    // localType才是要定义的新的变量。  type是node的属性
    let {type: localType, name: localName} = node;

    console.log(localType);     // "Identifier"
    console.log(localName);     // "foo"
</script>

注意:冒号后面才是要定义的新的变量,这个和我们的对象字面量不太一样!

这个地方也可以使用默认值。

let node = {
        type: "Identifier"
    };

let { type: localType, name: localName = "bar" } = node;

console.log(localType);     // "Identifier"
console.log(localName);     // "bar"

7.3 数组解构

7.3.1 数组解构基本语法

数据解构的语法和对象解构看起来类似,只是将对象字面量替换成了数组字面量,而且解构操作的是数组内部的位置(索引)而不是对象中的命名属性,例如:

let colors = [ "red", "green", "blue" ];
let [ firstColor, secondColor ] = colors;

console.log(firstColor);        // "red"
console.log(secondColor);       // "green"

如果只想取数组中的某一项,则可以不用命名。

let colors = [ "red", "green", "blue" ];
//只取数组中的第三项。
let [ , , thirdColor ] = colors;

console.log(thirdColor);        // "blue"

7.3.2 解构表达式

你可以想要赋值的情况下使用数组的解构赋值表达式,但是和对象解构不同,没必要将它们包含在圆括号中,例如:

let colors = [ "red", "green", "blue" ],
    firstColor = "black",
    secondColor = "purple";

[ firstColor, secondColor ] = colors;  //可以不用加括号。当然添加也不犯法

console.log(firstColor);        // "red"
console.log(secondColor);       // "green"

数组解构表达式有一个很常用的地方,就是交换两个变量的值。在以前一般定义一个第三方变量进行交换,例如下面的代码:

<script type="text/javascript">
    let a = 3,
        b = 4,
        temp;
    temp = a;
    a = b;
    b = temp;
    console.log(a);
    console.log(b)
</script>

那么在ES6中完全可以抛弃第三方变量这种方式,使用我们的数组解构表达式

<script type="text/javascript">
    let a = 3,
        b = 4;
    //左侧和前面的案例是一样的,右侧是一个新创建的数组字面量。
    [a, b] = [b, a];
    console.log(a);
    console.log(b)
</script>

八、新的基本类型:Symbol

以前我们有5种基本数据类型:Number、String、Boolean、Null、Undefined

ES6新增了一种新的数据类型:Symbol

在ES5之前我们都没办法创建私有变量,只能想办法去封装。symbol 来创建私有成员,这也是 JavaScript 开发者长久以来期待的一项特性。

8.1 创建Symbol

Symbol在基本数据类型中是比较特别的。我们以前的都可以用字面量去创建基本数据类型的数据,但是Symbol却不可以使用字面量的是形式去创建。

我们可以使用symbol全局函数来创建Symbol。

<script type="text/javascript">
    let firstName = Symbol();   //创建一个Symbol
    let person = {};

    person[firstName] = "张三";
    console.log(person[firstName]);     // "张三"
</script>

说明:上面的代码中,firstName 作为 symbol 类型被创建并赋值给 person 对象以作其属性。每次访问这个属性时必须使用该 symbol 。

在创建Symbol的时候,也可以传入字符串,这个字符串也仅仅是在调试输出的时候方便,实际没有啥用处。

<script type="text/javascript">
    var s1 = Symbol("abc");
    var s2 = Symbol("abc");
    console.log(s1 == s2); //false
</script>

注意:任意两个Symbol都不会相等,即使创建他们的时候使用了相同的参数。

8.2 识别Symbol

既然 symbol 是基本类型,你可以使用 typeof 操作符来判断变量是否为 symbol 。ECMAScript 6 拓展了 typeof 使其操作 symbol 时返回 "symbol"。例如:

let symbol = Symbol();
console.log(typeof symbol);         // "symbol"

8.3 Symbol作为属性名

由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
var mySymbol = Symbol();

// 第一种写法

var a = {};

a[mySymbol] = 'Hello!';

// 第二种写法

var a = {

    [mySymbol]: 'Hello!'
}

以上两种写法都是相同的结果

注意:

  1. symbol作为对象的属性的时候,只能使用 [ ] 去访问,不能使用点去访问。

  2. symbol作为对象的属性名使用的时候,该属性还是公开属性,不是私有属性。但是这个时候使用for... in和for...of时无法遍历到这个symbol属性的。

8.4 Symbol属性名的遍历

Symbol 作为属性名,该属性不会出现在for...in循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。

看下面的代码

<script type="text/javascript">
    var obj = {};
    var a = Symbol('a');
    var b = Symbol('b');

    obj[a] = 'Hello';
    obj[b] = 'World';
    // 返回obj对象所有Symbol类型的属性名组成的数组。
    var objectSymbols = Object.getOwnPropertySymbols(obj);
    console.log(objectSymbols)  //[Symbol(a), Symbol(b)]
</script>

看下面的代码

var obj = {};

var foo = Symbol("foo");
obj[foo] = "lisi";
for (var i in obj) {
  console.log(i); // 无输出 。   因为遍历不到Symbol型的属性 
}

Object.getOwnPropertyNames(obj);// []   只能拿到非Symbol类型的属性

Object.getOwnPropertySymbols(obj) //[Symbol(foo)]

还有一个新API可以拿到所有类型的属性,包括常规和Symbol型的。

Reflect.ownKeys

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};

Reflect.ownKeys(obj);//  ["enum", "nonEnum", Symbol(my_key)]

说明:

  1. 由于以 Symbol 值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。

8.5 Symbol.for(字符串)和Symbol.keyFor(symbol类型的值)

一、Symbol.for(字符串参数):在全局环境中搜索 以该字符串作为参数的Symbol值,如果搜到则返回这个sybol,如果搜不到则创建一个Symbol,并把它注册在全局环境中。

    <script type="text/javascript">
        //第一次搜不到,则新创建一个返回,并在全局环境(window)中注册
        var a = Symbol.for("foo");
        //第二次搜到上次创建的
        var b = Symbol.for("foo");
        console.log(a === b);  //因为两次搜到的是同一个Symbol,所以此处是true
    </script>

Symbol.for()和Symbol()都可以创建Symbol类型的数据。

二者区别:

  1. Symbol.for()对同样的字符串,每次得到结果肯定是一样的。因为都是从全局环境中搜索。
  2. Symbol()则不会有搜索的过程,每次都是一个全新的不同的symbol,而且也不会向全局环境中注册。

看下面的代码

<script type="text/javascript">
   var a = Symbol("foo");
   var b = Symbol.for("foo");
   console.log(a == b); //false
</script>

二、Symbol.keyFor(symbol):返回一个已经全局注册的symbol的"key"。

<script type="text/javascript">
    var a = Symbol("foo");
    var b = Symbol.for("foo");
    console.log(Symbol.keyFor(a)); // undefined.   因为a没有向全局环境中注册,所以是undefinded
    console.log(Symbol.keyFor(b)); // foo
</script>

九、Set数据结构

JavaScript 在绝大部分历史时期内只有一种集合类型,那就是数组。数组在 JavaScript 中的使用方式和其它语言很相似,但是其它集合类型的缺乏导致数组也经常被当作队列(queues)和栈(stacks)来使用。

因为数组的索引只能是数字类型,当开发者觉得非数字类型的索引是必要的时候会使用非数组对象。这项用法促进了以非类数组对象为基础的 set 和 map 集合类型的实现。

Set是类似数组的一种结构,可以存储数据,与数组的区别主要是 Set中的元素不能重复,而数组中的元素可以重复

一句话总结:Set类型是一个包含无重复元素的有序列表

9.1 创建Set和并添加元素

Set本身是一个构造函数。

<script type="text/javascript">
    //创建Set数据结构对象。
    var s = new Set();
    //调用set对象的add方法,向set中添加元素
    s.add("a");
    s.add("c");
    s.add("b");
    //set的size属性可以获取set中元素的个数
    console.log(s.size)
</script>

9.2 Set中不能添加重复元素

<script type="text/javascript">
    var s = new Set();
    s.add("a");
    s.add("c");
    s.add("b");
    s.add("a");  //重复,所以添加失败。注意这个地方并不会保存。
    console.log(s.size); // 长度是3
</script>   

看下面的代码:

<script type="text/javascript">
    var s = new Set();
    s.add(5);
    s.add("5");
    console.log(s.size); // 长度是2
</script>
在上面的代码中,数字5和字符串5都会添加成功。为什么呢?

Set是使用什么机制来判断两个元素是否相等的呢?

是通过我们前面说过的 Object.is(a, b) 来判断两个元素是否相等。

<script type="text/javascript">
    var s = new Set();
    s.add(+0);
    s.add(-0);  //重复添加不进去
    s.add(NaN);
    s.add(NaN); //重复添加不进去
    s.add([]);
    s.add([]);  //两个空数组不相等,所以可以添加进去
    s.add({});
    s.add({});  // 两个空对象也不重复,所以也可以添加进去
    console.log(s.size); // 长度是6
</script>

9.3 使用数组初始化Set

<script type="text/javascript">
    //使用数组中的元素来初始化Set,当然碰到重复的也不会添加进去。
    var s = new Set([2, 3, 2, 2, 4]);
    console.log(s.size)
</script>

9.4 判断一个值是否在Set中

使用Set的 has() 方法可以判断一个值是否在这个set中。

<script type="text/javascript">
    let set = new Set();
    set.add(5);
    set.add("5");

    console.log(set.has(5));    // true
    console.log(set.has(6));    // false
</script>

9.5 移除Set中的元素

delete(要删除的值) :删除单个值

clear():清空所有的值

<script type="text/javascript">
    let set = new Set();
    set.add(5);
    set.add("5");

    console.log(set.has(5));    // true

    set.delete(5);

    console.log(set.has(5));    // false
    console.log(set.size);      // 1

    set.clear();

    console.log(set.has("5"));  // false
    console.log(set.size);      // 0
</script>

9.6 遍历Set

数组有个方法forEach可以遍历数组。

  1. Set也有forEach可以遍历Set。

使用Set的forEach遍历时的回调函数有三个参数:

function (value, key, ownerSet){

}

参数1:遍历到的元素的值

参数2:对set集合来说,参数2的值和参数1的值是完全一样的。

参数3:这个 ==set== 自己

<script type="text/javascript">
    let set = new Set(["a", "c", "b", 9]);
    set.forEach(function (v, k, s) {
        console.log(v + "   " + (v === k) + "  " + (s === set));   // 永远是true
    })

</script>
  1. for…of也可以遍历set。
for(var v of set){
    console.log(v)
}

9.7 将Set转换为数组

将数组转换为Set相当容易,你只需要在创建Set集合时把数组作为参数传递进去即可。

把Set转换为数组使用前面讲到的扩展运算符也很容易

<script type="text/javascript">
    let set = new Set([1, 2, 3, 3, 3, 4, 5]),
        arr = [...set];  //使用扩展运算符。那么新的数组中已经没有了重复元素。注意,此处对set并没有什么影响

    console.log(arr);             // [1,2,3,4,5]
</script>

这种情况在需要去数组中重复元素的时候非常好用。

<script type="text/javascript">
    function eliminateDuplicates(items) {
        return [...new Set(items)];
    }
    let numbers = [1, 2, 3, 3, 3, 4, 5, 5, 2, 1, 1],
        //返回的是新的没有重复元素的数组。
        noDuplicates = eliminateDuplicates(numbers);
    console.log(noDuplicates);      // [1,2,3,4,5]
</script>

Set提供了处理一系列值的方式,不过如果想给这些值添加一些附加数据则显得力不从心,所以又提供了一种新的数据结构:Map

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • ES6 的内置对象扩展 Array 的扩展方法 一、Array 的扩展方法 1. 扩展运算符(展开语法) 扩展运算...
    李小白呀阅读 2,350评论 0 1
  • ES6新增特性 【说明】ES5和ES6是javascript语法发展过程中的新增版本,对一些语法及功能性方法作了增...
    time_刚刚好阅读 7,005评论 0 5
  • 一、let 和 const 1、块级作用域 先来举个栗子 会输出4个4,因为: var定义的变量是全局的, 所以全...
    大南瓜鸭阅读 2,890评论 0 2
  • 1.let和const关键字let: 变量不能重复声明。 块级作用域。在if-else语句、while循环、for...
    coder勇阅读 3,652评论 0 0
  • ECMAScript6,即ES6,是ECMAScript的第六次修订,于2015年完成,也称ES2015 ES6是...
    babyface_fe阅读 4,705评论 0 0

友情链接更多精彩内容