let 和 const 是 ES6 新增的2个声明符,let 用于声明变量,const 用于声明常量。
{
var a = 10;
let b = 20;
const c = 30;//常量:不能被修改的量
c = 40;
//Uncaught TypeError:Assignment to constant variable.(未捕获的类型错误:赋值给常量变量)
}
console.log(a); // 10
console.log(b); // b is not defined
console.log(b = c); // c is not defined
在一个大括号中用 let 和 const 声明的变量在外部不可访问了,每个大括号都是独立的作用域。
用 var 声明一个变量是存在变量提升的,这是JS中的一个缺陷所在, 但是现在的 let 不存在变量提升。
ES6 规定在某个区块中, 一旦用 let 或 const 声明一个变量,那么这个区块就变成块级作用域,用 let 或者 const 声明的变量即为该区块绑定, 该变量不受任何变量影响。 在该变量使用 let 声明前不可以用。在语法上,我们叫这种情况为:暂时性死区 (temporal dead zone,简称 TDZ)。
使用 const 声明的变量为常量,常量不可以被修改。
console.log(Math.PI); // 3.141592653589793
Math.PI = 4;
console.log(Math.PI); // 3.141592653589793
const arr = [2,3,4,5];
// arr = [3,4,4,6];
// 不能修改数组或对象的引用,但是可以通过 API 去修改内部结构
arr.push(6,7);
console.log(arr); // [2, 3, 4, 5, 6, 7]
扩展运算符(spread)为三个点号(…),功能是把数组或类数组对象展开成一系列用逗号隔开的值。
var arr = [1,2,3,4,5];
console.log(arr); // Array(5)
console.log(...arr); // 1 2 3 4 5 // console.log 不会显示逗号,中间其实是有逗号的
var foo = function(a, b, c) {
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
}
var arr = [1, 2, 3];
// 传统写法
foo(arr[0], arr[1], arr[2]);
// 使用扩展运算符
foo(...arr);
剩余运算符(rest)也是三个点号(…),不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组。
// 主要用于不定参数,所以 ES6 开始可以不再使用 arguments 对象
var bar = function(a, ...args) {
console.log(a); // 1
console.log(args); // [ 2, 3, 4 ]
}
bar(1, 2, 3, 4);
字符串的 Unicode 表示,规则为\u + 四位十六进制。
console.log("\u0061"); // a
这种新的字符表示方式只能表示 \u0000 ~ \uffff 之间的字符,如果超出范围必须用双字节表示。
console.log("\uD842\uDFB6"); //
如果不按照规则书写,例如 console.log("\uD842\uDFB69"),这个9是多余字符,那么则认为这段字符是 \uD842\uDFB6 + 9 所以打印结果是 ₻9。
如果想要一次性表示超出范围的字符那么我们可以使用 {} 来表示。
console.log("\u20BB9"); // ₻9
console.log("\u{20BB9}"); //
ES6 中存在一种新的字符串, 这种字符串是以 反引号(波浪线上的那个字符 ` )括起来表示的。
通常拼接一个带有标签的字符串, 是用这样的方式:
variate + " 这是一个文字" + obj.name + " " + variate
但是有了 ES6 模板字符串一切都变得非常简单了,用 ${变量名} 括住变量让拼接变得非常容易:
`${variate} 这是一个文字${obj.name}${variate} `
在字符串中使用反引号的时候我们需要进行转义:
console.log(`\`Yo\` World!`); // `Yo` World!
模板还可以调用函数:
function fn() {
return "Hello World";
}
console.log(`foo ${fn()} bar`); // foo Hello World bar
一种新的遍历方式,遍历数组的所有元素。
tip:可以用于遍历字符串:
var s = "abc";
for(let b of s) {
console.log(b); // "a" "b" "c"
}
for of 不能用于遍历对象。
一个常规函数如下:
const test = function (x) {
return x + 2;
}
使用箭头函数:
const test = (x) => {
return x + 2;
};
const 函数名 = (参数列表) => { 函数体 }。
如果箭头函数只有一个形参,那么可以省略小括号。
const test = x => {
return x + 2;
};
如果函数体只有一条语句,那么可以省略大括号:
const foo = str => console.log(str);
如果只有一条语句,并且这条语句就是return语句,可以省略 return 关键字:
const test = x => x + 2;
箭头函数会自动绑定 this(箭头函数没有自己的this)。
缺陷:
(1) 箭头函数是不能 new 的,它的设计初衷就跟构造函数不太一样;
(2) 箭头函数如果要返回一个 JSON 对象,必须用小括号包起来。如 var test = () => ({ id: 3, val: 20 })。
document.querySelector("button").onclick = function () {
setTimeout(() => {
console.log(this); //
},1000);
};
箭头函数和普通函数的区别:
(1) 写法不一样,箭头函数更简洁;
(2) 普通函数存在变量提升;
f(); // hello
function f(){
console.log("hello");
}
g(); //报错:Uncaught ReferenceError: g is not defined
const g = () => console.log("hello");
(3) this指向不同,箭头函数没有自己的 this,指向外层作用域的 this;
(4) 箭头函数没有自己的 arguments,箭头函数的 arguments 指向它的父级函数所在作用域的arguments;
(5) 箭头函数不能作为构造函数;
(6) 箭头函数没有new.target。
// 对象解构
var obj = {name:"lisi", age:80, gender: "female"};
var {age, name, gender="male"} = obj;
console.log(age, name, gender); // 80 'lisi' 'female'
var json = [
{name:"lisi", age:80, gender: "female"},
{name:"liwu", age:70, gender: "male"},
{name:"liliu", age:60, gender: "female"}
]
var [{age},{name},{gender}] = json;
// var {name} = b;
console.log(name, age, gender); // liwu 80 female
// 数组解构
let [a, b, c] = [1, 2, 3];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
解构赋值就是把数据结构分解,然后给变量进行赋值。
如果解构不成功,变量跟数值个数不匹配的时候,变量的值为 undefined。
数组解构用中括号([])包裹,多个变量用逗号隔开,对象解构用花括号({})包裹,多个变量用逗号隔开。
利用解构赋值能够方便的去取数组的元素,对象的属性和方法。
symbol 类型是第6种基本数据类型。
Symbol 函数会生成一个唯一的值。可以理解为 symbol 类型跟字符串是接近的。但每次生成唯一的值,也就是每次都不相等,至于它等于多少,并不重要。这对于一些字典变量比较有用。
var s1 = Symbol();
var s2 = Symbol();
var s3 = Symbol("abc");
var s4 = Symbol("abc");
//s1不等于s2
//s3不等于s4
在通过 Symbol 生成独一无二的值时可以设置一个标记,这个标记仅仅用于区分, 没有其它任何含义。
注意点:
(1) symbol 是基本数据类型!!!!不要加 new 哦;
(2) 后面括号可以传入一个字符串,只是一个标记,方便我们阅读,没有任何意义;
(3) 类型转化的时候不可转化为数值;
// 只能转化为字符串和布尔值
const name = Symbol('name');
console.log(String(name)); // Symbol(name)
console.log(Boolean(name)); // true
console.log(Number(name)); // 报错:Cannot convert a Symbol value to a number
(4) 不能做任何运算;
(5) Symbol 生成的值作为属性或者方法的时候,一定要保存下来,否则后续无法使用;
let name=Symbol('name');
let obj={
// name:'lnj',
[Symbol('name')]:'lbj'
};
console.log(obj.name); // 访问不到,因为 [Symbol('name')] 又是一个新的值,和上面的 name 不是同一个
(6) for循环遍历对象的时候是无法遍历出 symbol 的属性和方法的,需要使用
Object.getOwnPropertySymbols。
let name=Symbol('name');
let obj={
[name]:'lnj',
age:12,
teacher:'wyx'
};
for (let key in obj) {
console.log(key); //只能打印出 age 和 teacher
}
//这个方法可以单独取出Symbol(name)
console.log(Object.getOwnPropertySymbols(obj));
Symbol的使用场景:
对象的属性默认是字符串,所以不加引号(‘’,"")也可以,如果需要类型为 symbol,需要使用中括号([])括起来。
不可以用点(.)来访问,因为点运算符后面总是字符串。
Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。
// 后面的括号可以给 symbol 做上标记便于识别
let name = Symbol('name');
let say = Symbol('say');
let obj = {
// 如果想使用变量作为对象属性的名称,必须加上中括号,.运算符后面跟着的都是字符串
[name]: 'lnj',
[say]: function () {
console.log('say');
}
};
obj.name = 'it666';
obj[Symbol('name')] = 'it666';
console.log(obj);
魔术字符串:在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。
const gender = {
//这样就说明man就是一个独一无二的值,不用再 man:'man'
man: Symbol(),
woman: Symbol(),
};
function isMan(gender) {
switch (gender) {
case gender.man:
console.log('男性');
break;
case gender.woman:
console.log('女性');
break;
}
}
isMan(gender.man); //男性
由于以 Symbol 值作为键名,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。
注意:symbol 并不能实现真正的私有变量的效果,只是不能通过常规的遍历方法拿到 symbol 类型的属性而已。
对象的遍历方法:
(1) for (let xx in obj): xx 代表 key;
(2) for (let xx of obj):xx 代表 value;
(3) Object.keys(obj): 返回包含 key 的数组;
(4) Object.values(obj): 返回包含 value 的数组;
(5) Object.getOwnPropertyNames():返回包含 key 的数组。
上述的所有方法都是遍历不到 symbol 类型的(注意,是遍历时取不到 symbol,并不是说我们访问不到对象的 symbol 类型)。
可以遍历到 symbol 的方法:
(1) Object.getOwnPropertySymbols:返回对象中只包含 symbol 类型 key 的数组;
(2) Reflect.ownKeys:返回对象中所有类型 key 的数组(包含 symbol)。
Symbol 自带的方法:
(1) Symbol.for(str)
因为 symbol 类型的值都是独一无二的,但有时,我们希望重新使用同一个 Symbol 值,Symbol.for 方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');s1 === s2 // true
(2) Symbol.keyFor()
由于 Symbol 写法没有登记机制,所以每次调用都会返回一个不同的值。
Symbol.keyFor 方法返回一个已登记的 symbol 类型值的 key。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined