大家要是看不惯这边的排版,可以去我的语雀看看,语雀真的绝
https://www.yuque.com/isremya/vqgp35
let 和 const 命令
- let声明的变量只在它所在的代码块有效。
- 变量 i 是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。
- 变量 i 是let声明的,当前的 i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量
- 另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc
- ES6 明确规定,如果区块中存在let和 const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。即不存在变量提升。
- 暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
- let不允许在相同作用域内,重复声明同一个变量。
function func(arg) { let arg; //不能在函数内部重新声明参数。 } func() // 报错 function func(arg) { { let arg; } } func() // 不报错
- 允许在块级作用域内声明函数。
- 函数声明类似于 var ,即会提升到全局作用域或函数作用域的头部。
- 同时,函数声明还会提升到所在的块级作用域的头部。
// 块级作用域内部的函数声明语句,建议不要使用
{ let a = 'secret'; function f() { return a; } }
// 块级作用域内部,优先使用函数表达式
{ let a = 'secret'; let f = function () { return a; }; }
const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
- ES6 一共有 6 种声明变量的方法。var命令和function 命令。let和const命令,import 命令和class命令。
- ES6 一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const'命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
- 全局环境中,this会返回顶层对象。但是,Node 模块和 ES6 模块中,this返回的是当前模块。
- 函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this 会指向顶层对象。但是,严格模式下,这时this 会返回 undefined。
变量的解构赋值
- ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
- 如果解构不成功,变量的值就等于 undefined。
- 数组的元素是按次序排列的,变量的取值由它的位置决定;
- 而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" foo // error: foo is not defined //上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。 const node = { loc: { start: { line: 1, column: 5 } } }; let { loc, loc: { start }, loc: { start: { line }} } = node; line // 1 loc // Object {start: Object} start // Object {line: 1, column: 5} // 报错 let {foo: {bar}} = {baz: 'baz'}; //whywhywhy???
- 对象的解构也可以指定默认值。
var {x = 3} = {}; x // 3 var {x, y = 5} = {x: 1}; x // 1 y // 5 var {x: y = 3} = {}; y // 3 var {x: y = 3} = {x: 5}; y // 5 var { message: msg = 'Something went wrong' } = {}; msg // "Something went wrong" // 错误的写法 let x; {x} = {x: 1}; // SyntaxError: syntax error // 正确的写法 let x; ({x} = {x: 1}); //只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
- 字符串也可以解构赋值。
const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o"
- 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null
无法转为对象,所以对它们进行解构赋值,都会报错。
- 函数也可以解构赋值。
[[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ]
- 不得使用圆括号的情况
- 变量声明语句
- 函数参数
- 赋值语句的模式
//以下三种模式全部报错 let [(a)] = [1]; let {x: (c)} = {}; function f([(z)]) { return z; } ({ p: a }) = { p: 42 }; ([a]) = [5];
- 只有赋值语句的非模式部分,可以使用圆括号。
[(b)] = [3]; // 正确 ({ p: (d) } = {}); // 正确 [(parseInt.prop)] = [3]; // 正确
- 变量的解构赋值用途
- 交换变量的值
- 从函数返回多个值
- 函数如果要返回多个值,只能将它们放在数组或对象里返回。
- 函数参数的定义
- 方便地将一组参数与变量名对应起来。
- 提取 JSON 数据
- 函数参数的默认值 review
- 遍历 Map 结构
- 任何部署了 Iterator 接口的对象,都可以用 for...of
循环遍历。
- Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。
- 输入模块的指定方法 review
//交换变量 let x = 1; let y = 2; [x, y] = [y, x]; // 返回一个对象 function example() { return { foo: 1, bar: 2 }; } let { foo, bar } = example(); //提取 JSON 对象中的数据 let jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData; //遍历 Map 结构 const map = new Map(); map.set('first', 'hello'); map.set('second', 'world'); for (let [key, value] of map) { console.log(key + " is " + value); } // first is hello // second is world // 获取键名 for (let [key] of map) { // ... } // 获取键值 for (let [,value] of map) { // ... }
字符串的拓展
模板字符串
- 用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
- 如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
- 所有模板字符串的空格和换行,都是被保留的,可以使用
trim
方法消除它。 - 模板字符串中嵌入变量,需要将变量名写在
${}
之中。 - 模板字符串之中还能调用函数。
- 如果模板字符串中的变量没有声明,将报错。
$('#result').append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `); let greeting = `\`Yo\` World!`; $('#list').html(` <ul> <li>first</li> <li>second</li> </ul> `.trim()); function fn() { return "Hello World"; } `foo ${fn()} bar` // foo Hello World bar
实例:模板编译看不懂
标签模板 看不懂
字符串的新增方法
String.raw()
- 该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串
- 如果原字符串的斜杠已经转义,那么 String.raw()
会进行再次转义。
String.raw`Hi ${2+3}!` // 实际返回 "Hi\n5!",显示的是转义后的结果 "Hi 5!" //why why why? // `foo${1 + 2}bar` // 等同于 String.raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"
确定一个字符串是否包含在另一个字符串中
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
repeat()
返回一个新字符串,表示将原字符串重复n
次
参数如果是小数,会被取整。
参数是负数或者Infinity,会报错。
参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。
参数NaN
等同于 0。
参数是字符串,则会先转换成数字。
'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello" 'na'.repeat(2.9) // "nana" 'na'.repeat(-0.9) // "" 'na'.repeat(NaN) // "" //why why why? 'na'.repeat('na') // "" 'na'.repeat('3') // "nanana"
padStart(),padEnd()
- 如果某个字符串不够指定长度,会在头部或尾部补全。padStart() 用于头部补全,padEnd() 用于尾部补全。
- 如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。
- 如果省略第二个参数,默认使用空格补全长度。
'x'.padStart(4, 'ab') // 'abax' 'x'.padEnd(5, 'ab') // 'xabab' 'xxx'.padStart(2, 'ab') // 'xxx' 'x'.padStart(4) // ' x' 'x'.padEnd(4) // 'x '
trimStart(),trimEnd()
- padStart() 消除字符串头部的空格,trimEnd() 消除尾部的空格。
- 它们返回的都是新字符串,不会修改原始字符串。
const s = ' abc '; s.trim() // "abc" s.trimStart() // "abc " s.trimEnd() // " abc"
matchAll()
- 返回一个正则表达式在当前字符串的所有匹配