本文共 7684 字,大约阅读时间需要 25 分钟。
众所周知,通过 var 声明的变量没有块级作用域,而 let 有。你比如说:
function foo(){ for(var i=0; i<=10; i++){ console.log(i) } console.log(i) // var 的作用域是整个函数,所以哪怕 for 逻辑之外也可以访问到}function bar(){ for(let i=0; i<=10; i++){ console.log(i) // 而 let 的作用域是块,for 之外无法访问 }}
此外,let 不可以相同变量重新声明,而 var 可以:
var foo = 12var foo = 24let bar = 12let bar = 24 // SyntaxError: Identifier 'bar' has already been declared
再外,申明全局变量时,通过 var 申明的变量,会自动成为全局对象 window
的属性,而 let 则不会。
至于 let 与 const,let 用以声明变量,const 用以声明常量。
解构赋值在对象、基本数据类型上均可以使用:
let [a, b] = [12, 13]// 可以据此进行变量的值交换let foo = 10, bar = 12[foo, bar] = [bar, foo]
// 123 -> foo, "baz" -> barlet { id: foo, name: bar} = { id: 123, name: "baz"}let { foo, bar} = { foo: 123, bar: "bar"} // 此时要求变量名为对象属性名称
let [foo = 1, bar = 2] = [undefined, undefined] // 申明时直接初始化,此时最终,foo 为 1,bar 为 2let [foo = 1, bar = 2] = [null, null] // 不同于 undefined,null 可用于赋值,则此时 foo、bar 皆为 null
主要是一系列帮助函数,你比如:
顾名思义,等效于其他语言中诸如 contains 的函数:
"foobar".includes("bar") // true // 特殊地,大家总 includes.("") 总真
用于判断某字符串是否以什么什么开头或结尾:
"foobar".startsWith("foo") // true"foobar".endsWith("bar") // true
用于生成重复的字符串,你比如说:
"8".repeat(3) // "888"
在字符串前添加 “pad”,你比如说:
"8".padStart(3, "8") // "888",两个参数分别为 maxLength、fillString
模板字符串是与传统 CStyle 先占位后赋值不同的另一种实现动态字符串的方式:
let foo = 10086console.log(`${ foo} is 10086`) // "10086 is 10086" // 以单反引号包裹,通过 ${} 组合操作符获取变量的值
与 forEach 不同,map 函数返回 由依次执行所传入匿名函数结果组成的新数组,你比如:
[1,2,3,4].map(i=>i+"handled") // ["1handled", "2handled", "3handled", "4handled"]
与 map 一样,皆可以遍历可迭代对象的诸项,但其返回的数组是 由满足所传入匿名函数(理想状态下,传入的函数应该是一个可以明确返回真假值的函数(不理想时,则总是返回真))的原对象的各项组成,起到了一个过滤的作用,你比如说:
[1,2,3,4].filter(i=>i>2) // [3, 4]
用于判断各元素是否满足某条件,some 当有元素满足条件时返回 true,而 every 需要各元素都满足:
[1,2,3,4].some(i=>i>2) // true[1,2,3,4].every(i=>i>2) // false
用于找到 第一个使得所传函数返回真 的元素,你比如:
let foo = [1,2,3]foo.find(i=>i===2) // 2
用于填充,所谓填充,即用 固定值 替换数组中的 指定的诸元素,你比如:
let foo = [1,2,4,6,5]foo.fill(7,2,5) // [1,2,7,7,7] // 三个参数分别为 用于替换的固定值、开始索引、结束索引(左闭右开)
用于将任何有 length 属性的对象转化为数组,你比如:
let foo = { 0: "first", 1: "second", length: 2}Array.from(foo) // ["first", "second"] // 对象 key 应为数组索引,否则转化成的数组诸项皆为 undefined
判断是否包含:
let foo = [1,2,4]foo.includes(3) // false
花式遍历数组的方式:
let foo = [1,2,4,3]// 1. for in array - 得到索引for(let i in foo){ console.log(i)} // 0 1 2 3// 2. for of array - 得到元素for(let i of foo){ console.log(i)} // 1 2 4 3// 3. for of array.keys() - keys() 迭代器生成索引for(let i of foo.keys()){ console.log(foo[i])} // 1 2 4 3// 4. for of array.entries() - entries() 迭代器生成 索引-值 键值对for(let i of foo.entries()){ console.log(i)} // [0, 1] [1, 2] [2, 4] [3, 3]
当然,也可以 forEach(以及 map 等):
let foo = [1,2,4,3]foo.forEach(i=>console.log(i)) // 1 2 4 3
即为函数参数指定默认值:
function foo(a = 1, b = 1){ return a + b}
其中需要注意,当参数同时有 指定默认值的 和 不指定默认值的混合使用时,需要把指定默认值的参数放到后面,你比如:
function foo(a, b = 1){ return a + b}
rest 参数通过 ...
操作符,将除列出的参数外,剩余的参数赋值给一个参数,你比如:
function foo(a, ...s){ console.log(s)}foo(1, 4, 3, 2) // [4, 3, 2]
另外,...
操作符是个好东西,它的作用是将一个某容器(数组、对象等)展开,通过它可以方便地进行数组、对象的深复制,你比如:
let foo = [1,2,3,4]let bar = [...foo]let baz = { a: 1}let quz = { ...baz}
此外,通过 ...
还可以方便地进行一些操作,你比如:
let foo = [10, 9, 7, 3, 12, 1]let bar = [8, 11]// 找最大值Math.max(...foo) // 12 // 此处 ...foo 从现象上看,可以理解为没有中括号的 10, 9, 7, 3, 12, 1// 组合数组[...foo, ...bar] // [10, 9, 7, 3, 12, 1, 8, 11]
箭头函数是个好东西,有了它可以更优雅、简洁地写匿名函数:
let foo = [1,2,3,4]// 1. 通过 function 关键字foo.map(function(item){ return item+"handled"}) // ["1handled", "2handled", "3handled", "4handled"]// 2. 箭头函数foo.map(item=>item+"handled") // ["1handled", "2handled", "3handled", "4handled"]
当需要传入多个参数(以及不传参数)的时候,需要在箭头左侧加上括号,你比如:
(a, b) => a+b
当然,也可以在箭头右侧通过大括号以及显式的返回值,处理更复杂的逻辑:
(a, b) => { if(a>123){ return b } return 456}
当变量和属性同名时,可以简写:
let name = "foo"let bar = { name}console.log(bar) // {name: "foo"}
你比如:
let foo = { bar(){ console.log("bar")}}foo.bar() // bar
函数也可以写成键值对形式:
let foo = { bar: function(){ console.log("bar")}} // 或者 {bar: ()=>console.log("bar")}foo.bar() // bar
你比如:
let foo = "name"let quz = "whatever"let bar = { [foo]: "baz", [quz]: ()=>console.log("boom")}bar.name // bazbar.whatever() // boom
通过 Object.is 比较两个对象的地址,等效于 ===
,你比如:
let foo = { a: 1, b: 2}let bar = { a: 1, b: 2}Object.is(foo, bar) // false
通过 Object.keys() 可以将对象 keys 转化为一个数组,而后遍历之,即可依次取出对象 values,你比如:
let foo = { a: 1, b: 2, c:3}Object.keys(foo).forEach(k=>console.log(foo[k])) // 1 2 3
而类似地,Object.values() 可以直接将 values 转化为一个数组,你比如:
let foo = { a: 1, b: 2, c:3}Object.values(foo).forEach(v=>console.log(v)) // 1 2 3
另外,通过 Object.entries() 可以将键值对转化为数组,亦可以方便地遍历对象:
let foo = { a: 1, b: 2, c: 3}Object.entries(foo) // ["a", 1] ["b": 2] ["c": 3]
所谓 set,集合也,主要特征为元素(元素可以为基本类型和对象的混合)不重复:
// 从数组创建 集合let foo = new Set([1, 1, 2, 2])foo // Set(2) {1, 2}
带有的主要方法包括:add
、delete
、has
、clear
,你比如:
let foo = new Set([1, 1, 2, 2])foo.add(3) // Set(3) {1, 2, 3}foo.delete(3) // Set(2) {1, 2}foo.has(3) // falsefoo.clear() // Set(0) {}
集合的遍历可以通过其 keys()、values() 函数,二者返回值相同:
let foo = new Set([1, 1, 2, 2])foo.keys() // SetIterator {1, 2}foo.values() // SetIterator {1, 2}
此外,通过 set 可以实现对数组的去重,你比如:
let foo = [1,2,2,3]let bar = new Set(foo)foo = [...bar.values()] // [1, 2, 3]
所谓 map,键值对也,即每个元素为一对数据,包括键、值,你比如:
let foo = new Map([["a", 1]]) // 按理说一个 map 中应该有很多对键值,因此从现象上看,如同一个二维数组foo // Map(1) {"a" => 1}
map 的操作函数包括:set
、get
、delete
、size
、clear
,你比如:
let foo = new Map([["a", 1]])foo.get("a") // 1foo.set("a", 2) // Map(1) {"a" => 2}foo.set("b", 3) // Map(2) {"a" => 2, "b" => 3}foo.delete("a") // true, 此时 foo: Map(1) {"b" => 3}foo.size // 1 // 为一个属性而不是方法foo.clear() // 此时 foo: Map(0) {}
Es6 中声明一个类如下:
class foo{ constructor(a, b){ this.a = a this.b = b }}
其中,类内可省略 function
关键字,且通过 this
可实现类属性的申明。
另外,类可以通过 extends 实现集成,通过 super 调用父类的方法或者构造函数。你比如:
class foo{ constructor(a){ this.a = a } saySth(){ console.log(this.a) }}class bar extends foo{ constructor(a,b){ super(a) // 调用父类构造函数 this.b = b } saySth(){ super.saySth() console.log(this.b) }}let quz = new bar(1, 2)quz.saySth()
所谓 symbol,象征、代号也,其生而为了独一无二,以解决属性名称重复的冲突。即便是相同的描述,对应的 Symbol 也不同,你比如:
Symbol("foo") === Symbol("foo") // false
通过 Symbol,我们可以安全地为某个对象动态添加属性:
let foo = { }foo[Symbol("bar")] = "bar"foo[Symbol("bar")] = "bar"foo // Symbol(bar): "bar", Symbol(bar): "bar"}
另外,我们也可以通过 Symbol.for() 的方式生成 symbol,但是这种方式生成 symbol 时,会先全局判断是否有相同描述的 symbol 已经被申明过,若是返回旧有的,否则才生成新的。这意思是:
Symbol.for("foo") === Symbol.for("foo") // true
Promise 是异步编程的解决方案,可以将 Promise 对象 理解为保存了未来结果的对象。
Promise 对象有三种状态,包括 pending
、fulfilled
、rejected
,分别表示 进行中(阻塞中)、成功、失败,后二者会分别执行 resolve
、reject
回调,一个用于示意的基本例子如下:
// 假定有一个请求函数 request// 其返回是形如 {status:200, data: ""} 的标准的 http请求返回const foo = new Promise((resolve, reject)=>{ const req = request() if(req.status===200){ resolve(req.data) }else{ reject(req.message) // 或者说直接 reject("失败!") 等 }})
由上述可见,通过 resolve 将状态由 pending 转为 fulfilled
,通过 reject 将状态由 pending 转为 rejected
。得到了 Promise 实例后,我们可以通过 then、catch 链式分别获取到 resolve、reject 的值。接着上述示例,你比如:
foo .then(data=>console.log(data)) // 链式得到的返回值依旧是一个 promise .catch(err=>console.log(err)) .finally(()=>{ // 做一些收尾操作 })
而 ECMAScript 2017 标准提出了 async/await
语法糖,使得异步编程更为简洁。上述例子的基本逻辑也可以写成:
async function foo(){ const req = await request() if(req.status===200){ console.log(req.data) }else{ console.log(req.message) } // 而后一些收尾操作}
其中,async
用于装饰 函数,await
用于装饰异步请求,这样一来程序逻辑只要顺序书写即可。
转载地址:http://sqbws.baihongyu.com/