網(wǎng)站建設(shè)軟件下載西安seo服務(wù)公司
var、let、const之間有什么區(qū)別?
var: 在ES5中,頂層對象的屬性和全局變量是等價的,用var聲明的變量既是全局變量,也是頂層變量?
注意:頂層對象,在瀏覽器環(huán)境指的是window對象,在 Node 指的是global對象
var a = 10;?
console.log(window.a) // 10
使用var聲明的變量存在變量提升的情況
console.log(a) // undefined?
var a = 20
在編譯階段,編譯器會將其變成以下執(zhí)行
var a?
console.log(a)?
a = 20
使用var,我們能夠?qū)σ粋€變量進行多次聲明,后面聲明的變量會覆蓋前面的變量聲明
var a = 20 ?
var a = 30?
console.log(a) // 30
在函數(shù)中使用使用var聲明變量時候,該變量是局部的
var a = 20?
function change(){?var a = 30?
}?
change()?
console.log(a) // 20
而如果在函數(shù)內(nèi)不使用var,該變量是全局的
var a = 20?
function change(){?a = 30?
}?
change()?
console.log(a) // 30
let : let是ES6新增的命令,用來聲明變量?
用法類似于var,但是所聲明的變量,只在let命令所在的代碼塊內(nèi)有效
{?let a = 20?
}?
console.log(a) // ReferenceError: a is not defined.
不存在變量提升
console.log(a) // 報錯ReferenceError?
let a = 2
這表示在聲明它之前,變量a是不存在的,這時如果用到它,就會拋出一個錯誤?
只要塊級作用域內(nèi)存在let命令,這個區(qū)域就不再受外部影響
var a = 123?
if (true) {?a = 'abc' // ReferenceError?let a;?
}
使用let聲明變量前,該變量都不可用,也就是大家常說的“暫時性死區(qū)”?
最后,let不允許在相同作用域中重復(fù)聲明
let a = 20?
let a = 30?
// Uncaught SyntaxError: Identifier 'a' has already been declared
注意的是相同作用域,下面這種情況是不會報錯的
let a = 20?
{?let a = 30?
}
因此,我們不能在函數(shù)內(nèi)部重新聲明參數(shù)
function func(arg) {?let arg;?
}?
func()?
// Uncaught SyntaxError: Identifier 'arg' has already been declared
const: const聲明一個只讀的常量,一旦聲明,常量的值就不能改變
const a = 1?
a = 3?
// TypeError: Assignment to constant variable.
這意味著,const一旦聲明變量,就必須立即初始化,不能留到以后賦值
const a;?
// SyntaxError: Missing initializer in const declaration
如果之前用var或let聲明過變量,再用const聲明同樣會報錯
var a = 20?
let b = 20?
const a = 30?
const b = 30?
// 都會報錯
const實際上保證的并不是變量的值不得改動,而是變量指向的那個內(nèi)存地址所保存的數(shù)據(jù)不得改動?
對于簡單類型的數(shù)據(jù),值就保存在變量指向的那個內(nèi)存地址,因此等同于常量?
對于復(fù)雜類型的數(shù)據(jù),變量指向的內(nèi)存地址,保存的只是一個指向?qū)嶋H數(shù)據(jù)的指針,const只能保證這個指針是固定的,并不能確保改變量的結(jié)構(gòu)不變
const foo = {};?
?
// 為 foo 添加一個屬性,可以成功?
foo.prop = 123;?
foo.prop // 123?
?
// 將 foo 指向另一個對象,就會報錯?
foo = {}; // TypeError: "foo" is read-only
使用區(qū)別:
var、let、const三者區(qū)別可以圍繞下面五點展開:?
- 變量提升?
- 暫時性死區(qū)?
- 塊級作用域?
- 重復(fù)聲明?
- 修改聲明的變量?
- 使用
變量提升?
var 聲明的變量存在變量提升,即變量可以在聲明之前調(diào)用,值為undefined?
// 2023.4.25 更新?
let和const不存在變量提升,即它們所聲明的變量一定要在聲明后使用,否則報錯?
let / const 不存在變量提升是不完全正確的,只能說由于暫時性死區(qū)的存在使得我們無法直觀感受到變量提升的效果。?
let 和 const 定義的變量都會被提升,但是不會被初始化,不能被引用,不會像var定義的變量那樣,初始值為undefined。?
當(dāng)進入let變量的作用域時,會立即給它創(chuàng)建存儲空間,但是不會對它進行初始化。?
變量的賦值可以分為三個階段:
- 創(chuàng)建變量,在內(nèi)存中開辟空間?
- 初始化變量,將變量初始化為undefined?
- 真正賦值
關(guān)于let、var和function:?
- let 的「創(chuàng)建」過程被提升了,但是初始化沒有提升。?
- var 的「創(chuàng)建」和「初始化」都被提升了。?
- function 的「創(chuàng)建」「初始化」和「賦值」都被提升了。
// var?
console.log(a) // undefined?
var a = 10?
?
// let ?
console.log(b) // Cannot access 'b' before initialization?
let b = 10?
?
// const?
console.log(c) // Cannot access 'c' before initialization?
const c = 10
暫時性死區(qū)?
var不存在暫時性死區(qū)?
let和const存在暫時性死區(qū),只有等到聲明變量的那一行代碼出現(xiàn),才可以獲取和使用該變量
// var?
console.log(a) // undefined?
var a = 10?
?
// let?
console.log(b) // Cannot access 'b' before initialization?
let b = 10?
?
// const?
console.log(c) // Cannot access 'c' before initialization?
const c = 10
塊級作用域?
var不存在塊級作用域?
let和const存在塊級作用域
// var?
{?var a = 20?
}?
console.log(a) // 20?
?
// let?
{?let b = 20?
}?
console.log(b) // Uncaught ReferenceError: b is not defined?
?
// const?
{?const c = 20?
}?
console.log(c) // Uncaught ReferenceError: c is not defined
重復(fù)聲明
var允許重復(fù)聲明變量?
let和const在同一作用域不允許重復(fù)聲明變量
// var?
var a = 10?
var a = 20 // 20?
?
// let?
let b = 10?
let b = 20 // Identifier 'b' has already been declared?
?
// const?
const c = 10?
const c = 20 // Identifier 'c' has already been declared
修改聲明的變量
// var?
var a = 10?
a = 20?
console.log(a) // 20?
?
//let?
let b = 10?
b = 20?
console.log(b) // 20?
?
// const?
const c = 10?
c = 20?
console.log(c) // Uncaught TypeError: Assignment to constant variable
使用?
能用const的情況盡量使用const,其他情況下大多數(shù)使用let,避免使用var
ES6新特性
關(guān)于ES6和JavaScript的關(guān)系?
1、ES6是對于ES2015+的俗稱,也可以說是通常叫法,那么,ES6是什么呢??
ES 全稱是ECMAScript,它是JavaScript基礎(chǔ)構(gòu)建的一種語言,JavaScript正是建立在ECMAScript語言的基礎(chǔ)規(guī)范中建立使用的,那么,ECMAScript的使用,對于JavaScript至關(guān)重要!?
在我的理解中,ECMAScript是一種語言層面的東西,它只是定義了JavaScript以及在它基礎(chǔ)之上建立的其他語言的語法規(guī)范,而JavaScript的語言,更關(guān)于一種平臺性質(zhì)在其中。?
JavaScript包括 ECMAScript、DOM、BOM三個組成部分,DOM和BOM是web API提供的接口或者是JavaScript和瀏覽器之間進行交互的部分,實質(zhì)就是操縱文檔元素,進行展示布局,而ECMAScript在JavaScript中其中語法的作用,它不會去跟文檔有直接的關(guān)系,但是他的數(shù)據(jù)處理完成后會通過web API展示在文檔中。?
ES6新特性的分類?
新特性主要歸為四大類:?
- 解決原有語法上的一些不足?
比如let 和 const 的塊級作用域? - 對原有語法進行增強?
比如解構(gòu)、展開、參數(shù)默認值、模板字符串? - 全新的對象、全新的方法、全新的功能?
比如promise、proxy、object的assign、is? - 全新的數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu)?
比如symbol、set、map
1. let、const 塊級作用域以及和 var 的區(qū)別 可參考上邊的描述
2.解構(gòu)-快速提取數(shù)組/對象中的元素
- 數(shù)組解構(gòu)?
- 單獨解構(gòu)-根據(jù)數(shù)組索引,將數(shù)組解構(gòu)成單獨的元素
const arr = [1, 2, 3]?
?
const [a, b, c] = arr?
console.log(a, b, c) //1,2,3?
const [, , d] = arr?
console.log(d) //3
默認值,解構(gòu)時可以給變量設(shè)置默認值,數(shù)組沒有這個元素的話
const arr = [1, 2, 3]?
?
const [, , , defaultVal = '4'] = arr?
console.log('設(shè)置默認值', defaultVal)
剩余解構(gòu)-用 “…+變量名” 解構(gòu)剩余參數(shù)到新數(shù)組,只能用一次
const arr = [1, 2, 3]?
?
const [e, ...rest] = arr?
console.log(rest) //[2, 3]
實例應(yīng)用
// 拆分字符串?
const str = 'xiaobai/18/200'?
const strArr = str.split('/')?
const [, age] = strArr?
console.log(age) //18
對象解構(gòu)?
- 單個/多個解構(gòu)-跟數(shù)組解構(gòu)差不多
const obj = { name: 'xiaohui', age: 18, height: undefined }?
const { name, age } = obj?
console.log(name, age) // 'xiaohui', 18
解構(gòu)+重命名-給解構(gòu)出來的變量重命名
const obj = { name: 'xiaohui', age: 18, height: undefined }?
const { name: objName } = obj?
console.log(objName)
默認值-給解構(gòu)變量設(shè)置默認值
const obj = { name: 'xiaohui', age: 18, height: undefined }?
const { next = 'default' } = obj?
console.log(next)
3.模板字符串
用法:使用``將字符串包裹起來?
功能:可以換行、插值、使用標簽函數(shù)進行字符串操作?
示例:
- 換行/插值
//換行?
const str = `fdsjak?fdsa`?
console.log(str)?
?
// 插值?
const strs = `random: ${Math.random()}`?
console.log(strs)
標簽函數(shù)-可以對模板字符串的字符串和插值進行處理和過濾等操作
/**?* 字符串模板函數(shù)?* @param {array} strs 以插值為分隔符組成的字符串?dāng)?shù)組?* @param {string} name 插值的value,有多少個就會傳入多少個?*/?
const tagFunc = (strs, name, gender) => {?const [str1, str2, str3] = strs?const genderParsed = gender == '1' ? '男' : '女'?// 可以在此做過濾,字符串處理,多語言等操作?return str1 + name + str2 + str3 + genderParsed?
0-0-0-}?
?
// 帶標簽的模板字符串,?
const person = {?name: 'xiaohui',?gender: 1,?
}?
// 返回值為標簽函數(shù)的返回值?
const result = tagFunc`my name is ${person.name}.gender is ${person.gender}`?
console.log(result) //my name is xiaohui.gender is 男
4. 字符串?dāng)U展方法?
- includes-是否包含?
- startsWith-是否以什么開始?
- endsWith-是否以什么結(jié)束
const str = 'abcd'?
?
console.log(str.includes('e')) //false?
console.log(str.startsWith('a')) //true?
console.log(str.endsWith('a')) //false
5.參數(shù)默認值&剩余參數(shù)
給函數(shù)形參設(shè)置默認值
// 帶默認參數(shù)的形參一般放在后面,減少傳參導(dǎo)致的錯誤幾率?
const defaultParams = function (name, age = 0) {?return [age, name]?
}?
console.log(defaultParams(1))
使用…rest 形式設(shè)置剩余形參,支持無限參數(shù)
// 剩余參數(shù),轉(zhuǎn)化成數(shù)組?
const restParams = function (...args) {?console.log(args.toString()) //1, 2, 3, 4, 5?
}?
?
restParams(1, 2, 3, 4, 5)
6.展開數(shù)組
const arr = [1, 2, 3]?
?
console.log(...arr)?
// 等價于es5中以下寫法?
console.log.apply(console, arr)
7.箭頭函數(shù)
特性&優(yōu)勢:?
1、簡化了函數(shù)的寫法?
2、沒有 this 機制,this 繼承自上一個函數(shù)的上下文,如果上一層沒有函數(shù),則指向 window?
3、作為異步回調(diào)函數(shù)時,可解決 this 指向問題
const inc = (n) => n + 1?
console.log(inc(100))?
?
const obj = {?name: 'aa',?func() {?setTimeout(() => {?console.log(this.name) //aa?}, 0)?setTimeout(function () {?console.log(this.name) //undefined?}, 0)?},?
}?
obj.func()
8.對象字面量增強
- 同名屬性可以省略 key:value 形式,直接 key,?
- 函數(shù)可以省略 key:value 形式?
- 可以直接 func(),?
- 可以使用計算屬性,比如:{[Math.random()]: value}
/**?* 1、增強了對象字面量:?* 1,同名屬性可以省略key:value形式,直接key,?* 2,函數(shù)可以省略key:value形式?* 3,可以直接func(),?* 4,可以使用計算屬性,比如:{[Math.random()]: value}?*/?
const arr = [1, 2, 3]?
const obj = {?arr,?func() {?console.log(this.arr)?},?[Math.random()]: arr,?
}?
?
console.log(obj)
9.Object.assign(target1, target2, targetN)-復(fù)制/合并對象
/**?* Object.assign(target1, target2, ...targetn)?* 后面的屬性向前面的屬性合并?* 如果target1是空對象,可以創(chuàng)建一個全新對象,而不是對象引用?*/?
const obj1 = {?a: 1,?b: 2,?
}?
const obj2 = {?a: 1,?b: 2,?
}?
?
const obj3 = Object.assign({}, obj1)?
obj3.a = 5?
console.log(obj3, obj2, obj1)
10.Object.is(value1, value2)?
作用:比較兩個值是否相等
console.log(NaN === NaN) //false?
console.log(Object.is(NaN, NaN)) //true?
console.log(0 === -0) // true?
console.log(Object.is(0, -0)) //false?
console.log(Object.is(1, 1)) //true
11.Proxy(object, handler)
作用:?
- 代理一個對象的所有,包括讀寫操作和各種操作的監(jiān)聽?
用法:
const P = {?n: 'p',?a: 19,?
}?
?
const proxy = new Proxy(P, {?get(target, property) {?console.log(target, property)?return property in target ? target[property] : null?},?defineProperty(target, property, attrs) {?console.log(target, property, attrs)?// throw new Error('不允許修改')?},?deleteProperty(target, property) {?console.log(target, property)?delete target[property]?},?set(target, property, value) {?target[property] = value?},?
})?
?
proxy.c = 100?
console.log('pp', P)
優(yōu)勢:?
擁有很多 defineProperty 沒有的屬性方法,比如:?
- handler.getPrototypeOf() —Object.getPrototypeOf 方法的監(jiān)聽器?
- handler.setPrototypeOf() —Object.setPrototypeOf 方法的監(jiān)聽器。?
- handler.isExtensible() —Object.isExtensible 方法的監(jiān)聽器。?
- handler.preventExtensions() —Object.preventExtensions 方法的監(jiān)聽器。?
- handler.getOwnPropertyDescriptor() —Object.getOwnPropertyDescriptor 方法的監(jiān)聽器。?
- handler.defineProperty() —Object.defineProperty 方法的監(jiān)聽器。?
- handler.has() —in 操作符的監(jiān)聽器。?
- handler.get() —屬性讀取操作的監(jiān)聽器。?
- handler.set() —屬性設(shè)置操作的監(jiān)聽器。?
- handler.deleteProperty() —delete 操作符的監(jiān)聽器?
- handler.ownKeys() —Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的監(jiān)聽器。?
- handler.apply() —函數(shù)調(diào)用操作的監(jiān)聽器。?
- handler.construct() —new 操作符的監(jiān)聽器。?
對數(shù)組的監(jiān)視更方便?
以非侵入的方式監(jiān)管對象的讀寫
12.Reflect
作用:?
集成 Object 操作的所有方法,統(tǒng)一、方便,具體方法如下:?
用于對對象的統(tǒng)一操作,集成 Object 相關(guān)的所有方法?
1、apply:類似 Function.prototype.apply?
2、Reflect.construct()?
對構(gòu)造函數(shù)進行 new 操作,相當(dāng)于執(zhí)行 new target(…args)。?
3、Reflect.defineProperty()?
和 Object.defineProperty() 類似。?
4、Reflect.deleteProperty()?
作為函數(shù)的 delete 操作符,相當(dāng)于執(zhí)行 delete target[name]。?
5、Reflect.get()?
獲取對象身上某個屬性的值,類似于 target[name]。?
6、Reflect.getOwnPropertyDescriptor()?
類似于 Object.getOwnPropertyDescriptor()。?
7、Reflect.getPrototypeOf()?
類似于 Object.getPrototypeOf(), 獲取目標對象的原型。?
8、Reflect.has()?
判斷一個對象是否存在某個屬性,和 in 運算符 的功能完全相同。?
9、Reflect.isExtensible()?
類似于 Object.isExtensible().判斷對象是否可擴展,可以添加額外屬性?
Object.seal(封閉對象), Object.freeze(凍結(jié)對象)是不可擴展的?
10、Reflect.ownKeys()?
返回一個包含所有自身屬性(不包含繼承屬性)的數(shù)組。(類似于 Object.keys(), 但不會受 enumerable 影響).?
11、Reflect.preventExtensions()?
類似于 Object.preventExtensions()。返回一個 Boolean。?
12、Reflect.set()?
將值分配給屬性的函數(shù)。返回一個 Boolean,如果更新成功,則返回 true, 反之返回 false。?
13、Reflect.setPrototypeOf()?
類似于 Object.setPrototypeOf()。
const obj = {?name: 'reflect',?
}?
Reflect.preventExtensions(obj) //禁止擴展?
console.log(Reflect.set(obj, 'age', 'xiaobai')) //false?
console.log(obj) //{ name: 'reflect' }?
console.log(Reflect.isExtensible(obj, 'name')) //false?
console.log(Reflect.ownKeys(obj)) //[ 'name' ]
13.Promise
14.class&靜態(tài)方法&繼承
使用 class 關(guān)鍵字定義類
class Person {?constructor(props) {?this.props = props?}?
}
方法?
- 實例方法,需要實例化之后才能調(diào)用,this 指向?qū)嵗?
- 靜態(tài)方法,用 static 修飾符修飾,可以直接通過類名調(diào)用,不需要實例化,this 不指向?qū)嵗?#xff0c;而是指向當(dāng)前類
class Person {?constructor(props) {?this.props = props?}?// 實例方法?eat() {}?// 靜態(tài)方法?static run() {}?
}?
// 調(diào)用靜態(tài)方法?
Person.run()?
const person = new Person('props')?
// 調(diào)用實例方法?
person.eat()
繼承:子類使用 extends 關(guān)鍵字實現(xiàn)繼承,可以繼承父類所有屬性
class Student extends Person {?constructor(props) {?super(props)?}?printProps() {?console.log(this.props)?}?
}?
?
const student = new Student('student')?
student.printProps()
15.Set
說明:?
Set 是一種類似于數(shù)組的數(shù)據(jù)結(jié)構(gòu)?
特性:?
- 元素唯一性,不允許重復(fù)元素?
- 使用 add 增加重復(fù)元素,將會被忽略?
用途:?
- 數(shù)組去重?
- 數(shù)據(jù)存儲
const arr = [1, 3, 1, 1, 1]?
const set = new Set(arr)?
set.add(1).add(1)?
console.log(set.size) //2?
const newArr = Array.from(set)?
console.log(newArr) //[ 1, 3 ]
16.Map
說明:?
類似 Object,以 key、value 形式存儲數(shù)據(jù)?
區(qū)別:?
Map 鍵不會隱式轉(zhuǎn)換成字符串,而是保持原有類型?
實例:
const map = new Map()?
map.set(1, 1)?
map.set('name', 'map')?
map.set(obj, obj)?
console.log(map.get(1)) //1?
/**?1 1?name map?{ '1': 1, true: true, a: 'a' } { '1': 1, true: true, a: 'a' }?*/?
map.forEach((val, key) => {?console.log(key, val)?
})
17.Symbol
說明:?
JavaScript 第六種原始數(shù)據(jù)類型,用來定義一個唯一的變量?
作用:?
創(chuàng)建唯一的變量,解決對象鍵名重復(fù)問題?
為對象、類、函數(shù)等創(chuàng)建私有屬性?
修改對象的 toString 標簽?
為對象添加迭代器屬性?
如何獲取對象的 symbol 屬性??
Object.getOwnPropertySymbols(object)?
實例
// 對象屬性重名問題;?
const objSymbol = {?[Symbol()]: 1,?[Symbol()]: 2,?
}?
console.log(objSymbol)?
?
// 2、為對象、類、函數(shù)等創(chuàng)建私有屬性?
const name = Symbol()?
const obj2 = {?[name]: 'symbol',?testPrivate() {?console.log(this[name])?},?
}?
?
obj2.testPrivate()?
// 定義toString標簽;?
console.log(obj2.toString())?
obj2[Symbol.toStringTag] = 'xx'?
console.log(obj2.toString()) //[object xx]
18.for…of…
用途:?
已統(tǒng)一的方式,遍歷所有引用數(shù)據(jù)類型?
特性:?
可以隨時使用 break 終止遍歷,而 forEach 不行?
實例:
// 基本用法?
// 遍歷數(shù)組?if (item > 2) {?console.log(item)?}?
}?
?
// 遍歷set?
const set = new Set()?
set.add('foo').add('bar')?
for (const item of set) {?console.log('set for of', item)?
}?
// 遍歷map?
const map = new Map()?
map.set('foo', 'one').set('bar', 'two')?
for (const [key, val] of map) {?console.log('for of map', key, val)?
}?
//迭代對象?
const obj = {?name: 'xiaohui',?age: '10',?store: [1, 2, 3],?// 實現(xiàn)可迭代的接口?[Symbol.iterator]: function () {?const params = [this.name, this.age, this.store]?let index = 0?return {?next() {?const ret = {?value: params[index],?done: index >= params.length,?}?index++?return ret?},?}?},?
}?
?
for (const item of obj) {?console.log('obj for of', item)?
}
19.迭代器模式
作用:通過 Symbol.interator 對外提供統(tǒng)一的接口,獲取內(nèi)部的數(shù)據(jù)?
外部可以通過 for…of…去迭代內(nèi)部的數(shù)據(jù)
const tods = {?life: ['eat', 'sleep'],?learn: ['js', 'dart'],?// 增加的任務(wù)?work: ['sale', 'customer'],?[Symbol.iterator]: function () {?const all = []?Object.keys(this).forEach((key) => {?all.push(...this[key])?})?let index = 0?return {?next() {?const ret = {?value: all[index],?done: index >= all.length,?}?index++?return ret?},?}?},?
}?
?
for (const item of tods) {?console.log(item)?
}
20.Generator 生成器
Generator?
- 函數(shù)前添加 *,生成一個生成器?
- 一般配合 yield 關(guān)鍵字使用?
- 最大特點,惰性執(zhí)行,調(diào) next 才會往下執(zhí)行?
- 主要用來解決異步回調(diào)過深的問題
// 生成迭代器方法?
// 生成器Generator的應(yīng)用?
?
function* createIdGenerator() {?let id = 1?while (id < 3) yield id++?
}?
const createId = createIdGenerator()?
console.log(createId.next()) //{ value: 1, done: false }?
console.log(createId.next()) //{ value: 2, done: false }?
console.log(createId.next()) //{ value: undefined, done: true }?
?
const todos = {?life: ['eat', 'sleep', 'baba'],?learn: ['es5', 'es6', 'design pattern'],?work: ['b', 'c', 'framework'],?[Symbol.iterator]: function* () {?const all = [...this.life, ...this.learn, ...this.work]?for (const i of all) {?yield i?}?},?
}?
for (const item of todos) {?console.log(item)?
}
21.includes 函數(shù)-es2016
判斷數(shù)組是否包含某個元素,包含 NaN,解決 indexOf 無法查找 NaN 問題
// includes函數(shù)?
const arr = ['foo', 'bar', 'baz', NaN]?
console.log(arr.includes(NaN)) //true?
console.log(arr.indexOf(NaN)) //-1
22.運算符-es2016
// 指數(shù)運算符 **?
// es5中2十次方?
console.log(Math.pow(2, 10))?
// es6中2十次方?
console.log(2 ** 10)
23.values 函數(shù)-es2017
將對象的值以數(shù)組的形式返回
const obj = {?foo: 1,?bar: 2,?baz: 3,?
}?
?
console.log(Object.values(obj)) //[ 1, 2, 3 ]
24.entries 函數(shù)-es2017
將對象以鍵值對二維數(shù)組返回,使之可以使用 for…of…遍歷
const obj = {?foo: 1,?bar: 2,?baz: 3,?
}?
console.log(Object.entries(obj))?
const entry = Object.entries(obj)?
for (const [key, value] of entry) {?console.log(key, value)?
}
25.Object.getOwnPropertyDescriptors(obj)-es2017
獲取對象的描述信息?
可以通過獲得的描述信息,配合 Object.defineProperties 來完整復(fù)制對象,包含 get,set 方法
// getOwnPropertyDescriptors?
?
// 普通get方法?
const objGet = {?foo: 1,?bar: 2,?get getCount() {?return this.foo + this.bar?},?
}?
// assign方法會把getCount當(dāng)做普通屬性復(fù)制,從而getCount為3,修改bar不管用?
const objGet1 = Object.assign({}, objGet)?
objGet1.bar = 3?
console.log(objGet1.getCount) //3?
// descriptors?
const descriptors = Object.getOwnPropertyDescriptors(objGet)?
console.log('des', descriptors)?
// 通過descriptors來復(fù)制對象,可以完整復(fù)制對象,包含get,set?
const objGet2 = Object.defineProperties({}, descriptors)?
objGet2.bar = 3?
console.log(objGet2.getCount) //4
26.padStart, padEnd 函數(shù)-es2017
在字符串前,或者后面追加指定字符串?
參數(shù):?
targetLenght: 填充后的目標長度?
padString:填充的字符串?
規(guī)則:?
1、填充的字符串超過目標長度,會在規(guī)定長度時被截斷?
2、填充字符串太短會以空格填充?
3、padString 未傳值,以空格填充?
作用:?
一般用來對齊字符串輸出
/**?* foo.................|1?barbar..............|2?bazbazbaz...........|3?*/?console.log(`${key.padEnd(20, '.')}${value.toString().padStart(2, '|')}`)
基本數(shù)據(jù)類型
JavaScript 中的簡單數(shù)據(jù)類型包括以下幾種:?
- 字符串(String):用于表示文本數(shù)據(jù),用引號(單引號或雙引號)包裹起來,例如:“Hello, World!”。?
- 數(shù)字(Number):用于表示數(shù)值數(shù)據(jù),包括整數(shù)和浮點數(shù)(帶小數(shù)點的數(shù)),例如:42、3.14。?
- 布爾值(Boolean):用于表示邏輯值,只有兩個可能的取值:true(真)和false(假)。?
- undefined:表示未定義的值,通常表示未聲明的變量或缺少返回值的函數(shù)。?
- null:表示空值,用于顯式地表示變量或?qū)ο鬀]有值。?
- Symbol(符號):表示唯一的標識符,用于對象屬性的鍵。?
- BigInt:用于表示任意精度的整數(shù)。BigInt 是一種簡單數(shù)據(jù)類型,在 ECMAScript 2020 中引入。?
這些簡單數(shù)據(jù)類型在 JavaScript 中是不可變的,也就是說,它們的值在創(chuàng)建后不能被修改。當(dāng)你對一個簡單數(shù)據(jù)類型的值進行操作時,實際上是創(chuàng)建了一個新的值。
ES6中數(shù)組擴展
擴展運算符(Spread operator):使用 … 語法可以將一個數(shù)組展開成多個獨立的元素,或者將多個元素合并為一個數(shù)組。?
- Array.from():通過類似數(shù)組的對象或可迭代對象創(chuàng)建一個新的數(shù)組。?
- Array.of():創(chuàng)建一個由傳入?yún)?shù)組成的新數(shù)組。?
- find() 和 findIndex():用于在數(shù)組中查找滿足指定條件的第一個元素及其索引。?
- includes():檢查數(shù)組是否包含指定的元素,并返回布爾值。?
- fill():使用指定的值填充數(shù)組的所有元素。?
- flat() 和 flatMap():用于將嵌套的數(shù)組展平,減少維度。?
- map()、filter()、reduce()、forEach() 等方法的回調(diào)函數(shù)支持箭頭函數(shù)語法。?
- entries()、keys() 和 values():用于遍歷數(shù)組的鍵值對、鍵和值。?
- 數(shù)組解構(gòu)賦值:可以通過解構(gòu)賦值從數(shù)組中提取值并賦給變量。?
- 數(shù)組的擴展屬性:Array.prototype.length 可以被修改,- Array.prototype[@@toStringTag] 返回 “Array”。
箭頭函數(shù)
什么是箭頭函數(shù)??
ES6中允許使用箭頭=>來定義箭頭函數(shù),具體語法,我們來看一個簡單的例子:
// 箭頭函數(shù)?
let fun = (name) => {?// 函數(shù)體?return `Hello ${name} !`;?
};?
?
// 等同于?
let fun = function (name) {?// 函數(shù)體?return `Hello ${name} !`;?
};
可以看出,定義箭頭函在數(shù)語法上要比普通函數(shù)簡潔得多。箭頭函數(shù)省去了function關(guān)鍵字,采用箭頭=>來定義函數(shù)。函數(shù)的參數(shù)放在=>前面的括號中,函數(shù)體跟在=>后的花括號中。
箭頭函數(shù)與普通函數(shù)的區(qū)別?
1、語法更加簡潔、清晰?
從上面的基本語法示例中可以看出,箭頭函數(shù)的定義要比普通函數(shù)定義簡潔、清晰得多,很快捷。?
2、箭頭函數(shù)不會創(chuàng)建自己的this(重要!!深入理解!!)?
我們先來看看MDN上對箭頭函數(shù)this的解釋。?
?
箭頭函數(shù)不會創(chuàng)建自己的this,所以它沒有自己的this,它只會從自己的作用域鏈的上一層繼承this。?
?箭頭函數(shù)沒有自己的this,它會捕獲自己在定義時(注意,是定義時,不是調(diào)用時)所處的外層執(zhí)行環(huán)境的this,并繼承這個this值。所以,箭頭函數(shù)中this的指向在它被定義的時候就已經(jīng)確定了,之后永遠不會改變。
3、箭頭函數(shù)繼承而來的this指向永遠不變(重要)
上面的例子,就完全可以說明箭頭函數(shù)繼承而來的this指向永遠不變。對象obj的方法b是使用箭頭函數(shù)定義的,這個函數(shù)中的this就永遠指向它定義時所處的全局執(zhí)行環(huán)境中的this,即便這個函數(shù)是作為對象obj的方法調(diào)用,this依舊指向Window對象。
4、.call()/.apply()/.bind()無法改變箭頭函數(shù)中this的指向
.call()/.apply()/.bind()方法可以用來動態(tài)修改函數(shù)執(zhí)行時this的指向,但由于箭頭函數(shù)的this定義時就已經(jīng)確定且永遠不會改變。所以使用這些方法永遠也改變不了箭頭函數(shù)this的指向,雖然這么做代碼不會報錯
5、箭頭函數(shù)不能作為構(gòu)造函數(shù)使用
我們先了解一下構(gòu)造函數(shù)的new都做了些什么?簡單來說,分為四步:?
① JS內(nèi)部首先會先生成一個對象; ② 再把函數(shù)中的this指向該對象; ③ 然后執(zhí)行構(gòu)造函數(shù)中的語句; ④ 最終返回該對象實例。?
但是!!因為箭頭函數(shù)沒有自己的this,它的this其實是繼承了外層執(zhí)行環(huán)境中的this,且this指向永遠不會隨在哪里調(diào)用、被誰調(diào)用而改變,所以箭頭函數(shù)不能作為構(gòu)造函數(shù)使用,或者說構(gòu)造函數(shù)不能定義成箭頭函數(shù),否則用new調(diào)用時會報錯!
6、箭頭函數(shù)沒有自己的arguments
箭頭函數(shù)沒有自己的arguments對象。在箭頭函數(shù)中訪問arguments實際上獲得的是外層局部(函數(shù))執(zhí)行環(huán)境中的值。
7、箭頭函數(shù)沒有原型prototype
let sayHi = () => {?console.log('Hello World !')?
};?
console.log(sayHi.prototype); // undefined
8、箭頭函數(shù)不能用作Generator函數(shù),不能使用yeild關(guān)鍵字
symbol 有什么用處?
ES5 的對象屬性名都是字符串,這容易造成屬性名的沖突。比如,你使用了一個他人提供的對象,但又想為這個對象添加新的方法(mixin 模式),新方法的名字就有可能與現(xiàn)有方法產(chǎn)生沖突。如果有一種機制,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的沖突。這就是 ES6 引入Symbol的原因。?
ES6 引入了一種新的原始數(shù)據(jù)類型Symbol,表示獨一無二的值。它是 JavaScript 語言的第七種數(shù)據(jù)類型,前六種是:undefined、null、布爾值(Boolean)、字符串(String)、數(shù)值(Number)、對象(Object)。?
Symbol 值通過Symbol函數(shù)生成。這就是說,對象的屬性名現(xiàn)在可以有兩種類型,一種是原來就有的字符串,另一種就是新增的 Symbol 類型。凡是屬性名屬于 Symbol 類型,就都是獨一無二的,可以保證不會與其他屬性名產(chǎn)生沖突。
async/await 和 Promise 有什么關(guān)系?
Promise?
?
Promise 對象是一個代理對象(代理一個值),被代理的值在Promise對象創(chuàng)建時可能是未知的。它允許你為異步操作的成功和失敗分別綁定相應(yīng)的處理方法(handlers)。 這讓異步方法可以像同步方法那樣返回值,但并不是立即返回最終執(zhí)行結(jié)果,而是一個能代表未來出現(xiàn)的結(jié)果的promise對象?
async/await?
es2017的新語法,async/await就是generator + promise的語法糖?
async/await 和 Promise 的關(guān)系非常的巧妙,await必須在async內(nèi)使用,并裝飾一個Promise對象,async返回的也是一個Promise對象。?
async/await中的return/throw會代理自己返回的Promise的resolve/reject,而一個Promise的resolve/reject會使得await得到返回值或拋出異常。?
- 如果方法內(nèi)無await節(jié)點?
return 一個字面量則會得到一個{PromiseStatus: resolved}的Promise。?
throw 一個Error則會得到一個{PromiseStatus: rejected}的Promise。? - 如果方法內(nèi)有await節(jié)點?
async會返回一個{PromiseStatus: pending}的Promise(發(fā)生切換,異步等待Promise的執(zhí)行結(jié)果)。?
Promise的resolve會使得await的代碼節(jié)點獲得相應(yīng)的返回結(jié)果,并繼續(xù)向下執(zhí)行。?
Promise的reject 會使得await的代碼節(jié)點自動拋出相應(yīng)的異常,終止向下繼續(xù)執(zhí)行。
如何中斷Promise
Promise 有個缺點就是一旦創(chuàng)建就無法取消,所以本質(zhì)上 Promise 是無法被終止的,但我們在開發(fā)過程中可能會遇到下面兩個需求
- 中斷調(diào)用鏈
就是在某個 then/catch 執(zhí)行之后,不想讓后續(xù)的鏈式調(diào)用繼續(xù)執(zhí)行了
somePromise?.then(() => {})?.then(() => {?// 終止 Promise 鏈,讓下面的 then、catch 和 finally 都不執(zhí)行?})?.then(() => console.log('then'))?.catch(() => console.log('catch'))?.finally(() => console.log('finally'))
?一種方法是在then中直接拋錯, 這樣就不會執(zhí)行后面的then, 直接跳到catch方法打印err(但此方法并沒有實際中斷)。但如果鏈路中對錯誤進行了捕獲,后面的then函數(shù)還是會繼續(xù)執(zhí)行。?
Promise的then方法接收兩個參數(shù):
Promise.prototype.then(onFulfilled, onRejected)
若onFulfilled或onRejected是一個函數(shù),當(dāng)函數(shù)返回一個新Promise對象時,原Promise對象的狀態(tài)將跟新對象保持一致,詳見Promises/A+標準。?
因此,當(dāng)新對象保持“pending”狀態(tài)時,原Promise鏈將會中止執(zhí)行。
Promise.resolve().then(() => {?console.log('then 1')?return new Promise(() => {})?
}).then(() => {?console.log('then 2')?
}).then(() => {?console.log('then 3')?
}).catch((err) => {?console.log(err)?
})
- 中斷Promise
注意這里是中斷而不是終止,因為 Promise 無法終止,這個中斷的意思是:在合適的時候,把 pending 狀態(tài)的 promise 給 reject 掉。例如一個常見的應(yīng)用場景就是希望給網(wǎng)絡(luò)請求設(shè)置超時時間,一旦超時就就中斷,我們這里用定時器模擬一個網(wǎng)絡(luò)請求,隨機 3 秒之內(nèi)返回。
function timeoutWrapper(p, timeout = 2000) {?const wait = new Promise((resolve, reject) => {?setTimeout(() => {?reject('請求超時')?}, timeout)?})?return Promise.race([p, wait])?
}