Hoisting 机制
在函数作用域或全局作用域中通过关键字 var 生命的变量,无论实际上是在哪里声明的,都会被当成在当前作用域顶部声明的变量,这就是我们常说的提升(Hoisting)机制。
块级声明
块级声明用于声明在指定块的作用域之外无法访问的变量。块级作用域(亦被称为词法作用域)存在于:
let:
const:
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
数组解构:
let [a, b, c] = [123, 456, 789]
console.log(a, b, c) 123 456 789
对象解构:
let { name, age } = { name: 'Jack', age: 18 }
console.log(name, age) Jack 18
函数参数解构:
function f (p1, { p2 = 'aa', p3 = 'bb' }) {
console.log(p1, p2, p3)
}
f('p1', { p2: 'p2' }) p1 p2 bb
字符串解构:
let [a, b, c, d, e] = 'hello'
console.log(a, b, c, d, e) h e l l o
实用方法:
includes(String):返回布尔值,表示是否找到了参数字符串。
startsWith(String):返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith(String):返回布尔值,表示参数字符串是否在源字符串的尾部。
repeat(Number):repeat方法需要指定一个数值,然后返回一个新字符串,表示将原字符串重复Number次。
模板字符串:
let basket = { count: 5, onSale: true }
$('#result').append(`
There are ${basket.count} items
in your basket, ${basket.onSale}
are on sale!
`);
模板字符串(template string)是增强版的字符串,用反引号(`)标识
它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中
模板字符串中嵌入变量,需要将变量名写在 ${}
之中
方法:
Array.from() 将一个伪数组转为一个真正的数组
实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,
以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。
Array.of() Array.of方法用于将一组值,转换为数组
这个方法的主要目的,是弥补数组构造函数Array()的不足。
因为参数个数的不同,会导致Array()的行为有差异。
find() 查找数组中某个元素
findIndex() 查找数组中某个元素的索引下标
includes() 返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似
实例方法:
ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。可以用 for...of
循环进行遍历,唯一的区别是 keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。
entries()
keys()
values()
[[Construct]]
方法,所以不能被当做构造函数let reflect = value => value;
// 实际上相当于:
let reflext = function(value) {
return value;
};
console.log(+0 == -0) // true
console.log(+0 === -0) // true
console.log(Object.is(+0, -0)) // false
console.log(NaN == NaN) // false
console.log(NaN === NaN) // false
console.log(Object.is(NaN, NaN)) // true
String.raw`Hi\n${2+3}!`; // 返回 "Hi\\n5!"
String.raw`Hi\\n` === "Hi\\\\n" // true
let msg = "Hello World!"
console.log(msg.startsWidth("Hello")) // true
console.log(msg.endsWidth("!")) // true
console.log(msg.includes("o")) // true
'x'.repeat(3) // "xxx"
'na'.repeat(NaN) // ""
在 ES5 及早期版本中,语言包含5中原始类型:string、number、boolean、null 和 undefined。ES6 引入了第6种原始类型:Symbol。
let firstName = Symbol()
let person = {}
person[firstName] = "Nico"
console.log(person[firstName]) // "Nico"
let firstName = Symbol("first name")
let person = {}
person[firstName] = "Nico"
console.log("first name" in person) // false
console.log(person[firstName]) // "Nico"
console.log(firstName) // "Symbol(first name)"
let symbol = Symbol("test symbol")
console.log(typeof symbol) // symbol
// 使用一个可计算对象字面量属性
let person = {
[firstName]: "Nico"
}
// 将属性设置为只读
Object.defineProperty(person, firstName, { writable: false })
let lastName = Symbol("last name")
Object.defineProperties(person, {
[lastName]: {
value: "Zakas",
writable: false
}
})
console.log(person[firstName])
console.log(person[lastName])
// 修改失败,只读
person[lastName] = "jack"
console.log(person[lastName]) // Zakas
let uid = Symbol.for("uid")
let object = {}
object[uid] = "12345"
console.log(object[uid]) // 123456
console.log(uid) // Symbol(uid)
let uid = Symbol.for("uid")
console.log(Symbol.keyFor(uid)) // "uid"
let uid2 = Symbol.for("uid")
console.log(Symbol.keyFor(uid2)) // "uid"
let uid3 = Symbol("uid")
console.log(Symbol.keyFor(uid3)) // undefined
uid 和 uid2 都返回了 “uid” 这个键,而在 Symbol 全局注册表中不存在 uid3 这个 Symbol,也就是不存在与之有关系的键,所以最终返回 undefined在数组解构语法中,我们通过值在数组中的位置进行选取,且可以将其存储在任意变量中,未显式声明的元素都会直接被忽略。并且在这个过程中,数组本身不会发生任何变化。
let colors = ["green", "red", "blue"]
let [ firstColor, secondColor ] = colors
console.log(firstColor) // green
console.log(secondColor) // red
在解构模式中,也可以直接省略元素,只为感兴趣的元素提供变量名。
let colors = ["green", "red", "blue"]
let [ , , thirdColor ] = colors
console.log(thirdColor) // blue
数组解构也可用于赋值上下文,但不需要用小括号包裹表达式,这一点与对象结构的约定不同
let colors = ["green", "red", "blue"],
firstColor = "black",
secondColor = "purple"
;[ firstColor, secondColor ] = colors
console.log(firstColor) // green
let node = {
type: "Identifer",
name: "foo"
}
let { type, name } = node
console.log(type) // Identifer
console.log(name) // foo
// 语法错误!
var { type, name }
// 语法错误!
let { type, name }
// 语法错误!
const { type, name }
let node = {
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 4
}
},
range: [0, 3]
}
let {
loc: { start },
range: [ startIndex ]
} = node
console.log(start.line) // 1
console.log(start.column) //1
console.log(startIndex) // 0
class Person {
// 等价于 Person 的构造函数
constructor(name) {
this.name = name
}
// 等价于 Person.prototype.sayName
sayName() {
console.log(this.name)
}
}
let person = new Person("Nico")
person.sayName() // "Nico"
console.log(person instanceof Person) // true
console.log(person instanceof Object) // true
console.log(typeof Person) // "function"
console.log(typeof Person.prototype.sayName) // "function"
函数声明可以被提升,而类声明与 let 声明类似,不能被提升;真正执行声明语句之前,他们会一直存在与临时死区中。
类声明中的所有代码将自动运行在严格模式下,而且无法强行让代码脱离严格模式执行。
在自定义类型中,需要通过 Object.defineProperty() 方法手工指定某个方法为不可枚举;而在类中,所有的方法都是不可枚举的。
每个类都有一个名为 [[Construct]] 的内部方法,通过关键字 new 调用那些不含 [[Construct]] 的方法会导致程序抛出错误。
使用除关键字 new 以外的方式调用类的构造函数会导致程序抛出错误。
在类中修改类名会导致程序报错
// 等价于 Person
let Person2 = (function() {
"use strict"
const Person2 = function (name) {
// 确保通过关键字 new 调用该函数
if (typeof new.target === "undefined") {
throw new Error("必须通过关键字 new 调用构造函数")
}
this.name = name
}
Object.defineProperty(Person2.prototype, "sayName", {
value: function() {
// 确保不会通过关键字 new 调用该方法
if (typeof new.target !== "undefined") {
throw new Error("不可使用关键字 new 调用该方法")
}
console.log(this.name)
},
// enumerable 可枚举的; configurable 可配置的
enumerable: false,
writable: true,
configurable: true
})
return Person2
}())
class Foo {
constructor() {
Foo = "bar" // 执行时会抛出错误
}
}
const foo = new Foo() // 报错啦
console.log(Foo) // [Function: Foo]
// 但是类声明结束后就可以修改
Foo = "baz"
console.log(Foo) // "baz"
let Person = class {}
let Person = class Person2 {}
console.log(typeof Person) // function
console.log(typeof Person2) // undefined
// 等价于 Person
let Person = (function() {
"use strict"
const Person2 = function (name) {
// 确保通过关键字 new 调用该函数
if (typeof new.target === "undefined") {
throw new Error("必须通过关键字 new 调用构造函数")
}
this.name = name
}
Object.defineProperty(Person2.prototype, "sayName", {
value: function() {
// 确保不会通过关键字 new 调用该方法
if (typeof new.target !== "undefined") {
throw new Error("不可使用关键字 new 调用该方法")
}
console.log(this.name)
},
// enumerable 可枚举的; configurable 可配置的
enumerable: false,
writable: true,
configurable: true
})
return Person2
}())
Es6 中的 Map 类型是一种存储着许多键值对的有序列表,其中的简明和对象的值支持所有的数据类型。
let map = new Map()
map.set("title", "Understanding ES6")
map.set("year", 2019)
console.log(map.get("title")) // Understanding ES6
console.log(map.get("year")) // 2019
let map = new Map()
map.set("title", "Understanding ES6")
map.set("year", 2019)
console.log(map.get("title")) // Understanding ES6
console.log(map.get("year")) // 2019
console.log(map.size) // 2
console.log(map.has("title")) // true
map.clear()
console.log(map.size) // 0
let map = new Map([["name", "Nichlas"], ["age", 25]])
map.forEach(function(value, key, ownerMap) {
console.log(key + '--' + value) // name--Nichlas
console.log(ownerMap) // Map { 'name' => 'Nichlas', 'age' => 25 }
console.log(ownerMap === map) // true
})
ES6 中的 Weak Map 类型是一种存储着许多键值对的无序列表,列表的键名必须是非 null 类型的对象,键名对应的值则可以是任意类型。
let map = new WeakMap(),
element = document.querySelector(".element")
map.set(element, "cyan")
let value = map.get(element)
console.log(value) // "cyan"
// 移除 element 元素
element.parentNode.removeChild(element)
element = null
var fs = require("fs")
var p1 = new Promise(function(resolve, reject) {
fs.readFile('./demo.txt', 'utf8', function(err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
p1.then(function(contents) {
console.log(contents)
})
p1.then(null, function (err) {
console.log(err)
})
promise.catch(function(err) {
// 拒绝
console.error(err.message)
})
// 与下调用相同
promise.then(null, function(err) {
// 拒绝
console.log(err.message)
})