ES6进修笔记1--let和const敕令、解构赋值和Symbol

let和const敕令

let敕令

  1. 在声明地点的块级作用域内有效。

  2. 只需块级作用域内存在let敕令,它所声明的变量就“绑定”(binding)这个地区,不再受外部的影响。

  3. 在同一个作用域内,不许可反复声明变量。var能够反复声明。

  4. let敕令不存在变量提拔。var敕令会发作”变量提拔“征象。因而运用let敕令声明的变量,通常在声明之前就运用这些变量,就会报错。

块级作用域与函数声明

  1. 在块级作用域当中声明的函数,在块级作用域以外不可援用。

  2. 在浏览器的 ES6 环境中,块级作用域内声明的函数,行动相似于var声明的变量。

  3. ES6 的块级作用域许可声明函数的划定规矩,只在运用大括号的状况下建立,假如没有运用大括号,就会报错。
    应当防止在块级作用域内声明函数。假如确切须要,也应当写成函数表达式,而不是函数声明语句。

const

  1. const声明的变量不得转变值。只声明不赋值,就会报错。

  2. 在声明地点的块级作用域内有效。

  3. 不存在变量提拔。

  4. 在同一个作用域内,不许可反复声明变量。

const实际上保证的,是变量指向的谁人内存地点不得修改。因而:

  1. 关于简朴范例的数据(数值、字符串、布尔值),值就保留在变量指向的谁人内存地点,因而等同于常量。

  2. 关于复合范例的数据(主如果对象和数组),变量指向的内存地点,保留的只是一个指针,const只能保证这个指针是牢固的。

比方:

const foo = {};

// 为 foo 增加一个属性,能够胜利
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错。因为常量foo贮存的是一个地点,这个地点指向一个对象。不可变的只是这个地点,即不能把foo指向另一个地点,但对象自身是可变的,所以依旧能够为其增加新属性。
foo = {}; // TypeError: "foo" is read-only

顶层对象的属性

  1. var敕令和function敕令声明的全局变量是顶层对象的属性,即顶层对象的属性与全局变量是等价的。

  2. let敕令、const敕令、class敕令声明的全局变量,不属于顶层对象的属性。

解构赋值

从数组和对象中提取值,对变量举行赋值,这被称为解构。解构赋值左侧定义了要从原变量中掏出什么变量。解构赋值许可,等号左侧的情势当中,不安排任何变量名。

解构赋值的划定规矩是,只需等号右侧的值不是对象或数组,就先将其转为对象。因为undefined和null没法转为对象,所以对它们举行解构赋值,都邑报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

数组解构

数组的元素是按序次分列的,变量的取值由它的位置决议。

let [a, b, c] = [1, 2, 3];//从数组中提取值,根据对应位置,对变量赋值

实质上,这类写法属于“情势婚配”,只需等号双方的情势雷同,左侧的变量就会被给予对应的值。假如解构不胜利,变量的值就即是undefined。

不完全解构:等号左侧的情势,只婚配一部份的等号右侧的数组。

let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4

只需某种数据结构具有 Iterator 接口,都能够采纳数组情势的解构赋值。

对象解构

对象解构需变量与属性同名,才取到准确的值。

let { foo,bar } = { foo: "aaa", bar: "bbb" };
//是let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };的简写情势
foo // "aaa"
bar // "bbb"

let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined

假如变量名与属性名不一致,必需写成下面如许。

let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
//foo是婚配的情势,baz才是变量。真正被赋值的是变量baz,而不是情势foo。

以下语句中变量的声明和赋值是一体的

let {foo} = {foo: 1};

let敕令下面一行的圆括号是必需的,不然会报错。因为解析器会将首先的大括号,明白成一个代码块,而不是赋值语句。

let foo;
({foo} = {foo: 1}); // 胜利

经由过程解构能够无需声明来赋值一个变量。

({a, b} = {a: 1, b: 2})
//等同于
var {a, b} = {a: 1, b: 2}

数组实质是特别的对象,因而能够对数组举行对象属性的解构。

let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;//方括号这类写法,属于“属性名表达式”
first // 1
last // 3

字符串解构

字符串也能够解构赋值。这是因为此时,字符串被转换成了一个相似数组的对象。

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

相似数组的对象都有一个length属性,因而还能够对这个属性解构赋值。

let {length : len} = 'hello';
len // 5

数值和布尔值的解构

解构赋值时,假如等号右侧是数值和布尔值,则会先转为对象。

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

//数值和布尔值的包装对象都有toString属性,因而变量s都能取到值.

函数参数的解构赋值

函数的参数也能够运用解构赋值。

function add([x, y]){
  return x + y;
}

add([1, 2]); // 3

上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解组成变量x和y。关于函数内部的代码来讲,它们能感遭到的参数就是x和y。

默许值

解构赋值许可指定默许值。假如默许值是一个表达式,那末这个表达式是惰性求值的,即只要在用到的时刻,才会求值。

let [x, y = 'b'] = ['a']; // x='a', y='b'

var {x, y = 5} = {x: 1};
x // 1
y // 5

ES6 内部运用严厉相称运算符(===),推断一个位置是不是有值。所以,假如一个数组成员或对象的属性值不严厉即是undefined,默许值是不会见效的

let [x = 1] = [undefined];
x // 1

let [x = 1] = [null];
x // null
//数组成员是null,默许值就不会见效,因为null不严厉即是undefined。

var {x = 3} = {x: null};
x // null

函数参数的解构也能够运用默许值。以下两种写法不一样。

function move({x = 0, y = 0} = {}) {
  return [x, y];
}
move({x: 3}); // [3, 0]
//为变量x和y指定默许值
function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}
move({x: 3}); // [3, undefined]
//为函数move的参数指定默许值

情势不能运用圆括号

只要赋值语句非情势部份,能够运用圆括号。

({ p: (d) } = {}); // 准确
let [(a)] = [1];//报错   因为变量声明语句中,不能带有圆括号。
function f([(z)]) { return z; }// 报错 因为函数参数也属于变量声明,因而不能带有圆括号。


[(b)] = [3]; // 准确 因为情势是取数组的第一个成员,跟圆括号无关。
([a]) = [5]; //报错 因为赋值语句中,不能将全部情势,或嵌套情势中的一层,放在圆括号当中。

Symbol

ES6 引入了一种新的原始数据范例Symbol,示意举世无双的值。Symbol值经由过程Symbol函数天生。对象的属性名如今能够有两种范例,一种是本来就有的字符串,另一种就是新增的 Symbol 范例。通常属性名属于 Symbol 范例,就都是举世无双的,能够保证不会与其他属性名发生争执。

注重,Symbol函数前不能运用new敕令,不然会报错。这是因为 Symbol 值不是对象,所以也不能增加属性。基本上,它是一种相似于字符串的数据范例。Symbol函数能够接收一个字符串作为参数,示意对 Symbol 实例的形貌。假如 Symbol 的参数是一个对象,就会挪用该对象的toString要领,将其转为字符串,然后才天生一个 Symbol 值。

var s1 = Symbol('foo');
var s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

注重,Symbol函数的参数只是示意对当前 Symbol 值的形貌,因而雷同参数的Symbol函数的返回值是不相称的

// 没有参数的状况
var s1 = Symbol();
var s2 = Symbol();

s1 === s2 // false

// 有参数的状况
var s1 = Symbol('foo');
var s2 = Symbol('foo');

s1 === s2 // false

Symbol 值不能与其他范例的值举行运算,会报错。然则,Symbol 值能够显式转为字符串或布尔值,然则不能转为数值。

var sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string

String(sym) // 'Symbol(My symbol)'
Boolean(sym) // true
Number(sym) // TypeError

作为属性名的 Symbol

因为每个 Symbol 值都是不相称的,因而用于对象的属性名时,就可以保证不会涌现同名的属性。

var mySymbol = Symbol();

// 第一种写法
var a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
var a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都获得一样效果
a[mySymbol] // "Hello!"

运用 Symbol 值定义属性时,Symbol 值必需放在方括号当中。该属性还是公然属性。因而,Symbol 值作为对象属性名时,不能用点运算符。

var mySymbol = Symbol();
var a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"

上面代码中,因为点运算符背面老是字符串,所以不会读取mySymbol作为标识名所指代的谁人值,致使a的属性名实际上是一个字符串,而不是一个 Symbol 值。

