目录
1.块级绑定
新增加了两个重要的 JavaScript 关键字: let 和 const。
let 声明的变量只在 let 命令所在的代码块内有效。
const 声明一个只读的常量,一旦声明,常量的值就不能改变。
let和const变量不会被提升,只有先声明才能够访问。
暂时性死区
ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错。
let和const都有自己的块级作用域。
注:通常情况下,如果变量不需要被重新赋值,应该使用 const
声明常量;如果变量需要被重新赋值,应该使用 let
声明可变变量。
块级作用域
正常情况下,JS 引擎在编译代码阶段会生成全局执行上下文和函数执行上下文。这其中,每个上下文又分为了两个部分:变量环境和词法环境。
var a = 0
let b = 1
function foo() {
var a = 1
let b = 2
if (true) {
let b = 3
console.log(a, b)
}
}
foo() // 1, 3
- 使用 var 关键字声明的变量、及函数声明,会被放入变量环境中
- 使用 let 及 const 关键字声明的变量和常量会被放入词法环境中
- 词法环境内部也类似于一个栈结构,每一个块结构(即有一对大括号,如条件句、循环等)内的变量和常量会单独保存(使用 var 关键字声明的不会)。
- 所以,如上面的例子,函数 foo 的词法环境里,有两个区域。下面的区域保存了函数体内使用 let 声明的变量,上面的区域保存了 if 语句中使用 let 声明的变量。所以即使它们都叫做 b,但却是两个不同的变量。
经典的for循环闭包问题可以使用let 解决
for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i);
})
}
// 输出十个 10
for (let j = 0; j < 10; j++) {
setTimeout(function(){
console.log(j);
})
}
// 输出 0123456789
全局绑定
当在全局作用域上使 用 var 时,它会创建一个新的全局变量,并成为全局对象(在浏览器中是 window )的一 个属性,然而若你在全局作用域上使用 let 或 const ,虽然在全局作用域上会创建新的绑定,但不 会有任何属性被添加到全局对象上。
// 在浏览器中
let RegExp = "Hello!"; console.log(RegExp); // "Hello!"
console.log(window.RegExp === RegExp); // false
const ncz = "Hi!"; console.log(ncz); // "Hi!" console.log("ncz" in window); // false
2. 解构:更方便的数据访问
解构赋值是对赋值运算符的扩展,是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。 解构的源,解构赋值表达式的右边部分。解构的目标,解构赋值表达式的左边部分
包括数组解构,参数解构,对象解构
数组解构
// 基本
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
// 嵌套
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
// 剩余参数
let [a, ...b] = [1, 2, 3];
//a = 1
//b = [2, 3]
对象解构
// 基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
// 剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}
// 解构默认值
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;
参数解构
function setObj(name, type, { age = 123 }) {
console.log(name, type, age);
}
setObj(1, 1, { age: 345 }); // 正常
// setObj(1, 1);// 报错
参数解构有一个怪异点:默认情况下调用函数时未给参数解构传值会抛出错误。
可以给解构的参数提供默认值来处理这种行为
function setObj(name, type, { age = 123 } = {}) {
console.log(name, type, age);
}
// setObj(1, 1, { age: 345 });
setObj(1, 1);
3.符号-Symbol
Symbol 是 ES6 新推出的一种基本类型,它表示独一无二的值
const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false
const s1 = Symbol('debug');
const str = 'debug';
const s2 = Symbol('debug');
console.log(s1 === str); // false
console.log(s1 === s2); // false
console.log(s1); // Symbol(debug)
console.log(typeof s1); // symbol
特点:
- Symbol()不是一个完整的构造函数,因此不能通过new Symbol() 来创建(通过 new实例化的结果是一个 object对象,而不是原始类型的 symbol)
- Symbol不能进行隐式类型转换,但可以显式转为字符串;不能转化为数字,但可以转化为布尔值
- 由于Symbol值的唯一性,意味着它可以作为对象的属性名,避免出现相同属性名,产生某一个属性被改写或覆盖的情况。
注意:Symbol作为属性名
1、不能通过点运算符访问,需要通过方括号的形式访问。
2、不能通过for...in、for...of遍历,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是它也不是私有属性,可以通过Object.getOwnPropertySymbols()和 Reflect.ownKeys()方法获取对象Symbol 属性名。
let sym3 = Symbol('test');
let obj={name: 'lin', [sym3]: 'foo'};
obj[sym3]; //"foo"
JSON.stringify(obj); //"{"name":"lin"}"
Object.keys(obj); //["name"]
Object.getOwnPropertyNames(obj); //["name"]
for (let key in obj) {
console.log(key); //name
}
Object.getOwnPropertySymbols(obj); //[Symbol(test)]
为什么属性两边加上[]
?
这是ES6引入的一个属性名表达式(计算属性),用于动态获取属性名,讲白就是你可放变量名在里面,之后通过变量去调
方法:
Symbol有两个方法,分别是Symbol.for()和Symbol.keyFor()
1、Symbol.for()
用于将描述相同的Symbol变量指向同一个Symbol值
let a1 = Symbol.for('a');
let a2 = Symbol.for('a');
a1 === a2 // true
typeof a1 // "symbol"
typeof a2 // "symbol"
let a3= Symbol("a");
a1 === a3 // false
Symbol()和Symbol.for()的区别:
Symbol()和Symbol.for()都会生成新的Symbol。
Symbol()未在全局环境(Symbol的容器)中注册
而Symbol.for()有在全局环境中注册,所以有注册的相当于就单例了,有注册的话直接就返回了,没注册就新创建,而Symbol()每次都是创建新的。
2、Symbol.keyFor()
Symbol.keyFor()是用来返回一个在全局环境注册过的Symbol的key
注意:未注册的Symbol是无法获取的
let a1 = Symbol.for("a");
Symbol.keyFor(a1); // "a"
let a2 = Symbol("a");
Symbol.keyFor(a2); // undefined
属性:
Symbol.prototype.description用来返回Symbol数据描述
// Symbol()定义的数据
let a = Symbol("acc");
a.description // "acc"
Symbol.keyFor(a); // undefined
// Symbol.for()定义的数据
let a1 = Symbol.for("acc");
a1.description // "acc"
Symbol.keyFor(a1); // "acc"
// 未指定描述的数据
let a2 = Symbol();
a2.description // undefined
description属性和Symbol.keyFor()方法的区别是: description能返回所有Symbol类型数据的描述,而Symbol.keyFor()只能返回Symbol.for()在全局注册过的描述
使用场景
避免属性名覆盖
const name = Symbol('name');
const obj = {
[name]: 'ClickPaas',
}
模拟private 关键字来声明类的私有方法和私有变量的
const speak = Symbol();
class Person {
[speak]() {
console.log(123)
}
}
let person = new Person()
console.log(person[speak]())
无法在外部创建出一个相同的 speak,所以就无法调用该方法
https://juejin.cn/post/6844904056465408007
4. 字符串
模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。
let string = `Hello'\n'world`;
console.log(string);
// "Hello'
// 'world"
字符串插入变量和表达式
变量名写在 ${} 中,${} 中可以放入 JavaScript 表达式。
let name = "Thomas";
let age = 25;
let info = `My Name is ${name},I am ${age+1} years old next year.`
console.log(info);
// My Name is Mike,I am 28 years old next year.