Symbol 范例还能够用于定义一组常量,保证其值得唯一。Symbol 作为属性名,该属性不会出如今for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。可运用Object.getOwnPropertySymbols要领猎取对象的一切 Symbol 属性名。

Symbol.for(),Symbol.keyFor()

Symbol.for():会先搜检给定的key是不是已存在,假如不存在才会新建一个值,该值会被登记在全局环境中供搜刮。
Symbol():没有登记轨制,因而每次挪用都邑返回一个差别的值。

Symbol.for("bar") === Symbol.for("bar")
// true

Symbol("bar") === Symbol("bar")
// false


var s1 = Symbol("foo");
var s2=Symbol.for("foo");
s1===s2 //false

Symbol.keyFor():返回一个已登记的 Symbol 范例值的key。

var s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

var s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined 变量s2属于未登记的Symbol值,所以返回undefined。

Symbol.for为Symbol值登记的名字,是全局环境的,能够在差别的 iframe 或 service worker 中取到同一个值。

内置的Symbol值

  1. Symbol.hasInstance:对象的Symbol.hasInstance属性,指向一个内部要领。当其他对象运用instanceof运算符,推断是不是为该对象的实例时,会挪用这个要领。

  2. Symbol.isConcatSpreadable:对象的Symbol.isConcatSpreadable属性即是一个布尔值,示意该对象运用Array.prototype.concat()时,是不是能够睁开。数组的默许行动是能够睁开。相似数组的对象的默许行动是不能够睁开。Symbol.isConcatSpreadable属性即是true或undefined时,都是能够睁开的。关于一个类来讲,Symbol.isConcatSpreadable属性必需写成实例的属性。

    let arr1 = ['c', 'd'];
    ['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
    arr1[Symbol.isConcatSpreadable] // undefined
    
    let obj = {length: 2, 0: 'c', 1: 'd'};
    ['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']
    obj[Symbol.isConcatSpreadable] = true;
    ['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']
  3. Symbol.species:对象的Symbol.species属性,指向当前对象的组织函数。制造实例时,默许会挪用这个要领,即运用这个属性返回的函数看成组织函数,来制造新的实例对象。

  4. Symbol.match:对象的Symbol.match属性,指向一个函数。当实行str.match(myObject)时,假如该属性存在,会挪用它,返回该要领的返回值。

    String.prototype.match(regexp)
    // 等同于
    regexp[Symbol.match](this)
  5. Symbol.replace:对象的Symbol.replace属性,指向一个要领,当该对象被String.prototype.replace要领挪用时,会返回该要领的返回值。

    String.prototype.replace(searchValue, replaceValue)
    // 等同于
    searchValue[Symbol.replace](this, replaceValue)
  6. Symbol.search:对象的Symbol.search属性,指向一个要领,当该对象被String.prototype.search要领挪用时,会返回该要领的返回值。

    String.prototype.search(regexp)
    // 等同于
    regexp[Symbol.search](this)
  7. Symbol.split:对象的Symbol.split属性,指向一个要领,当该对象被String.prototype.split要领挪用时,会返回该要领的返回值。

    String.prototype.split(separator, limit)
    // 等同于
    separator[Symbol.split](this, limit)
  8. Symbol.iterator:对象的Symbol.iterator属性,指向该对象的默许遍历器要领。

  9. Symbol.toPrimitive:对象的Symbol.toPrimitive属性,指向一个要领。该对象被转为原始范例的值时,会挪用这个要领,返回该对象对应的原始范例值。

  10. Symbol.toStringTag:对象的Symbol.toStringTag属性,指向一个要领。在该对象上面挪用Object.prototype.toString要领时,假如这个属性存在,它的返回值会出如今toString要领返回的字符串当中,示意对象的范例。也就是说,这个属性能够用来定制[object Object]或[object Array]中object背面的谁人字符串。

  11. Symbol.unscopables:对象的Symbol.unscopables属性,指向一个对象。该对象指定了运用with关键字时,哪些属性会被with环境消除。

参考自:ECMAScript 6 入门

    原文作者:lijinxieyang
    原文地址: https://segmentfault.com/a/1190000009432661
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