從基礎到進階,測試你有多瞭解 JavaScript,刷新你的知識!javascript
答案在問題下方的摺疊部分,點擊便可展開問題。html
function sayHi() {
console.log(name)
console.log(age)
var name = 'Lydia'
let age = 21
}
sayHi()
複製代碼
Lydia
和 undefined
Lydia
和 ReferenceError
ReferenceError
和 21
undefined
和 ReferenceError
在函數內部,咱們首先經過 var
關鍵字聲明瞭 name
變量。這意味着變量被提高了(內存空間在建立階段就被設置好了),直到程序運行到定義變量位置以前默認值都是 undefined
。由於當咱們打印 name
變量時尚未執行到定義變量的位置,所以變量的值保持爲 undefined
。java
經過 let
和 const
關鍵字聲明的變量也會提高,可是和 var
不一樣,它們不會被初始化。在咱們聲明(初始化)以前是不能訪問它們的。這個行爲被稱之爲暫時性死區。當咱們試圖在聲明以前訪問它們時,JavaScript 將會拋出一個 ReferenceError
錯誤。node
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
複製代碼
0 1 2
和 0 1 2
0 1 2
和 3 3 3
3 3 3
和 0 1 2
因爲 JavaScript 的事件循環,setTimeout
回調會在遍歷結束後才執行。由於在第一個遍歷中遍歷 i
是經過 var
關鍵字聲明的,因此這個值是全局做用域下的。在遍歷過程當中,咱們經過一元操做符 ++
來每次遞增 i
的值。當 setTimeout
回調執行的時候,i
的值等於 3。git
在第二個遍歷中,遍歷 i
是經過 let
關鍵字聲明的:經過 let
和 const
關鍵字聲明的變量是擁有塊級做用域(指的是任何在 {} 中的內容)。在每次的遍歷過程當中,i
都有一個新值,而且每一個值都在循環內的做用域中。github
const shape = {
radius: 10,
diameter() {
return this.radius * 2
},
perimeter: () => 2 * Math.PI * this.radius
}
shape.diameter()
shape.perimeter()
複製代碼
20
and 62.83185307179586
20
and NaN
20
and 63
NaN
and 63
注意 diameter
的值是一個常規函數,可是 perimeter
的值是一個箭頭函數。web
對於箭頭函數,this
關鍵字指向的是它當前周圍做用域(簡單來講是包含箭頭函數的常規函數,若是沒有常規函數的話就是全局對象),這個行爲和常規函數不一樣。這意味着當咱們調用 perimeter
時,this
不是指向 shape
對象,而是它的周圍做用域(在例子中是 window
)。express
在 window
中沒有 radius
這個屬性,所以返回 undefined
。json
+true;
!"Lydia";
複製代碼
1
and false
false
and NaN
false
and false
一元操做符加號嘗試將 bool 轉爲 number。true
轉換爲 number 的話爲 1
,false
爲 0
。api
字符串 'Lydia'
是一個真值,真值取反那麼就返回 false
。
const bird = {
size: 'small'
}
const mouse = {
name: 'Mickey',
small: true
}
複製代碼
mouse.bird.size
是無效的mouse[bird.size]
是無效的mouse[bird["size"]]
是無效的
在 JavaScript 中,全部對象的 keys 都是字符串(除非對象是 Symbol)。儘管咱們可能不會定義它們爲字符串,但它們在底層總會被轉換爲字符串。
當咱們使用括號語法時([]),JavaScript 會解釋(或者 unboxes)語句。它首先看到第一個開始括號 [
並繼續前進直到找到結束括號 ]
。只有這樣,它纔會計算語句的值。
mouse[bird.size]
:首先計算 bird.size
,這會獲得 small
。mouse["small"]
返回 true
。
而後使用點語法的話,上面這一切都不會發生。mouse
沒有 bird
這個 key,這也就意味着 mouse.bird
是 undefined
。而後當咱們使用點語法 mouse.bird.size
時,由於 mouse.bird
是 undefined
,這也就變成了 undefined.size
。這個行爲是無效的,而且會拋出一個錯誤相似 Cannot read property "size" of undefined
。
let c = { greeting: 'Hey!' }
let d
d = c
c.greeting = 'Hello'
console.log(d.greeting)
複製代碼
Hello
undefined
ReferenceError
TypeError
在 JavaScript 中,當設置兩個對象彼此相等時,它們會經過引用進行交互。
首先,變量 c
的值是一個對象。接下來,咱們給 d
分配了一個和 c
對象相同的引用。
所以當咱們改變其中一個對象時,實際上是改變了全部的對象。
let a = 3
let b = new Number(3)
let c = 3
console.log(a == b)
console.log(a === b)
console.log(b === c)
複製代碼
true
false
true
false
false
true
true
false
false
false
true
true
new Number()
是一個內建的函數構造器。雖然它看着像是一個 number,但它實際上並非一個真實的 number:它有一堆額外的功能而且它是一個對象。
當咱們使用 ==
操做符時,它只會檢查二者是否擁有相同的值。由於它們的值都是 3
,所以返回 true
。
而後,當咱們使用 ===
操做符時,二者的值以及類型都應該是相同的。new Number()
是一個對象而不是 number,所以返回 false
。
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor
return this.newColor
}
constructor({ newColor = 'green' } = {}) {
this.newColor = newColor
}
}
const freddie = new Chameleon({ newColor: 'purple' })
freddie.colorChange('orange')
複製代碼
orange
purple
green
TypeError
colorChange
是一個靜態方法。靜態方法被設計爲只能被建立它們的構造器使用(也就是 Chameleon
),而且不能傳遞給實例。由於 freddie
是一個實例,靜態方法不能被實例使用,所以拋出了 TypeError
錯誤。
let greeting
greetign = {} // Typo!
console.log(greetign)
複製代碼
{}
ReferenceError: greetign is not defined
undefined
代碼打印出了一個對象,這是由於咱們在全局對象上建立了一個空對象!當咱們將 greeting
寫錯成 greetign
時,JS 解釋器實際在上瀏覽器中將它視爲 global.greetign = {}
(或者 window.greetign = {}
)。
爲了不這個爲題,咱們可使用 `"use strict"。這能確保當你聲明變量時必須賦值。
function bark() {
console.log('Woof!')
}
bark.animal = 'dog'
複製代碼
SyntaxError
. 你不能經過這種方式給函數增長屬性。undefined
ReferenceError
這在 JavaScript 中是能夠的,由於函數是對象!(除了基本類型以外其餘都是對象)
函數是一個特殊的對象。你寫的這個代碼其實不是一個實際的函數。函數是一個擁有屬性的對象,而且屬性也可被調用。
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const member = new Person("Lydia", "Hallie");
Person.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
console.log(member.getFullName());
複製代碼
TypeError
SyntaxError
Lydia Hallie
undefined
undefined
你不能像常規對象那樣,給構造函數添加屬性。若是你想一次性給全部實例添加特性,你應該使用原型。所以本例中,使用以下方式:
Person.prototype.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
複製代碼
這纔會使 member.getFullName()
起做用。爲何這麼作有益的?假設咱們將這個方法添加到構造函數自己裏。也許不是每一個 Person
實例都須要這個方法。這將浪費大量內存空間,由於它們仍然具備該屬性,這將佔用每一個實例的內存空間。相反,若是咱們只將它添加到原型中,那麼它只存在於內存中的一個位置,可是全部實例均可以訪問它!
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
const lydia = new Person('Lydia', 'Hallie')
const sarah = Person('Sarah', 'Smith')
console.log(lydia)
console.log(sarah)
複製代碼
Person {firstName: "Lydia", lastName: "Hallie"}
and undefined
Person {firstName: "Lydia", lastName: "Hallie"}
and Person {firstName: "Sarah", lastName: "Smith"}
Person {firstName: "Lydia", lastName: "Hallie"}
and {}
Person {firstName: "Lydia", lastName: "Hallie"}
and ReferenceError
對於 sarah
,咱們沒有使用 new
關鍵字。當使用 new
時,this
引用咱們建立的空對象。當未使用 new
時,this
引用的是全局對象(global object)。
咱們說 this.firstName
等於 "Sarah"
,而且 this.lastName
等於 "Smith"
。實際上咱們作的是,定義了 global.firstName = 'Sarah'
和 global.lastName = 'Smith'
。而 sarah
自己是 undefined
。
在捕獲(capturing)階段中,事件從祖先元素向下傳播到目標元素。當事件達到目標(target)元素後,冒泡(bubbling)纔開始。
除了基本對象(base object),全部對象都有原型。基本對象能夠訪問一些方法和屬性,好比 .toString
。這就是爲何你可使用內置的 JavaScript 方法!全部這些方法在原型上都是可用的。雖然 JavaScript 不能直接在對象上找到這些方法,但 JavaScript 會沿着原型鏈找到它們,以便於你使用。
function sum(a, b) {
return a + b
}
sum(1, '2')
複製代碼
NaN
TypeError
"12"
3
JavaScript 是一種動態類型語言:咱們不指定某些變量的類型。值能夠在你不知道的狀況下自動轉換成另外一種類型,這種類型稱爲隱式類型轉換(implicit type coercion)。Coercion 是指將一種類型轉換爲另外一種類型。
在本例中,JavaScript 將數字 1
轉換爲字符串,以便函數有意義並返回一個值。在數字類型(1
)和字符串類型('2'
)相加時,該數字被視爲字符串。咱們能夠鏈接字符串,好比 "Hello" + "World"
,這裏發生的是 "1" + "2"
,它返回 "12"
。
let number = 0
console.log(number++)
console.log(++number)
console.log(number)
複製代碼
1
1
2
1
2
2
0
2
2
0
1
2
一元後自增運算符 ++
:
0
)1
)一元前自增運算符 ++
:
2
)2
)結果是 0 2 2
.
function getPersonInfo(one, two, three) {
console.log(one)
console.log(two)
console.log(three)
}
const person = 'Lydia'
const age = 21
getPersonInfo`${person} is ${age} years old`
複製代碼
"Lydia"
21
["", " is ", " years old"]
["", " is ", " years old"]
"Lydia"
21
"Lydia"
["", " is ", " years old"]
21
若是使用標記模板字面量,第一個參數的值老是包含字符串的數組。其他的參數獲取的是傳遞的表達式的值!
function checkAge(data) {
if (data === { age: 18 }) {
console.log('You are an adult!')
} else if (data == { age: 18 }) {
console.log('You are still an adult.')
} else {
console.log(`Hmm.. You don't have an age I guess`)
}
}
checkAge({ age: 18 })
複製代碼
You are an adult!
You are still an adult.
Hmm.. You don't have an age I guess
在測試相等性時,基本類型經過它們的值(value)進行比較,而對象經過它們的引用(reference)進行比較。JavaScript 檢查對象是否具備對內存中相同位置的引用。
題目中咱們正在比較的兩個對象不是同一個引用:做爲參數傳遞的對象引用的內存位置,與用於判斷相等的對象所引用的內存位置並不一樣。
這也是 { age: 18 } === { age: 18 }
和 { age: 18 } == { age: 18 }
都返回 false
的緣由。
function getAge(...args) {
console.log(typeof args)
}
getAge(21)
複製代碼
"number"
"array"
"object"
"NaN"
擴展運算符(...args
)會返回實參組成的數組。而數組是對象,所以 typeof args
返回 "object"
。
function getAge() {
'use strict'
age = 21
console.log(age)
}
getAge()
複製代碼
21
undefined
ReferenceError
TypeError
使用 "use strict"
,你能夠確保不會意外地聲明全局變量。咱們歷來沒有聲明變量 age
,由於咱們使用 "use strict"
,它將拋出一個引用錯誤。若是咱們不使用 "use strict"
,它就會工做,由於屬性 age
會被添加到全局對象中了。
const sum = eval('10*10+5')
複製代碼
105
"105"
TypeError
"10*10+5"
代碼以字符串形式傳遞進來,eval
對其求值。若是它是一個表達式,就像本例中那樣,它對錶達式求值。表達式是 10 * 10 + 5
。這將返回數字 105
。
sessionStorage.setItem('cool_secret', 123)
複製代碼
關閉 tab 標籤頁 後,sessionStorage
存儲的數據纔會刪除。
若是使用 localStorage
,那麼數據將永遠在那裏,除非調用了 localStorage.clear()
。
var num = 8
var num = 10
console.log(num)
複製代碼
8
10
SyntaxError
ReferenceError
使用 var
關鍵字,你能夠用相同的名稱聲明多個變量。而後變量將保存最新的值。
你不能使用 let
或 const
來實現這一點,由於它們是塊做用域的。
const obj = { 1: 'a', 2: 'b', 3: 'c' }
const set = new Set([1, 2, 3, 4, 5])
obj.hasOwnProperty('1')
obj.hasOwnProperty(1)
set.has('1')
set.has(1)
複製代碼
false
true
false
true
false
true
true
true
true
true
false
true
true
true
true
true
全部對象的鍵(不包括 Symbol)在底層都是字符串,即便你本身沒有將其做爲字符串輸入。這就是爲何 obj.hasOwnProperty('1')
也返回 true
。
對於集合,它不是這樣工做的。在咱們的集合中沒有 '1'
:set.has('1')
返回 false
。它有數字類型爲 1
,set.has(1)
返回 true
。
const obj = { a: 'one', b: 'two', a: 'three' }
console.log(obj)
複製代碼
{ a: "one", b: "two" }
{ b: "two", a: "three" }
{ a: "three", b: "two" }
SyntaxError
若是你有兩個名稱相同的鍵,則鍵會被替換掉。它仍然位於第一個鍵出現的位置,可是值是最後出現那個鍵的值。
基本執行上下文是全局執行上下文:它是代碼中隨處可訪問的內容。
for (let i = 1; i < 5; i++) {
if (i === 3) continue
console.log(i)
}
複製代碼
1
2
1
2
3
1
2
4
1
3
4
若是某個條件返回 true
,則 continue
語句跳過本次迭代。
String.prototype.giveLydiaPizza = () => {
return 'Just give Lydia pizza already!'
}
const name = 'Lydia'
name.giveLydiaPizza()
複製代碼
"Just give Lydia pizza already!"
TypeError: not a function
SyntaxError
undefined
String
是內置的構造函數,咱們能夠向它添加屬性。我只是在它的原型中添加了一個方法。基本類型字符串被自動轉換爲字符串對象,由字符串原型函數生成。所以,全部 string(string 對象)均可以訪問該方法!
const a = {}
const b = { key: 'b' }
const c = { key: 'c' }
a[b] = 123
a[c] = 456
console.log(a[b])
複製代碼
123
456
undefined
ReferenceError
對象的鍵被自動轉換爲字符串。咱們試圖將一個對象 b
設置爲對象 a
的鍵,且相應的值爲 123
。
然而,當字符串化一個對象時,它會變成 "[object Object]"
。所以這裏說的是,a["[object Object]"] = 123
。而後,咱們再一次作了一樣的事情,c
是另一個對象,這裏也有隱式字符串化,因而,a["[object Object]"] = 456
。
而後,咱們打印 a[b]
,也就是 a["[object Object]"]
。以前剛設置爲 456
,所以返回的是 456
。
const foo = () => console.log('First')
const bar = () => setTimeout(() => console.log('Second'))
const baz = () => console.log('Third')
bar()
foo()
baz()
複製代碼
First
Second
Third
First
Third
Second
Second
First
Third
Second
Third
First
咱們有一個 setTimeout
函數,並首先調用它。然而,它是最後打印日誌的。
這是由於在瀏覽器中,咱們不只有運行時引擎,還有一個叫作 WebAPI
的東西。WebAPI
提供了 setTimeout
函數,也包含其餘的,例如 DOM。
將 callback 推送到 WebAPI 後,setTimeout
函數自己(但不是回調!)將從棧中彈出。
如今,foo
被調用,打印 "First"
。
foo
從棧中彈出,baz
被調用. 打印 "Third"
。
WebAPI 不能隨時向棧內添加內容。相反,它將回調函數推到名爲 queue 的地方。
這就是事件循環開始工做的地方。一個事件循環查看棧和任務隊列。若是棧是空的,它接受隊列上的第一個元素並將其推入棧。
bar
被調用,打印 "Second"
,而後它被棧彈出。
<div onclick="console.log('first div')">
<div onclick="console.log('second div')">
<button onclick="console.log('button')">
Click!
</button>
</div>
</div>
複製代碼
div
div
button
致使事件的最深嵌套的元素是事件的 target。你能夠經過 event.stopPropagation
來中止冒泡。
<div onclick="console.log('div')">
<p onclick="console.log('p')">
Click here!
</p>
</div>
複製代碼
p
div
div
p
p
div
若是咱們點擊 p
,咱們會看到兩個日誌:p
和 div
。在事件傳播期間,有三個階段:捕獲、目標和冒泡。默認狀況下,事件處理程序在冒泡階段執行(除非將 useCapture
設置爲 true
)。它從嵌套最深的元素向外傳播。
const person = { name: 'Lydia' }
function sayHi(age) {
console.log(`${this.name} is ${age}`)
}
sayHi.call(person, 21)
sayHi.bind(person, 21)
複製代碼
undefined is 21
Lydia is 21
function
function
Lydia is 21
Lydia is 21
Lydia is 21
function
使用這兩種方法,咱們均可以傳遞咱們但願 this
關鍵字引用的對象。可是,.call
是當即執行的。
.bind
返回函數的副本,但帶有綁定上下文!它不是當即執行的。
function sayHi() {
return (() => 0)()
}
typeof sayHi()
複製代碼
"object"
"number"
"function"
"undefined"
sayHi
方法返回的是當即執行函數(IIFE)的返回值.此當即執行函數的返回值是 0
, 類型是 number
參考:只有7種內置類型:null
,undefined
,boolean
,number
,string
,object
和 symbol
。 function
不是一種類型,函數是對象,它的類型是object
。
0
new Number(0)
('')
(' ')
new Boolean(false)
undefined
複製代碼
0
, ''
, undefined
0
, new Number(0)
, ''
, new Boolean(false)
, undefined
0
, ''
, new Boolean(false)
, undefined
只有 6 種 falsy 值:
undefined
null
NaN
0
''
(empty string)false
Function
構造函數, 好比 new Number
和 new Boolean
,是 truthy。
console.log(typeof typeof 1)
複製代碼
"number"
"string"
"object"
"undefined"
typeof 1
返回 "number"
。 typeof "number"
返回 "string"
。
const numbers = [1, 2, 3]
numbers[10] = 11
console.log(numbers)
複製代碼
[1, 2, 3, 7 x null, 11]
[1, 2, 3, 11]
[1, 2, 3, 7 x empty, 11]
SyntaxError
當你爲數組設置超過數組長度的值的時候, JavaScript 會建立名爲 "empty slots" 的東西。它們的值其實是 undefined
。你會看到如下場景:
[1, 2, 3, 7 x empty, 11]
這取決於你的運行環境(每一個瀏覽器,以及 node 環境,都有可能不一樣)
(() => {
let x, y
try {
throw new Error()
} catch (x) {
(x = 1), (y = 2)
console.log(x)
}
console.log(x)
console.log(y)
})()
複製代碼
1
undefined
2
undefined
undefined
undefined
1
1
2
1
undefined
undefined
catch
代碼塊接收參數 x
。當咱們傳遞參數時,這與以前定義的變量 x
不一樣 。這個 x
是屬於 catch
塊級做用域的。
而後,咱們將塊級做用域中的變量賦值爲 1
,同時也設置了變量 y
的值。如今,咱們打印塊級做用域中的變量 x
,值爲 1
。
catch
塊以外的變量 x
的值仍爲 undefined
, y
的值爲 2
。當咱們在 catch
塊以外執行 console.log(x)
時,返回 undefined
,y
返回 2
。
JavaScript 只有基本類型和對象。
基本類型包括 boolean
, null
, undefined
, bigint
, number
, string
, symbol
。
[[0, 1], [2, 3]].reduce(
(acc, cur) => {
return acc.concat(cur)
},
[1, 2]
)
複製代碼
[0, 1, 2, 3, 1, 2]
[6, 1, 2]
[1, 2, 0, 1, 2, 3]
[1, 2, 6]
[1, 2]
是初始值。初始值將會做爲首次調用時第一個參數 acc
的值。在第一次執行時, acc
的值是 [1, 2]
, cur
的值是 [0, 1]
。合併它們,結果爲 [1, 2, 0, 1]
。 第二次執行, acc
的值是 [1, 2, 0, 1]
, cur
的值是 [2, 3]
。合併它們,最終結果爲 [1, 2, 0, 1, 2, 3]
!!null
!!''
!!1
複製代碼
false
true
false
false
false
true
false
true
true
true
true
false
null
是 falsy。 !null
的值是 true
。 !true
的值是 false
。
""
是 falsy。 !""
的值是 true
。 !true
的值是 false
。
1
是 truthy。 !1
的值是 false
。 !false
的值是 true
。
setInterval
方法的返回值是什麼?setInterval(() => console.log('Hi'), 1000)
複製代碼
undefined
setInterval
返回一個惟一的 id。此 id 可被用於 clearInterval
函數來取消定時。
[...'Lydia']
複製代碼
["L", "y", "d", "i", "a"]
["Lydia"]
[[], "Lydia"]
[["L", "y", "d", "i", "a"]]
string 類型是可迭代的。擴展運算符將迭代的每一個字符映射成一個元素。
function* generator(i) {
yield i;
yield i * 2;
}
const gen = generator(10);
console.log(gen.next().value);
console.log(gen.next().value);
複製代碼
[0, 10], [10, 20]
20, 20
10, 20
0, 10 and 10, 20
通常的函數在執行以後是不能中途停下的。可是,生成器函數卻能夠中途「停下」,以後能夠再從停下的地方繼續。當生成器遇到yield
關鍵字的時候,會生成yield
後面的值。注意,生成器在這種狀況下不 返回 (return )值,而是 生成 (yield)值。
首先,咱們用10
做爲參數i
來初始化生成器函數。而後使用next()
方法一步步執行生成器。第一次執行生成器的時候,i
的值爲10
,遇到第一個yield
關鍵字,它要生成i
的值。此時,生成器「暫停」,生成了10
。
而後,咱們再執行next()
方法。生成器會從剛纔暫停的地方繼續,這個時候i
仍是10
。因而咱們走到了第二個yield
關鍵字處,這時候須要生成的值是i*2
,i
爲10
,那麼此時生成的值即是20
。因此這道題的最終結果是10,20
。
const firstPromise = new Promise((res, rej) => {
setTimeout(res, 500, "one");
});
const secondPromise = new Promise((res, rej) => {
setTimeout(res, 100, "two");
});
Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
複製代碼
"one"
"two"
"two" "one"
"one" "two"
當咱們向Promise.race
方法中傳入多個Promise
時,會進行 優先 解析。在這個例子中,咱們用setTimeout
給firstPromise
和secondPromise
分別設定了500ms和100ms的定時器。這意味着secondPromise
會首先解析出字符串two
。那麼此時res
參數即爲two
,是爲輸出結果。
let person = { name: "Lydia" };
const members = [person];
person = null;
console.log(members);
複製代碼
null
[null]
[{}]
[{ name: "Lydia" }]
首先咱們聲明瞭一個擁有name
屬性的對象 person
。
而後咱們又聲明瞭一個變量members
. 將首個元素賦值爲變量person
。 當設置兩個對象彼此相等時,它們會經過 引用 進行交互。可是當你將引用從一個變量分配至另外一個變量時,其實只是執行了一個 複製 操做。(注意一點,他們的引用 並不相同!)
接下來咱們讓person
等於null
。
咱們沒有修改數組第一個元素的值,而只是修改了變量person
的值,由於元素(複製而來)的引用與person
不一樣。members
的第一個元素仍然保持着對原始對象的引用。當咱們輸出members
數組時,第一個元素會將引用的對象打印出來。
const person = {
name: "Lydia",
age: 21
};
for (const item in person) {
console.log(item);
}
複製代碼
{ name: "Lydia" }, { age: 21 }
"name", "age"
"Lydia", 21
["name", "Lydia"], ["age", 21]
在for-in
循環中,咱們能夠經過對象的key來進行迭代,也就是這裏的name
和age
。在底層,對象的key都是字符串(若是他們不是Symbol的話)。在每次循環中,咱們將item
設定爲當前遍歷到的key.因此一開始,item
是name
,以後 item
輸出的則是age
。
console.log(3 + 4 + "5");
複製代碼
"345"
"75"
12
"12"
當全部運算符的 優先級 相同時,計算表達式須要肯定運算符的結合順序,即從右到左仍是從左往右。在這個例子中,咱們只有一類運算符+
,對於加法來講,結合順序就是從左到右。
3 + 4
首先計算,獲得數字7
.
因爲類型的強制轉換,7 + '5'
的結果是"75"
. JavaScript將7
轉換成了字符串,能夠參考問題15.咱們能夠用+
號把兩個字符串鏈接起來。 "7" + "5"
就獲得了"75"
.
num
的值是什麼?const num = parseInt("7*6", 10);
複製代碼
42
"42"
7
NaN
只返回了字符串中第一個字母. 設定了 進制 後 (也就是第二個參數,指定須要解析的數字是什麼進制: 十進制、十六機制、八進制、二進制等等……),parseInt
檢查字符串中的字符是否合法. 一旦遇到一個在指定進制中不合法的字符後,當即中止解析而且忽略後面全部的字符。
*
就是不合法的數字字符。因此只解析到"7"
,並將其解析爲十進制的7
. num
的值即爲7
.
[1, 2, 3].map(num => {
if (typeof num === "number") return;
return num * 2;
});
複製代碼
[]
[null, null, null]
[undefined, undefined, undefined]
[ 3 x empty ]
對數組進行映射的時候,num
就是當前循環到的元素. 在這個例子中,全部的映射都是number類型,因此if中的判斷typeof num === "number"
結果都是true
.map函數建立了新數組而且將函數的返回值插入數組。
可是,沒有任何值返回。當函數沒有返回任何值時,即默認返回undefined
.對數組中的每個元素來講,函數塊都獲得了這個返回值,因此結果中每個元素都是undefined
.
function getInfo(member, year) {
member.name = "Lydia";
year = "1998";
}
const person = { name: "Sarah" };
const birthYear = "1997";
getInfo(person, birthYear);
console.log(person, birthYear);
複製代碼
{ name: "Lydia" }, "1997"
{ name: "Sarah" }, "1998"
{ name: "Lydia" }, "1998"
{ name: "Sarah" }, "1997"
普通參數都是 值 傳遞的,而對象則不一樣,是 引用 傳遞。因此說,birthYear
是值傳遞,由於他是個字符串而不是對象。當咱們對參數進行值傳遞時,會建立一份該值的 複製 。(能夠參考問題46)
變量birthYear
有一個對"1997"
的引用,而傳入的參數也有一個對"1997"
的引用,但兩者的引用並不相同。當咱們經過給 year
賦值"1998"
來更新year
的值的時候咱們只是更新了year
(的引用)。此時birthYear
仍然是"1997"
.
而person
是個對象。參數member
引用與之 相同的 對象。當咱們修改member
所引用對象的屬性時,person
的相應屬性也被修改了,由於他們引用了相同的對象. person
的 name
屬性也變成了 "Lydia"
.
function greeting() {
throw "Hello world!";
}
function sayHi() {
try {
const data = greeting();
console.log("It worked!", data);
} catch (e) {
console.log("Oh no an error!", e);
}
}
sayHi();
複製代碼
"It worked! Hello world!"
"Oh no an error: undefined
SyntaxError: can only throw Error objects
"Oh no an error: Hello world!
經過throw
語句,我麼能夠建立自定義錯誤。 而經過它,咱們能夠拋出異常。異常能夠是一個字符串, 一個 數字, 一個 布爾類型 或者是一個 對象。在本例中,咱們的異常是字符串'Hello world'
.
經過 catch
語句,咱們能夠設定當try
語句塊中拋出異常後應該作什麼處理。在本例中拋出的異常是字符串'Hello world'
. e
就是這個字符串,所以被輸出。最終結果就是'Oh an error: Hello world'
.
function Car() {
this.make = "Lamborghini";
return { make: "Maserati" };
}
const myCar = new Car();
console.log(myCar.make);
複製代碼
"Lamborghini"
"Maserati"
ReferenceError
TypeError
返回屬性的時候,屬性的值等於 返回的 值,而不是構造函數中設定的值。咱們返回了字符串 "Maserati"
,因此 myCar.make
等於"Maserati"
.
(() => {
let x = (y = 10);
})();
console.log(typeof x);
console.log(typeof y);
複製代碼
"undefined", "number"
"number", "number"
"object", "number"
"number", "undefined"
let x = y = 10;
是下面這個表達式的縮寫:
y = 10;
let x = y;
複製代碼
咱們設定y
等於10
時,咱們實際上增長了一個屬性y
給全局對象(瀏覽器裏的window
, Nodejs裏的global
)。在瀏覽器中, window.y
等於10
.
而後咱們聲明瞭變量x
等於y
,也是10
.但變量是使用 let
聲明的,它只做用於 塊級做用域, 僅在聲明它的塊中有效;就是案例中的當即調用表達式(IIFE)。使用typeof
操做符時, 操做值 x
沒有被定義:由於咱們在x
聲明塊的外部,沒法調用它。這就意味着x
未定義。未分配或是未聲明的變量類型爲"undefined"
. console.log(typeof x)
返回"undefined"
.
而咱們建立了全局變量y
,而且設定y
等於10
.這個值在咱們的代碼各處都訪問的到。 y
已經被定義了,並且有一個"number"
類型的值。 console.log(typeof y)
返回"number"
.
class Dog {
constructor(name) {
this.name = name;
}
}
Dog.prototype.bark = function() {
console.log(`Woof I am ${this.name}`);
};
const pet = new Dog("Mara");
pet.bark();
delete Dog.prototype.bark;
pet.bark();
複製代碼
"Woof I am Mara"
, TypeError
"Woof I am Mara"
,"Woof I am Mara"
"Woof I am Mara"
, undefined
TypeError
, TypeError
咱們能夠用delete
關鍵字刪除對象的屬性,對原型也是適用的。刪除了原型的屬性後,該屬性在原型鏈上就不可用了。在本例中,函數bark
在執行了delete Dog.prototype.bark
後不可用, 然然後面的代碼還在調用它。
當咱們嘗試調用一個不存在的函數時TypeError
異常會被拋出。在本例中就是 TypeError: pet.bark is not a function
,由於pet.bark
是undefined
.
const set = new Set([1, 1, 2, 3, 4]);
console.log(set);
複製代碼
[1, 1, 2, 3, 4]
[1, 2, 3, 4]
{1, 1, 2, 3, 4}
{1, 2, 3, 4}
Set
對象是獨一無二的值的集合:也就是說同一個值在其中僅出現一次。
咱們傳入了數組[1, 1, 2, 3, 4]
,他有一個重複值1
.覺得一個集合裏不能有兩個重複的值,其中一個就被移除了。因此結果是 {1, 2, 3, 4}
.
// counter.js
let counter = 10;
export default counter;
複製代碼
// index.js
import myCounter from "./counter";
myCounter += 1;
console.log(myCounter);
複製代碼
10
11
Error
NaN
引入的模塊是 只讀 的: 你不能修改引入的模塊。只有導出他們的模塊才能修改其值。
當咱們給myCounter
增長一個值的時候會拋出一個異常: myCounter
是隻讀的,不能被修改。
const name = "Lydia";
age = 21;
console.log(delete name);
console.log(delete age);
複製代碼
false
, true
"Lydia"
, 21
true
, true
undefined
, undefined
delete
操做符返回一個布爾值: true
指刪除成功,不然返回false
. 可是經過 var
, const
或 let
關鍵字聲明的變量沒法用 delete
操做符來刪除。
name
變量由const
關鍵字聲明,因此刪除不成功:返回 false
. 而咱們設定age
等於21
時,咱們實際上添加了一個名爲age
的屬性給全局對象。對象中的屬性是能夠刪除的,全局對象也是如此,因此delete age
返回true
.
const numbers = [1, 2, 3, 4, 5];
const [y] = numbers;
console.log(y);
複製代碼
[[1, 2, 3, 4, 5]]
[1, 2, 3, 4, 5]
1
[1]
咱們能夠經過解構賦值來解析來自對象的數組或屬性的值,好比說:
[a, b] = [1, 2];
複製代碼
a
的值如今是1
,b
的值如今是2
.而在題目中,咱們是這麼作的:
[y] = [1, 2, 3, 4, 5];
複製代碼
也就是說,y
等於數組的第一個值就是數字1
.咱們輸出y
, 返回1
.
const user = { name: "Lydia", age: 21 };
const admin = { admin: true, ...user };
console.log(admin);
複製代碼
{ admin: true, user: { name: "Lydia", age: 21 } }
{ admin: true, name: "Lydia", age: 21 }
{ admin: true, user: ["Lydia", 21] }
{ admin: true }
擴展運算符...
爲對象的組合提供了可能。你能夠複製對象中的鍵值對,而後把它們加到另外一個對象裏去。在本例中,咱們複製了user
對象鍵值對,而後把它們加入到admin
對象中。admin
對象就擁有了這些鍵值對,因此結果爲{ admin: true, name: "Lydia", age: 21 }
.
const person = { name: "Lydia" };
Object.defineProperty(person, "age", { value: 21 });
console.log(person);
console.log(Object.keys(person));
複製代碼
{ name: "Lydia", age: 21 }
, ["name", "age"]
{ name: "Lydia", age: 21 }
, ["name"]
{ name: "Lydia"}
, ["name", "age"]
{ name: "Lydia"}
, ["age"]
經過defineProperty
方法,咱們能夠給對象添加一個新屬性,或者修改已經存在的屬性。而咱們使用defineProperty
方法給對象添加了一個屬性以後,屬性默認爲 不可枚舉(not enumerable). Object.keys
方法僅返回對象中 可枚舉(enumerable) 的屬性,所以只剩下了"name"
.
用defineProperty
方法添加的屬性默認不可變。你能夠經過writable
, configurable
和 enumerable
屬性來改變這一行爲。這樣的話, 相比於本身添加的屬性,defineProperty
方法添加的屬性有了更多的控制權。
const settings = {
username: "lydiahallie",
level: 19,
health: 90
};
const data = JSON.stringify(settings, ["level", "health"]);
console.log(data);
複製代碼
"{"level":19, "health":90}"
"{"username": "lydiahallie"}"
"["level", "health"]"
"{"username": "lydiahallie", "level":19, "health":90}"
JSON.stringify
的第二個參數是 替代者(replacer). 替代者(replacer)能夠是個函數或數組,用以控制哪些值如何被轉換爲字符串。
若是替代者(replacer)是個 數組 ,那麼就只有包含在數組中的屬性將會被轉化爲字符串。在本例中,只有名爲"level"
和 "health"
的屬性被包括進來, "username"
則被排除在外。 data
就等於 "{"level":19, "health":90}"
.
而若是替代者(replacer)是個 函數,這個函數將被對象的每一個屬性都調用一遍。 函數返回的值會成爲這個屬性的值,最終體如今轉化後的JSON字符串中(譯者注:Chrome下,通過實驗,若是全部屬性均返回同一個值的時候有異常,會直接將返回值做爲結果輸出而不會輸出JSON字符串),而若是返回值爲undefined
,則該屬性會被排除在外。
let num = 10;
const increaseNumber = () => num++;
const increasePassedNumber = number => number++;
const num1 = increaseNumber();
const num2 = increasePassedNumber(num1);
console.log(num1);
console.log(num2);
複製代碼
10
, 10
10
, 11
11
, 11
11
, 12
一元操做符 ++
先返回 操做值, 再累加 操做值。num1
的值是10
, 由於increaseNumber
函數首先返回num
的值,也就是10
,隨後再進行 num
的累加。
num2
是10
由於咱們將 num1
傳入increasePassedNumber
. number
等於10
(num1
的值。一樣道理,++
先返回 操做值, 再累加 操做值。) number
是10
,因此num2
也是10
.
const value = { number: 10 };
const multiply = (x = { ...value }) => {
console.log(x.number *= 2);
};
multiply();
multiply();
multiply(value);
multiply(value);
複製代碼
20
, 40
, 80
, 160
20
, 40
, 20
, 40
20
, 20
, 20
, 40
NaN
, NaN
, 20
, 40
在ES6中,咱們可使用默認值初始化參數。若是沒有給函數傳參,或者傳的參值爲 "undefined"
,那麼參數的值將是默認值。上述例子中,咱們將 value
對象進行了解構並傳到一個新對象中,所以 x
的默認值爲 {number:10}
。
默認參數在調用時纔會進行計算,每次調用函數時,都會建立一個新的對象。咱們前兩次調用 multiply
函數且不傳遞值,那麼每一次 x
的默認值都爲 {number:10}
,所以打印出該數字的乘積值爲20
。
第三次調用 multiply
時,咱們傳遞了一個參數,即對象value
。 *=
運算符其實是x.number = x.number * 2
的簡寫,咱們修改了x.number
的值,並打印出值20
。
第四次,咱們再次傳遞value
對象。 x.number
以前被修改成20
,因此x.number * = 2
打印爲40
。
[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
複製代碼
1
2
and 3
3
and 6
4
1
2
and 2
3
and 3
4
1
undefined
and 2
undefined
and 3
undefined
and 4
undefined
1
2
and undefined
3
and undefined
4
reducer
函數接收4個參數:
reducer
函數的返回值將會分配給累計器,該返回值在數組的每一個迭代中被記住,並最後成爲最終的單個結果值。
reducer
函數還有一個可選參數initialValue
, 該參數將做爲第一次調用回調函數時的第一個參數的值。若是沒有提供initialValue
,則將使用數組中的第一個元素。
在上述例子,reduce
方法接收的第一個參數(Accumulator)是x
, 第二個參數(Current Value)是y
。
在第一次調用時,累加器x
爲1
,當前值「y」
爲2
,打印出累加器和當前值:1
和2
。
例子中咱們的回調函數沒有返回任何值,只是打印累加器的值和當前值。若是函數沒有返回值,則默認返回undefined
。 在下一次調用時,累加器爲undefined
,當前值爲「3」, 所以undefined
和3
被打印出。
在第四次調用時,回調函數依然沒有返回值。 累加器再次爲 undefined
,當前值爲「4」。 undefined
和4
被打印出。
Dog
類?class Dog {
constructor(name) {
this.name = name;
}
};
class Labrador extends Dog {
// 1
constructor(name, size) {
this.size = size;
}
// 2
constructor(name, size) {
super(name);
this.size = size;
}
// 3
constructor(size) {
super(name);
this.size = size;
}
// 4
constructor(name, size) {
this.name = name;
this.size = size;
}
};
複製代碼
在子類中,在調用super
以前不能訪問到this
關鍵字。 若是這樣作,它將拋出一個ReferenceError
:1和4將引起一個引用錯誤。
使用super
關鍵字,須要用給定的參數來調用父類的構造函數。 父類的構造函數接收name
參數,所以咱們須要將name
傳遞給super
。
Labrador
類接收兩個參數,name
參數是因爲它繼承了Dog
,size
做爲Labrador
類的額外屬性,它們都須要傳遞給Labrador
的構造函數,所以使用構造函數2正確完成。
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));
// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
複製代碼
running index.js
, running sum.js
, 3
running sum.js
, running index.js
, 3
running sum.js
, 3
, running index.js
running index.js
, undefined
, running sum.js
import
命令是編譯階段執行的,在代碼運行以前。所以這意味着被導入的模塊會先運行,而導入模塊的文件會後執行。
這是CommonJS中require()
和import
之間的區別。使用require()
,您能夠在運行代碼時根據須要加載依賴項。 若是咱們使用require
而不是import
,running index.js
,running sum.js
,3
會被依次打印。
console.log(Number(2) === Number(2))
console.log(Boolean(false) === Boolean(false))
console.log(Symbol('foo') === Symbol('foo'))
複製代碼
true
, true
, false
false
, true
, false
true
, false
, true
true
, true
, true
每一個Symbol
都是徹底惟一的。傳遞給Symbol
的參數只是給Symbol
的一個描述。 Symbol
的值不依賴於傳遞的參數。 當咱們測試相等時,咱們建立了兩個全新的符號:第一個Symbol('foo')
,第二個Symbol('foo')
, 這兩個值是惟一的,彼此不相等,所以返回false
。
const name = "Lydia Hallie"
console.log(name.padStart(13))
console.log(name.padStart(2))
複製代碼
"Lydia Hallie"
, "Lydia Hallie"
" Lydia Hallie"
, " Lydia Hallie"
("[13x whitespace]Lydia Hallie"
, "[2x whitespace]Lydia Hallie"
)" Lydia Hallie"
, "Lydia Hallie"
("[1x whitespace]Lydia Hallie"
, "Lydia Hallie"
)"Lydia Hallie"
, "Lyd"
使用padStart
方法,咱們能夠在字符串的開頭添加填充。傳遞給此方法的參數是字符串的總長度(包含填充)。字符串Lydia Hallie
的長度爲12
, 所以name.padStart(13)
在字符串的開頭只會插入1(13 - 12 = 1
)個空格。
若是傳遞給padStart
方法的參數小於字符串的長度,則不會添加填充。
console.log("🥑" + "💻");
複製代碼
"🥑💻"
257548
使用+
運算符,您能夠鏈接字符串。 上述狀況,咱們將字符串「🥑」
與字符串」💻「
鏈接起來,產生」🥑💻「
。
console.log
語句後註釋掉的值?function* startGame() {
const answer = yield "Do you love JavaScript?";
if (answer !== "Yes") {
return "Oh wow... Guess we're gone here";
}
return "JavaScript loves you back ❤️";
}
const game = startGame();
console.log(/* 1 */); // Do you love JavaScript?
console.log(/* 2 */); // JavaScript loves you back ❤️
複製代碼
game.next("Yes").value
and game.next().value
game.next.value("Yes")
and game.next.value()
game.next().value
and game.next("Yes").value
game.next.value()
and game.next.value("Yes")
generator
函數在遇到yield
關鍵字時會「暫停」其執行。 首先,咱們須要讓函數產生字符串Do you love JavaScript?
,這能夠經過調用game.next().value
來完成。上述函數的第一行就有一個yield
關鍵字,那麼運行當即中止了,yield
表達式自己沒有返回值,或者說老是返回undefined
, 這意味着此時變量 answer
爲undefined
next
方法能夠帶一個參數,該參數會被看成上一個 yield
表達式的返回值。當咱們調用game.next("Yes").value
時,先前的 yield
的返回值將被替換爲傳遞給next()
函數的參數"Yes"
。此時變量 answer
被賦值爲 "Yes"
,if
語句返回false
,因此JavaScript loves you back ❤️
被打印。
console.log(String.raw`Hello\nworld`);
複製代碼
Hello world!
Hello
world
Hello\nworld
Hello\n
world
String.raw
函數是用來獲取一個模板字符串的原始字符串的,它返回一個字符串,其中忽略了轉義符(\n
,\v
,\t
等)。但反斜槓可能形成問題,由於你可能會遇到下面這種相似狀況:
const path = `C:\Documents\Projects\table.html`
String.raw`${path}`
複製代碼
這將致使:
"C:DocumentsProjects able.html"
直接使用String.raw
String.raw`C:\Documents\Projects\table.html`
複製代碼
它會忽略轉義字符並打印:C:\Documents\Projects\table.html
上述狀況,字符串是Hello\nworld
被打印出。
async function getData() {
return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);
複製代碼
"I made it!"
Promise {<resolved>: "I made it!"}
Promise {<pending>}
undefined
異步函數始終返回一個promise。await
仍然須要等待promise的解決:當咱們調用getData()
並將其賦值給data
,此時data
爲getData
方法返回的一個掛起的promise,該promise並無解決。
若是咱們想要訪問已解決的值"I made it!"
,能夠在data
上使用.then()
方法:
data.then(res => console.log(res))
這樣將打印 "I made it!"
function addToList(item, list) {
return list.push(item);
}
const result = addToList("apple", ["banana"]);
console.log(result);
複製代碼
['apple', 'banana']
2
true
undefined
push()
方法返回新數組的長度。一開始,數組包含一個元素(字符串"banana"
),長度爲1。 在數組中添加字符串"apple"
後,長度變爲2,並將從addToList
函數返回。
push
方法修改原始數組,若是你想從函數返回數組而不是數組長度,那麼應該在push item
以後返回list
。
const box = { x: 10, y: 20 };
Object.freeze(box);
const shape = box;
shape.x = 100;
console.log(shape)
複製代碼
{ x: 100, y: 20 }
{ x: 10, y: 20 }
{ x: 100 }
ReferenceError
Object.freeze
使得沒法添加、刪除或修改對象的屬性(除非屬性的值是另外一個對象)。
當咱們建立變量shape
並將其設置爲等於凍結對象box
時,shape
指向的也是凍結對象。你可使用Object.isFrozen
檢查一個對象是否被凍結,上述狀況,Object.isFrozen(shape)
將返回true
。
因爲shape
被凍結,而且x
的值不是對象,因此咱們不能修改屬性x
。 x
仍然等於10
,{x:10,y:20}
被打印。
注意,上述例子咱們對屬性x
進行修改,可能會致使拋出TypeError異常(最多見但不只限於嚴格模式下時)。
const { name: myName } = { name: "Lydia" };
console.log(name);
複製代碼
"Lydia"
"myName"
undefined
ReferenceError
當咱們從右側的對象解構屬性name
時,咱們將其值Lydia
分配給名爲myName
的變量。
使用{name:myName}
,咱們是在告訴JavaScript咱們要建立一個名爲myName
的新變量,而且其值是右側對象的name
屬性的值。
當咱們嘗試打印name
,一個未定義的變量時,就會引起ReferenceError
。
function sum(a, b) {
return a + b;
}
複製代碼
純函數一種若輸入參數相同,則永遠會獲得相同輸出的函數。
sum
函數老是返回相同的結果。 若是咱們傳遞1
和2
,它將老是返回3
而沒有反作用。 若是咱們傳遞5
和10
,它將老是返回15
,依此類推,這是純函數的定義。
const add = () => {
const cache = {};
return num => {
if (num in cache) {
return `From cache! ${cache[num]}`;
} else {
const result = num + 10;
cache[num] = result;
return `Calculated! ${result}`;
}
};
};
const addFunction = add();
console.log(addFunction(10));
console.log(addFunction(10));
console.log(addFunction(5 * 2));
複製代碼
Calculated! 20
Calculated! 20
Calculated! 20
Calculated! 20
From cache! 20
Calculated! 20
Calculated! 20
From cache! 20
From cache! 20
Calculated! 20
From cache! 20
Error
add
函數是一個記憶函數。 經過記憶化,咱們能夠緩存函數的結果,以加快其執行速度。上述狀況,咱們建立一個cache
對象,用於存儲先前返回過的值。
若是咱們使用相同的參數屢次調用addFunction
函數,它首先檢查緩存中是否已有該值,若是有,則返回緩存值,這將節省執行時間。若是沒有,那麼它將計算該值,並存儲在緩存中。
咱們用相同的值三次調用了addFunction
函數:
在第一次調用,num
等於10
時函數的值還沒有緩存,if語句num in cache
返回false
,else塊的代碼被執行:Calculated! 20
,而且其結果被添加到緩存對象,cache
如今看起來像{10:20}
。
第二次,cache
對象包含10
的返回值。 if語句 num in cache
返回true
,From cache! 20
被打印。
第三次,咱們將5 * 2
(值爲10)傳遞給函數。 cache
對象包含10
的返回值。 if語句 num in cache
返回true
,From cache! 20
被打印。
const myLifeSummedUp = ["☕", "💻", "🍷", "🍫"]
for (let item in myLifeSummedUp) {
console.log(item)
}
for (let item of myLifeSummedUp) {
console.log(item)
}
複製代碼
0
1
2
3
and "☕"
"💻"
"🍷"
"🍫"
"☕"
"💻"
"🍷"
"🍫"
and "☕"
"💻"
"🍷"
"🍫"
"☕"
"💻"
"🍷"
"🍫"
and 0
1
2
3
0
1
2
3
and {0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}
經過for-in
循環,咱們能夠遍歷一個對象自有的、繼承的、可枚舉的、非Symbol的屬性。 在數組中,可枚舉屬性是數組元素的「鍵」, 即它們的索引。 相似於下面這個對象:
{0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}
其中鍵則是可枚舉屬性,所以 0
,1
,2
,3
被記錄。
經過for-of
循環,咱們能夠迭代可迭代對象(包括 Array
,Map
,Set
,String
,arguments
等)。當咱們迭代數組時,在每次迭代中,不一樣屬性的值將被分配給變量item
, 所以「☕」
,「💻」
,「🍷」
,「🍫」
被打印。
const list = [1 + 2, 1 * 2, 1 / 2]
console.log(list)
複製代碼
["1 + 2", "1 * 2", "1 / 2"]
["12", 2, 0.5]
[3, 2, 0.5]
[1, 1, 1]
數組元素能夠包含任何值。 數字,字符串,布爾值,對象,數組,null
,undeifned
, 以及其餘表達式,如日期,函數和計算。
元素將等於返回的值。 1 + 2
返回3
,1 * 2
返回'2,'1 / 2
返回0.5
。
function sayHi(name) {
return `Hi there, ${name}`
}
console.log(sayHi())
複製代碼
Hi there,
Hi there, undefined
Hi there, null
ReferenceError
默認狀況下,若是不給函數傳參,參數的值將爲undefined
。 上述狀況,咱們沒有給參數name
傳值。 name
等於undefined
,並被打印。
在ES6中,咱們可使用默認參數覆蓋此默認的undefined
值。 例如:
function sayHi(name =「Lydia」){...}
在這種狀況下,若是咱們沒有傳遞值或者若是咱們傳遞undefined
,name
老是等於字符串Lydia
var status = "😎"
setTimeout(() => {
const status = "😍"
const data = {
status: "🥑",
getStatus() {
return this.status
}
}
console.log(data.getStatus())
console.log(data.getStatus.call(this))
}, 0)
複製代碼
"🥑"
and "😍"
"🥑"
and "😎"
"😍"
and "😎"
"😎"
and "😎"
this
關鍵字的指向取決於使用它的位置。 在函數中,好比getStatus
,this
指向的是調用它的對象,上述例子中data
對象調用了getStatus
,所以this
指向的就是data
對象。 當咱們打印this.status
時,data
對象的status
屬性被打印,即"🥑"
。
使用call
方法,能夠更改this
指向的對象。data.getStatus.call(this)
是將this
的指向由data
對象更改成全局對象。在全局對象上,有一個名爲status
的變量,其值爲」😎「
。 所以打印this.status
時,會打印「😎」
。
const person = {
name: "Lydia",
age: 21
}
let city = person.city
city = "Amsterdam"
console.log(person)
複製代碼
{ name: "Lydia", age: 21 }
{ name: "Lydia", age: 21, city: "Amsterdam" }
{ name: "Lydia", age: 21, city: undefined }
"Amsterdam"
咱們將變量city
設置爲等於person
對象上名爲city
的屬性的值。 這個對象上沒有名爲city
的屬性,所以變量city
的值爲undefined
。
請注意,咱們沒有引用person
對象自己,只是將變量city
設置爲等於person
對象上city
屬性的當前值。
而後,咱們將city
設置爲等於字符串「Amsterdam」
。 這不會更改person對象:沒有對該對象的引用。
所以打印person
對象時,會返回未修改的對象。
function checkAge(age) {
if (age < 18) {
const message = "Sorry, you're too young."
} else {
const message = "Yay! You're old enough!"
}
return message
}
console.log(checkAge(21))
複製代碼
"Sorry, you're too young."
"Yay! You're old enough!"
ReferenceError
undefined
const
和let
聲明的變量是具備塊級做用域的,塊是大括號({}
)之間的任何東西, 即上述狀況if / else
語句的花括號。 因爲塊級做用域,咱們沒法在聲明的塊以外引用變量,所以拋出ReferenceError
。
fetch('https://www.website.com/api/user/1')
.then(res => res.json())
.then(res => console.log(res))
複製代碼
fetch
方法的結果fetch
方法的結果.then()
中回調方法返回的結果undefined
第二個.then
中res
的值等於前一個.then
中的回調函數返回的值。 你能夠像這樣繼續連接.then
,將值傳遞給下一個處理程序。
hasName
設置爲true
的方法,前提是不能將true
做爲參數傳遞?function getName(name) {
const hasName = //
}
複製代碼
!!name
name
new Boolean(name)
name.length
使用邏輯非運算符!
,將返回一個布爾值,使用!! name
,咱們能夠肯定name
的值是真的仍是假的。 若是name
是真實的,那麼!name
返回false
。 !false
返回true
。
經過將hasName
設置爲name
,能夠將hasName
設置爲等於傳遞給getName
函數的值,而不是布爾值true
。
new Boolean(true)
返回一個對象包裝器,而不是布爾值自己。
name.length
返回傳遞的參數的長度,而不是布爾值true
。
console.log("I want pizza"[0])
複製代碼
"""
"I"
SyntaxError
undefined
可使用方括號表示法獲取字符串中特定索引的字符,字符串中的第一個字符具備索引0,依此類推。 在這種狀況下,咱們想要獲得索引爲0的元素,字符'I'
被記錄。
請注意,IE7及更低版本不支持此方法。 在這種狀況下,應該使用.charAt()
function sum(num1, num2 = num1) {
console.log(num1 + num2)
}
sum(10)
複製代碼
NaN
20
ReferenceError
undefined
您能夠將默認參數的值設置爲函數的另外一個參數,只要另外一個參數定義在其以前便可。 咱們將值10
傳遞給sum
函數。 若是sum
函數只接收1個參數,則意味着沒有傳遞num2
的值,這種狀況下,num1
的值等於傳遞的值10
。 num2
的默認值是num1
的值,即10
。 num1 + num2
返回20
。
若是您嘗試將默認參數的值設置爲後面定義的參數,則可能致使參數的值還沒有初始化,從而引起錯誤。好比:
function test(m = n, n = 2) {
console.log(m, n)
}
test() // Uncaught ReferenceError: Cannot access 'n' before initialization
test(3) // 3 2
test(3, 4) // 3 4
複製代碼
// module.js
export default () => "Hello world"
export const name = "Lydia"
// index.js
import * as data from "./module"
console.log(data)
複製代碼
{ default: function default(), name: "Lydia" }
{ default: function default() }
{ default: "Hello world", name: "Lydia" }
module.js
使用import * as name
語法,咱們將module.js
文件中全部export
導入到index.js
文件中,而且建立了一個名爲data
的新對象。 在module.js
文件中,有兩個導出:默認導出和命名導出。 默認導出是一個返回字符串「Hello World」的函數,命名導出是一個名爲name
的變量,其值爲字符串「Lydia」
。
data
對象具備默認導出的default
屬性,其餘屬性具備指定exports的名稱及其對應的值。
class Person {
constructor(name) {
this.name = name
}
}
const member = new Person("John")
console.log(typeof member)
複製代碼
"class"
"function"
"object"
"string"
類是構造函數的語法糖,若是用構造函數的方式來重寫Person
類則將是:
function Person() {
this.name = name
}
複製代碼
經過new
來調用構造函數,將會生成構造函數Person
的實例,對實例執行typeof
關鍵字將返回"object"
,上述狀況打印出"object"
。
let newList = [1, 2, 3].push(4)
console.log(newList.push(5))
複製代碼
[1, 2, 3, 4, 5]
[1, 2, 3, 5]
[1, 2, 3, 4]
Error
.push
方法返回數組的長度,而不是數組自己! 經過將newList
設置爲[1,2,3].push(4)
,實際上newList
等於數組的新長度:4
。
而後,嘗試在newList
上使用.push
方法。 因爲newList
是數值4
,拋出TypeError。
function giveLydiaPizza() {
return "Here is pizza!"
}
const giveLydiaChocolate = () => "Here's chocolate... now go hit the gym already."
console.log(giveLydiaPizza.prototype)
console.log(giveLydiaChocolate.prototype)
複製代碼
{ constructor: ...}
{ constructor: ...}
{}
{ constructor: ...}
{ constructor: ...}
{}
{ constructor: ...}
undefined
常規函數,例如giveLydiaPizza
函數,有一個prototype
屬性,它是一個帶有constructor
屬性的對象(原型對象)。 然而,箭頭函數,例如giveLydiaChocolate
函數,沒有這個prototype
屬性。 嘗試使用giveLydiaChocolate.prototype
訪問prototype
屬性時會返回undefined
。
const person = {
name: "Lydia",
age: 21
}
for (const [x, y] of Object.entries(person)) {
console.log(x, y)
}
複製代碼
name
Lydia
and age
21
["name", "Lydia"]
and ["age", 21]
["name", "age"]
and undefined
Error
Object.entries()
方法返回一個給定對象自身可枚舉屬性的鍵值對數組,上述狀況返回一個二維數組,數組每一個元素是一個包含鍵和值的數組:
[['name','Lydia'],['age',21]]
使用for-of
循環,咱們能夠迭代數組中的每一個元素,上述狀況是子數組。 咱們可使用const [x,y]
在for-of
循環中解構子數組。 x
等於子數組中的第一個元素,y
等於子數組中的第二個元素。
第一個子陣列是[「name」,「Lydia」]
,其中x
等於name
,而y
等於Lydia
。 第二個子陣列是[「age」,21]
,其中x
等於age
,而y
等於21
。
function getItems(fruitList, ...args, favoriteFruit) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange")
複製代碼
["banana", "apple", "pear", "orange"]
[["banana", "apple"], "pear", "orange"]
["banana", "apple", ["pear"], "orange"]
SyntaxError
... args
是剩餘參數,剩餘參數的值是一個包含全部剩餘參數的數組,而且只能做爲最後一個參數。上述示例中,剩餘參數是第二個參數,這是不可能的,並會拋出語法錯誤。
function getItems(fruitList, favoriteFruit, ...args) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange")
複製代碼
上述例子是有效的,將會返回數組:[ 'banana', 'apple', 'orange', 'pear' ]
function nums(a, b) {
if
(a > b)
console.log('a is bigger')
else
console.log('b is bigger')
return
a + b
}
console.log(nums(4, 2))
console.log(nums(1, 2))
複製代碼
a is bigger
, 6
and b is bigger
, 3
a is bigger
, undefined
and b is bigger
, undefined
undefined
and undefined
SyntaxError
在JavaScript中,咱們沒必要顯式地編寫分號(;
),可是JavaScript引擎仍然在語句以後自動添加分號。這稱爲自動分號插入。例如,一個語句能夠是變量,或者像throw
、return
、break
這樣的關鍵字。
在這裏,咱們在新的一行上寫了一個return
語句和另外一個值a + b
。然而,因爲它是一個新行,引擎並不知道它其實是咱們想要返回的值。相反,它會在return
後面自動添加分號。你能夠這樣看:
return;
a + b
複製代碼
這意味着永遠不會到達a + b
,由於函數在return
關鍵字以後中止運行。若是沒有返回值,就像這裏,函數返回undefined
。注意,在if/else
語句以後沒有自動插入!
class Person {
constructor() {
this.name = "Lydia"
}
}
Person = class AnotherPerson {
constructor() {
this.name = "Sarah"
}
}
const member = new Person()
console.log(member.name)
複製代碼
"Lydia"
"Sarah"
Error: cannot redeclare Person
SyntaxError
咱們能夠將類設置爲等於其餘類/函數構造函數。 在這種狀況下,咱們將Person
設置爲AnotherPerson
。 這個構造函數的名字是Sarah
,因此新的Person
實例member
上的name屬性是Sarah
。
const info = {
[Symbol('a')]: 'b'
}
console.log(info)
console.log(Object.keys(info))
複製代碼
{Symbol('a'): 'b'}
and ["{Symbol('a')"]
{}
and []
{ a: "b" }
and ["a"]
{Symbol('a'): 'b'}
and []
Symbol
類型是不可枚舉的。Object.keys
方法返回對象上的全部可枚舉的鍵屬性。Symbol
類型是不可見的,並返回一個空數組。 記錄整個對象時,全部屬性都是可見的,甚至是不可枚舉的屬性。
這是Symbol
的衆多特性之一:除了表示徹底惟一的值(防止對象意外名稱衝突,例如當使用2個想要向同一對象添加屬性的庫時),您還能夠隱藏
這種方式對象的屬性(儘管不徹底。你仍然可使用Object.getOwnPropertySymbols()
方法訪問 Symbol
。
const getList = ([x, ...y]) => [x, y]
const getUser = user => { name: user.name, age: user.age }
const list = [1, 2, 3, 4]
const user = { name: "Lydia", age: 21 }
console.log(getList(list))
console.log(getUser(user))
複製代碼
[1, [2, 3, 4]]
and undefined
[1, [2, 3, 4]]
and { name: "Lydia", age: 21 }
[1, 2, 3, 4]
and { name: "Lydia", age: 21 }
Error
and { name: "Lydia", age: 21 }
getList
函數接收一個數組做爲其參數。 在getList
函數的括號之間,咱們當即解構這個數組。 您能夠將其視爲:
[x, ...y] = [1, 2, 3, 4]
使用剩餘的參數... y
,咱們將全部剩餘參數放在一個數組中。 在這種狀況下,其他的參數是2
,3
和4
。 y
的值是一個數組,包含全部其他參數。 在這種狀況下,x
的值等於1
,因此當咱們打印[x,y]
時,會打印[1,[2,3,4]]
。
getUser
函數接收一個對象。對於箭頭函數,若是隻返回一個值,咱們沒必要編寫花括號。可是,若是您想從一個箭頭函數返回一個對象,您必須在圓括號之間編寫它,不然不會返回任何值!下面的函數將返回一個對象:
const getUser = user => ({ name: user.name, age: user.age })
因爲在這種狀況下不返回任何值,所以該函數返回undefined
。
const name = "Lydia"
console.log(name())
複製代碼
SyntaxError
ReferenceError
TypeError
undefined
變量name
保存字符串的值,該字符串不是函數,所以沒法調用。
當值不是預期類型時,會拋出TypeErrors
。 JavaScript指望name
是一個函數,由於咱們試圖調用它。 但它是一個字符串,所以拋出TypeError
:name is not a function
當你編寫了一些非有效的JavaScript時,會拋出語法錯誤,例如當你把return
這個詞寫成retrun
時。 當JavaScript沒法找到您嘗試訪問的值的引用時,拋出ReferenceErrors
。
// 🎉✨ This is my 100th question! ✨🎉
const output = `${[] && 'Im'}possible! You should${'' && `n't`} see a therapist after so much JavaScript lol`
複製代碼
possible! You should see a therapist after so much JavaScript lol
Impossible! You should see a therapist after so much JavaScript lol
possible! You shouldn't see a therapist after so much JavaScript lol
Impossible! You shouldn't see a therapist after so much JavaScript lol
[]
是一個真值。 使用&&
運算符,若是左側值是真值,則返回右側值。 在這種狀況下,左側值[]
是一個真值,因此返回Im
。
""
是一個假值。 若是左側值是假的,則不返回任何內容。 n't
不會被退回。
const one = (false || {} || null)
const two = (null || false || "")
const three = ([] || 0 || true)
console.log(one, two, three)
複製代碼
false
null
[]
null
""
true
{}
""
[]
null
null
true
使用||
運算符,咱們能夠返回第一個真值。 若是全部值都是假值,則返回最後一個值。
(false || {} || null)
:空對象{}
是一個真值。 這是第一個(也是惟一的)真值,它將被返回。one
等於{}
。
(null || false ||「」)
:全部值都是假值。 這意味着返回傳遞的值""
。 two
等於""
。
([] || 0 ||「」)
:空數組[]
是一個真值。 這是第一個返回的真值。 three
等於[]
。
const myPromise = () => Promise.resolve('I have resolved!')
function firstFunction() {
myPromise().then(res => console.log(res))
console.log('second')
}
async function secondFunction() {
console.log(await myPromise())
console.log('second')
}
firstFunction()
secondFunction()
複製代碼
I have resolved!
, second
and I have resolved!
, second
second
, I have resolved!
and second
, I have resolved!
I have resolved!
, second
and second
, I have resolved!
second
, I have resolved!
and I have resolved!
, second
有了promise,咱們一般會說:當我想要調用某個方法,可是因爲它可能須要一段時間,所以暫時將它放在一邊。只有當某個值被resolved/rejected,而且執行棧爲空時才使用這個值。
咱們能夠在async
函數中經過.then
和await
關鍵字得到該值。 儘管咱們能夠經過.then
和await
得到promise的價值,可是它們的工做方式有所不一樣。
在 firstFunction
中,當運行到myPromise
方法時咱們將其放在一邊,即promise進入微任務隊列,其餘後面的代碼(console.log('second')
)照常運行,所以second
被打印出,firstFunction
方法到此執行完畢,執行棧中宏任務隊列被清空,此時開始執行微任務隊列中的任務,I have resolved
被打印出。
在secondFunction
方法中,咱們經過await
關鍵字,暫停了後面代碼的執行,直到異步函數的值被解析纔開始後面代碼的執行。這意味着,它會等着直到 myPromise
以值I have resolved
被解決以後,下一行second
纔開始執行。
const set = new Set()
set.add(1)
set.add("Lydia")
set.add({ name: "Lydia" })
for (let item of set) {
console.log(item + 2)
}
複製代碼
3
, NaN
, NaN
3
, 7
, NaN
3
, Lydia2
, [Object object]2
"12"
, Lydia2
, [Object object]2
「+」運算符不只用於添加數值,還可使用它來鏈接字符串。 每當JavaScript引擎發現一個或多個值不是數字時,就會將數字強制爲字符串。
第一個是數字1。 1 + 2返回數字3。
可是,第二個是字符串「Lydia」。 「Lydia」是一個字符串,2是一個數字:2被強制轉換爲字符串。 「Lydia」和「2」被鏈接起來,產生字符串「Lydia2」。
{name:「 Lydia」}
是一個對象。 數字和對象都不是字符串,所以將兩者都字符串化。 每當咱們對常規對象進行字符串化時,它就會變成[Object object]
。 與「2」串聯的「 [Object object]」成爲「[Object object]2」。
Promise.resolve(5)
複製代碼
5
Promise {<pending>: 5}
Promise {<resolved>: 5}
Error
咱們能夠將咱們想要的任何類型的值傳遞Promise.resolve
,不管是否promise
。 該方法自己返回帶有已解析值的Promise
。 若是您傳遞常規函數,它將是具備常規值的已解決promise
。 若是你經過了promise,它將是一個已經resolved的且帶有傳的值的promise。
上述狀況,咱們傳了數字5,所以返回一個resolved狀態的promise,resolve值爲5
function compareMembers(person1, person2 = person) {
if (person1 !== person2) {
console.log("Not the same!")
} else {
console.log("They are the same!")
}
}
const person = { name: "Lydia" }
compareMembers(person)
複製代碼
Not the same!
They are the same!
ReferenceError
SyntaxError
對象經過引用傳遞。 當咱們檢查對象的嚴格相等性(===)時,咱們正在比較它們的引用。
咱們將「person2」的默認值設置爲「person」對象,並將「person」對象做爲「person1」的值傳遞。
這意味着兩個值都引用內存中的同一位置,所以它們是相等的。
運行「 else」語句中的代碼塊,並記錄They are the same!
。
const colorConfig = {
red: true,
blue: false,
green: true,
black: true,
yellow: false,
}
const colors = ["pink", "red", "blue"]
console.log(colorConfig.colors[1])
複製代碼
true
false
undefined
TypeError
在JavaScript中,咱們有兩種訪問對象屬性的方法:括號表示法或點表示法。 在此示例中,咱們使用點表示法(colorConfig.colors
)代替括號表示法(colorConfig [「 colors」]
)。
使用點表示法,JavaScript會嘗試使用該確切名稱在對象上查找屬性。 在此示例中,JavaScript嘗試在colorconfig對象上找到名爲colors的屬性。 沒有名爲「colors」的屬性,所以返回「undefined」。 而後,咱們嘗試使用[1]
訪問第一個元素的值。 咱們沒法對未定義的值執行此操做,所以會拋出Cannot read property '1' of undefined
。
JavaScript解釋(或取消裝箱)語句。 當咱們使用方括號表示法時,它會看到第一個左方括號[
並一直進行下去,直到找到右方括號]
。 只有這樣,它纔會評估該語句。 若是咱們使用了colorConfig [colors [1]],它將返回colorConfig對象上red屬性的值。
console.log('❤️' === '❤️')
複製代碼
true
false
在內部,表情符號是unicode。 heat表情符號的unicode是「 U + 2764 U + FE0F」
。 對於相同的表情符號,它們老是相同的,所以咱們將兩個相等的字符串相互比較,這將返回true。
const emojis = ['✨', '🥑', '😍']
emojis.map(x => x + '✨')
emojis.filter(x => x !== '🥑')
emojis.find(x => x !== '🥑')
emojis.reduce((acc, cur) => acc + '✨')
emojis.slice(1, 2, '✨')
emojis.splice(1, 2, '✨')
複製代碼
All of them
map
reduce
slice
splice
map
slice
splice
splice
使用splice
方法,咱們經過刪除,替換或添加元素來修改原始數組。 在這種狀況下,咱們從索引1中刪除了2個元素(咱們刪除了'🥑'
和'😍'
),同時添加了✨emoji表情。
map
,filter
和slice
返回一個新數組,find
返回一個元素,而reduce
返回一個減少的值。
const food = ['🍕', '🍫', '🥑', '🍔']
const info = { favoriteFood: food[0] }
info.favoriteFood = '🍝'
console.log(food)
複製代碼
['🍕', '🍫', '🥑', '🍔']
['🍝', '🍫', '🥑', '🍔']
['🍝', '🍕', '🍫', '🥑', '🍔']
ReferenceError
咱們將info
對象上的favoriteFood
屬性的值設置爲披薩表情符號「🍕」的字符串。字符串是原始數據類型。在JavaScript中,原始數據類型經過值起做用
在這種狀況下,咱們將info
對象上的favoriteFood
屬性的值設置爲等於food
數組中的第一個元素的值,字符串爲披薩表情符號('🍕'
)。字符串是原始數據類型,而且經過值進行交互,咱們更改info
對象上favoriteFood
屬性的值。 food數組沒有改變,由於favoriteFood的值只是該數組中第一個元素的值的複製,而且與該元素上的元素沒有相同的內存引用食物[0]
。當咱們記錄食物時,它仍然是原始數組['🍕','🍫','🥑','🍔']
。
JSON.parse()
複製代碼
使用JSON.parse()
方法,咱們能夠將JSON字符串解析爲JavaScript值。
// 將數字字符串化爲有效的JSON,而後將JSON字符串解析爲JavaScript值:
const jsonNumber = JSON.stringify(4) // '4'
JSON.parse(jsonNumber) // 4
// 將數組值字符串化爲有效的JSON,而後將JSON字符串解析爲JavaScript值:
const jsonArray = JSON.stringify([1, 2, 3]) // '[1, 2, 3]'
JSON.parse(jsonArray) // [1, 2, 3]
// 將對象字符串化爲有效的JSON,而後將JSON字符串解析爲JavaScript值:
const jsonArray = JSON.stringify({ name: "Lydia" }) // '{"name":"Lydia"}'
JSON.parse(jsonArray) // { name: 'Lydia' }
複製代碼
let name = 'Lydia'
function getName() {
console.log(name)
let name = 'Sarah'
}
getName()
複製代碼
undefined
ReferenceError
每一個函數都有其本身的執行上下文。 getName
函數首先在其自身的上下文(範圍)內查找,以查看其是否包含咱們嘗試訪問的變量name
。 上述狀況,getName
函數包含其本身的name
變量:咱們用let
關鍵字和Sarah
的值聲明變量name
。
帶有let
關鍵字(和const
)的變量被提高,可是與var
不一樣,它不會被***初始化***。 在咱們聲明(初始化)它們以前,沒法訪問它們。 這稱爲「暫時性死區」。 當咱們嘗試在聲明變量以前訪問變量時,JavaScript會拋出ReferenceError: Cannot access 'name' before initialization
。
若是咱們不在getName
函數中聲明name
變量,則javascript引擎會查看原型練。會找到其外部做用域有一個名爲name
的變量,其值爲Lydia
。 在這種狀況下,它將打印Lydia
:
let name = 'Lydia'
function getName() {
console.log(name)
}
getName() // Lydia
複製代碼
function* generatorOne() {
yield ['a', 'b', 'c'];
}
function* generatorTwo() {
yield* ['a', 'b', 'c'];
}
const one = generatorOne()
const two = generatorTwo()
console.log(one.next().value)
console.log(two.next().value)
複製代碼
a
and a
a
and undefined
['a', 'b', 'c']
and a
a
and ['a', 'b', 'c']
經過 yield
關鍵字, 咱們在 Generator
函數裏執行yield
表達式. 經過 yield*
關鍵字, 咱們能夠在一個Generator
函數裏面執行(yield
表達式)另外一個 Generator
函數, 或可遍歷的對象 (如數組).
在函數 generatorOne
中, 咱們經過 yield
關鍵字 yield 了一個完整的數組 ['a', 'b', 'c']
。函數one
經過next
方法返回的對象的value
屬性的值 (one.next().value
) 等價於數組 ['a', 'b', 'c']
.
console.log(one.next().value) // ['a', 'b', 'c']
console.log(one.next().value) // undefined
複製代碼
在函數 generatorTwo
中, 咱們使用 yield*
關鍵字。就至關於函數two
第一個yield
的值, 等價於在迭代器中第一個 yield
的值。數組['a', 'b', 'c']
就是這個迭代器. 第一個 yield
的值就是 a
, 因此咱們第一次調用 two.next().value
時, 就返回a
。
console.log(two.next().value) // 'a'
console.log(two.next().value) // 'b'
console.log(two.next().value) // 'c'
console.log(two.next().value) // undefined
複製代碼
console.log(`${(x => x)('I love')} to program`)
複製代碼
I love to program
undefined to program
${(x => x)('I love') to program
TypeError
帶有模板字面量的表達式首先被執行。至關於字符串會包含表達式,這個當即執行函數 (x => x)('I love')
返回的值. 咱們向箭頭函數 x => x
傳遞 'I love'
做爲參數。x
等價於返回的 'I love'
。這就是結果 I love to program
。
let config = {
alert: setInterval(() => {
console.log('Alert!')
}, 1000)
}
config = null
複製代碼
setInterval
的回調不會被調用setInterval
的回調被調用一次setInterval
的回調仍然會被每秒鐘調用config.alert()
, config 爲 null
通常狀況下當咱們將對象賦值爲 null
, 那些對象會被進行 垃圾回收(garbage collected) 由於已經沒有對這些對象的引用了。然而,setInterval
的參數是一個箭頭函數(因此上下文綁定到對象 config
了),回調函數仍然保留着對 config
的引用。只要存在引用,對象就不會被垃圾回收。由於沒有被垃圾回收,setInterval
的回調每1000ms (1s)會被調用一次。
'Hello world!'
?const myMap = new Map()
const myFunc = () => 'greeting'
myMap.set(myFunc, 'Hello world!')
//1
myMap.get('greeting')
//2
myMap.get(myFunc)
//3
myMap.get(() => 'greeting')
複製代碼
當經過 set
方法添加一個鍵值對,一個傳遞給 set
方法的參數將會是鍵名,第二個參數將會是值。在這個case裏,鍵名爲 函數 () => 'greeting'
,值爲'Hello world'
。 myMap
如今就是 { () => 'greeting' => 'Hello world!' }
。
1 是錯的,由於鍵名不是 'greeting'
而是 () => 'greeting'
。 3 是錯的,由於咱們給get
方法傳遞了一個新的函數。對象受 引用 影響。函數也是對象,所以兩個函數嚴格上並不等價,儘管他們相同:他們有兩個不一樣的內存引用地址。
const person = {
name: "Lydia",
age: 21
}
const changeAge = (x = { ...person }) => x.age += 1
const changeAgeAndName = (x = { ...person }) => {
x.age += 1
x.name = "Sarah"
}
changeAge(person)
changeAgeAndName()
console.log(person)
複製代碼
{name: "Sarah", age: 22}
{name: "Sarah", age: 23}
{name: "Lydia", age: 22}
{name: "Lydia", age: 23}
函數 changeAge
和函數 changeAgeAndName
有着不一樣的參數,定義一個 新 生成的對象 { ...person }
。這個對象有着全部 person
對象 中 k/v 值的副本。
首項, 咱們調用 changeAge
函數並傳遞 person
對象做爲它的參數。這個函數對 age
屬性進行加一操做。person
如今是 { name: "Lydia", age: 22 }
。
而後,咱們調用函數 changeAgeAndName
,然而咱們沒有傳遞參數。取而代之,x
的值等價 new 生成的對象: { ...person }
。由於它是一個新生成的對象,它並不會對對象 person
形成任何反作用。person
仍然等價於 { name: "Lydia", age: 22 }
。
6
?function sumValues(x, y, z) {
return x + y + z;
}
複製代碼
sumValues([...1, 2, 3])
sumValues([...[1, 2, 3]])
sumValues(...[1, 2, 3])
sumValues([1, 2, 3])
經過展開操做符 ...
,咱們能夠 暫開 單個可迭代的元素。函數 sumValues
function 接收三個參數: x
, y
和 z
。...[1, 2, 3]
的執行結果爲 1, 2, 3
,將會傳遞給函數 sumValues
。
let num = 1;
const list = ["🥳", "🤠", "🥰", "🤪"];
console.log(list[(num += 1)]);
複製代碼
🤠
🥰
SyntaxError
ReferenceError
經過 +=
操做符,咱們對值 num
進行加 1
操做。 num
有初始值 1
,所以 1 + 1
的執行結果爲 2
。數組 list
的第二項爲 🥰,console.log(list[2])
輸出 🥰.
const person = {
firstName: "Lydia",
lastName: "Hallie",
pet: {
name: "Mara",
breed: "Dutch Tulip Hound"
},
getFullName() {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(person.pet?.name);
console.log(person.pet?.family?.name);
console.log(person.getFullName?.());
console.log(member.getLastName?.());
複製代碼
undefined
undefined
undefined
undefined
Mara
undefined
Lydia Hallie
undefined
Mara
null
Lydia Hallie
null
null
ReferenceError
null
ReferenceError
經過 ES10 或 TS3.7+可選鏈操做符 ?.
,咱們再也不須要顯式檢測更深層的嵌套值是否有效。若是咱們嘗試獲取 undefined
或 null
的值 (nullish),表達將會短路並返回 undefined
. person.pet?.name
: person
有一個名爲 pet
的屬性: person.pet
不是 nullish。它有個名爲 name
的屬性,並返回字符串 Mara
。 person.pet?.family?.name
: person
有一個名爲 pet
的屬性: person.pet
不是 nullish. pet
並無 一個名爲 family
的屬性, person.pet.family
是 nullish。表達式返回 undefined
。 person.getFullName?.()
: person
有一個名爲 getFullName
的屬性: person.getFullName()
不是 nullish 並能夠被調用,返回字符串 Lydia Hallie
。 member.getLastName?.()
: member
is not defined: member.getLastName()
is nullish. The expression returns undefined
.
const groceries = ["banana", "apple", "peanuts"];
if (groceries.indexOf("banana")) {
console.log("We have to buy bananas!");
} else {
console.log(`We don't have to buy bananas!`);
}
複製代碼
undefined
1
咱們傳遞了一個狀態 groceries.indexOf("banana")
給if條件語句。groceries.indexOf("banana")
返回 0
, 一個 falsy 的值。由於if條件語句的狀態爲 falsy,else
塊區內的代碼執行,而且 We don't have to buy bananas!
被輸出.
const config = {
languages: [],
set language(lang) {
return this.languages.push(lang);
}
};
console.log(config.language);
複製代碼
function language(lang) { this.languages.push(lang }
0
[]
undefined
方法 language
是一個 setter
。Setters 並不保存一個實際值,它們的使命在於 修改 屬性。當調用方法 setter
, 返回 undefined
。
const name = "Lydia Hallie";
console.log(!typeof name === "object");
console.log(!typeof name === "string");
複製代碼
false
true
true
false
false
false
true
true
typeof name
返回 "string"
。字符串 "string"
是一個 truthy 的值,所以 !typeof name
返回一個布爾值 false
。 false === "object"
和 false === "string"
都返回 false
。 (若是咱們想檢測一個值的類型,咱們不該該用 !==
而不是 !typeof
)
const add = x => y => z => {
console.log(x, y, z);
return x + y + z;
};
add(4)(5)(6);
複製代碼
4
5
6
6
5
4
4
function
function
undefined
undefined
6
函數 add
是一個返回 返回箭頭函數的箭頭函數 的箭頭函數(still with me?)。第一個函數接收一個值爲 4
的參數 x
。咱們調用第二個函數,它接收一個值爲 5
的參數 y
。而後咱們調用第三個函數,它接收一個值爲 6
的參數 z
。當咱們嘗試在最後一個箭頭函數中獲取 x
, y
和 z
的值,JS 引擎根據做用域鏈去找 x
和 y
的值。獲得 4
5
6
.
async function* range(start, end) {
for (let i = start; i <= end; i++) {
yield Promise.resolve(i);
}
}
(async () => {
const gen = range(1, 3);
for await (const item of gen) {
console.log(item);
}
})();
複製代碼
Promise {1}
Promise {2}
Promise {3}
Promise {<pending>}
Promise {<pending>}
Promise {<pending>}
1
2
3
undefined
undefined
undefined
咱們給 函數range 傳遞: Promise{1}
, Promise{2}
, Promise{3}
,Generator 函數 range
返回一個全是 async object promise 數組。咱們將 async object 賦值給變量 gen
,以後咱們使用for await ... of
進行循環遍歷。咱們將返回的 Promise 實例賦值給 item
: 第一個返回 Promise{1}
, 第二個返回 Promise{2}
,以後是 Promise{3}
。由於咱們正 awaiting item
的值,resolved 狀態的 promsie,promise數組的resolved 值 以此爲: 1
,2
,3
.
const myFunc = ({ x, y, z }) => {
console.log(x, y, z);
};
myFunc(1, 2, 3);
複製代碼
1
2
3
{1: 1}
{2: 2}
{3: 3}
{ 1: undefined }
undefined
undefined
undefined
undefined
undefined
myFunc
指望接收一個包含 x
, y
和 z
屬性的對象做爲它的參數。由於咱們僅僅傳遞三個單獨的數字值 (1, 2, 3) 而不是一個含有 x
, y
和 z
屬性的對象 ({x: 1, y: 2, z: 3}), x
, y
和 z
有着各自的默認值 undefined
.
function getFine(speed, amount) {
const formattedSpeed = new Intl.NumberFormat({
'en-US',
{ style: 'unit', unit: 'mile-per-hour' }
}).format(speed)
const formattedAmount = new Intl.NumberFormat({
'en-US',
{ style: 'currency', currency: 'USD' }
}).format(amount)
return `The driver drove ${formattedSpeed} and has to pay ${formattedAmount}`
}
console.log(getFine(130, 300))
複製代碼
經過方法 Intl.NumberFormat
,咱們能夠格式化任意區域的數字值。咱們對數字值 130
進行 mile-per-hour
做爲 unit
的 en-US
區域 格式化,結果爲 130 mph
。對數字值 300
進行 USD
做爲 currentcy
的 en-US
區域格式化,結果爲 $300.00
.
const spookyItems = ["👻", "🎃", "🕸"];
({ item: spookyItems[3] } = { item: "💀" });
console.log(spookyItems);
複製代碼
["👻", "🎃", "🕸"]
["👻", "🎃", "🕸", "💀"]
["👻", "🎃", "🕸", { item: "💀" }]
["👻", "🎃", "🕸", "[object Object]"]
經過解構對象們,咱們能夠從右手邊的對象中拆出值,而且將拆出的值分配給左手邊對象同名的屬性。在這種狀況下,咱們將值 "💀" 分配給 spookyItems[3]
。至關於咱們正在篡改數組 spookyItems
,咱們給它添加了值 "💀"。當輸出 spookyItems
時,結果爲 ["👻", "🎃", "🕸", "💀"]
。
const name = "Lydia Hallie";
const age = 21;
console.log(Number.isNaN(name));
console.log(Number.isNaN(age));
console.log(isNaN(name));
console.log(isNaN(age));
複製代碼
true
false
true
false
true
false
false
false
false
false
true
false
false
true
false
true
經過方法 Number.isNaN
,你能夠檢測你傳遞的值是否爲 數字值 而且是否等價於 NaN
。name
不是一個數字值,所以 Number.isNaN(name)
返回 false
。age
是一個數字值,但它不等價於 NaN
,所以 Number.isNaN(age)
返回 false
. 經過方法 isNaN
, 你能夠檢測你傳遞的值是否一個 number。name
不是一個 number
,所以 isNaN(name)
返回 true
. age
是一個 number
所以 isNaN(age)
返回 false
.
const randomValue = 21;
function getInfo() {
console.log(typeof randomValue);
const randomValue = "Lydia Hallie";
}
getInfo();
複製代碼
"number"
"string"
undefined
ReferenceError
經過 const
關鍵字聲明的變量在被初始化以前不可被引用:這被稱之爲 暫時性死去。在函數 getInfo
中, 變量 randomValue
聲明在getInfo
的做用域的此法環境中。在想要對 typeof randomValue
進行log以前,變量 randomValue
仍未被初始化: 錯誤ReferenceError
被拋出! JS引擎並不會根據做用域鏈網上尋找該變量,由於咱們已經在 getInfo
函數中聲明瞭 randomValue
變量。
const myPromise = Promise.resolve("Woah some cool data");
(async () => {
try {
console.log(await myPromise);
} catch {
throw new Error(`Oops didn't work`);
} finally {
console.log("Oh finally!");
}
})();
複製代碼
Woah some cool data
Oh finally!
Woah some cool data
Oh finally!
Oops didn't work
Oh finally!
在 try
塊區,咱們打印 myPromise
變量的 awaited 值: "Woah some cool data"
。由於try
塊區沒有錯誤拋出,catch
塊區的代碼並不執行。finally
塊區的代碼 老是 執行,"Oh finally!"
被輸出。
const emojis = ["🥑", ["✨", "✨", ["🍕", "🍕"]]];
console.log(emojis.flat(1));
複製代碼
['🥑', ['✨', '✨', ['🍕', '🍕']]]
['🥑', '✨', '✨', ['🍕', '🍕']]
['🥑', ['✨', '✨', '🍕', '🍕']]
['🥑', '✨', '✨', '🍕', '🍕']
經過方法 flat
, 咱們能夠建立一個新的, 已被扁平化的數組。被扁平化的深度取決於咱們傳遞的值。在這個case裏,咱們傳遞了值 1
(並沒必要要,這是默認值),至關於只有第一層的數組纔會被鏈接。即這個 case 裏的 ['🥑']
and ['✨', '✨', ['🍕', '🍕']]
。鏈接這兩個數組獲得結果 ['🥑', '✨', '✨', ['🍕', '🍕']]
.
class Counter {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
const counterOne = new Counter();
counterOne.increment();
counterOne.increment();
const counterTwo = counterOne;
counterTwo.increment();
console.log(counterOne.count);
複製代碼
0
1
2
3
counterOne
是類 Counter
的一個實例。類 Counter 包含一個count
屬性在它的構造函數裏, 和一個 increment
方法。首先,咱們經過 counterOne.increment()
調用方法 increment
兩次。如今, counterOne.count
爲 2
. 而後,咱們建立一個新的變量
counterTwo
並將 counterOne
的引用地址賦值給它。由於對象受引用地址的影響,咱們剛剛建立了一個新的對象,其引用地址和 counterOne
的等價。所以它們指向同一塊內存地址,任何對其的反作用都會影響 counterTwo
。如今 counterTwo.count
爲 2
。 咱們調用 counterTwo.increment()
將 count
的值設爲 3
。而後,咱們打印 counterOne
裏的count,結果爲 3
。
const myPromise = Promise.resolve(Promise.resolve("Promise!"));
function funcOne() {
myPromise.then(res => res).then(res => console.log(res));
setTimeout(() => console.log("Timeout!", 0));
console.log("Last line!");
}
async function funcTwo() {
const res = await myPromise;
console.log(await res);
setTimeout(() => console.log("Timeout!", 0));
console.log("Last line!");
}
funcOne();
funcTwo();
複製代碼
Promise! Last line! Promise! Last line! Last line! Promise!
Last line! Timeout! Promise! Last line! Timeout! Promise!
Promise! Last line! Last line! Promise! Timeout! Timeout!
Last line! Promise! Promise! Last line! Timeout! Timeout!
首先,咱們調用 funcOne
。在函數 funcOne
的第一行,咱們調用myPromise
promise 異步操做。當JS引擎在忙於執行 promise,它繼續執行函數 funcOne
。下一行 異步操做 setTimeout
,其回調函數被 Web API 調用。 (詳情請參考我關於event loop的文章.) promise 和 timeout 都是異步操做,函數繼續執行當JS引擎忙於執行promise 和 處理 setTimeout
的回調。至關於 Last line!
首先被輸出, 由於它不是異步操做。執行完 funcOne
的最後一行,promise 狀態轉變爲 resolved,Promise!
被打印。然而,由於咱們調用了 funcTwo()
, 調用棧不爲空,setTimeout
的回調仍不能入棧。 咱們如今處於 funcTwo
,先 awaiting myPromise。經過 await
關鍵字, 咱們暫停了函數的執行直到 promise 狀態變爲 resolved (或 rejected)。而後,咱們輸出 res
的 awaited 值(由於 promise 自己返回一個 promise)。 接着輸出 Promise!
。 下一行就是 異步操做 setTimeout
,其回調函數被 Web API 調用。 咱們執行到函數 funcTwo
的最後一行,輸出 Last line!
。如今,由於 funcTwo
出棧,調用棧爲空。在事件隊列中等待的回調函數(() => console.log("Timeout!")
from funcOne
, and () => console.log("Timeout!")
from funcTwo
)以此入棧。第一個回調輸出 Timeout!
,並出棧。而後,第二個回調輸出 Timeout!
,並出棧。獲得結果 Last line! Promise! Promise! Last line! Timeout! Timeout!
index.js
中調用 sum.js?
中的 sum
?// sum.js
export default function sum(x) {
return x + x;
}
// index.js
import * as sum from "./sum";
複製代碼
sum(4)
sum.sum(4)
sum.default(4)
*
來導入,只能具名導出
使用符號 *
,咱們引入文件中的全部值,包括默認和具名。若是咱們有如下文件:
// info.js
export const name = "Lydia";
export const age = 21;
export default "I love JavaScript";
// index.js
import * as info from "./info";
console.log(info);
複製代碼
將會輸出如下內容:
{
default: "I love JavaScript",
name: "Lydia",
age: 21
}
複製代碼
以 sum
爲例,至關於如下形式引入值 sum
:
{ default: function sum(x) { return x + x } }
複製代碼
咱們能夠經過調用 sum.default
來調用該函數
const handler = {
set: () => console.log("Added a new property!"),
get: () => console.log("Accessed a property!")
};
const person = new Proxy({}, handler);
person.name = "Lydia";
person.name;
複製代碼
Added a new property!
Accessed a property!
Added a new property!
Accessed a property!
使用 Proxy 對象,咱們能夠給一個對象添加自定義行爲。在這個 case,咱們傳遞一個包含如下屬性的對象 handler
: set
and get
。每當我門 設置 屬性值時 set
被調用,每當咱們 獲取 時 get
被調用。 第一個參數是一個空對象 {}
,做爲 person
的值。對於這個對象,自定義行爲被定義在對象 handler
。若是咱們向對象 person
添加屬性,set
將被調用。若是咱們獲取 person
的屬性, get
將被調用。 首先,咱們向 proxy 對象(person.name = "Lydia"
)添加一個屬性 name
。set
被調用並輸出 "Added a new property!"
。 而後,咱們獲取 proxy 對象的一個屬性,對象 handler 的屬性 get
被調用。輸出 "Accessed a property!"
。
person
有反作用?const person = { name: "Lydia Hallie" };
Object.seal(person);
複製代碼
person.name = "Evan Bacon"
person.age = 21
delete person.name
Object.assign(person, { age: 21 })
使用 Object.seal
咱們能夠防止新屬性 被添加,或者存在屬性 被移除. 然而,你仍然能夠對存在屬性進行更改。
person
有反作用?const person = {
name: "Lydia Hallie",
address: {
street: "100 Main St"
}
};
Object.freeze(person);
複製代碼
person.name = "Evan Bacon"
delete person.address
person.address.street = "101 Main St"
person.pet = { name: "Mara" }
使用方法 Object.freeze
對一個對象進行 凍結。不能對屬性進行添加,修改,刪除。 然而,它僅 對對象進行 淺 凍結,意味着只有 對象中的 直接 屬性被凍結。若是屬性是另外一個 object,像案例中的 address
,address
中的屬性沒有被凍結,仍然能夠被修改。
person
有反作用?const person = {
name: "Lydia Hallie",
address: {
street: "100 Main St"
}
};
Object.freeze(person);
複製代碼
person.name = "Evan Bacon"
delete person.address
person.address.street = "101 Main St"
person.pet = { name: "Mara" }
使用方法 Object.freeze
對一個對象進行 凍結。不能對屬性進行添加,修改,刪除。 然而,它僅 對對象進行 淺 凍結,意味着只有 對象中的 直接 屬性被凍結。若是屬性是另外一個 object,像案例中的 address
,address
中的屬性沒有被凍結,仍然能夠被修改。
const add = x => x + x;
function myFunc(num = 2, value = add(num)) {
console.log(num, value);
}
myFunc();
myFunc(3);
複製代碼
2
4
and 3
6
2
NaN
and 3
NaN
2
Error
and 3
6
2
4
and 3
Error
首先咱們不傳遞任何參數調用 myFunc()
。由於咱們沒有傳遞參數,num
和 value
獲取它們各自的默認值:num 爲 2
, 而 value
爲函數 add
的返回值。對於函數 add
,咱們傳遞值爲2的 num
做爲參數。函數 add
返回 4
做爲 value
的值。 而後,咱們調用 myFunc(3)
並傳遞值 3
參數 num
的值。咱們沒有給 value
傳遞值。由於咱們沒有給參數 value
傳遞值,它獲取默認值:函數 add
的返回值。對於函數 add
,咱們傳遞值爲3的 num
給它。函數 add
返回 6
做爲 value
的值。
class Counter {
#number = 10
increment() {
this.#number++
}
getNum() {
return this.#number
}
}
const counter = new Counter()
counter.increment()
console.log(counter.#number)
複製代碼
10
11
undefined
SyntaxError
在 ES2020 中,經過 #
咱們能夠給 class 添加私有變量。在 class 的外部咱們沒法獲取該值。當咱們嘗試輸出 counter.#number
,語法錯誤被拋出:咱們沒法在 class Counter
外部獲取它!
const teams = [
{ name: "Team 1", members: ["Paul", "Lisa"] },
{ name: "Team 2", members: ["Laura", "Tim"] }
];
function* getMembers(members) {
for (let i = 0; i < members.length; i++) {
yield members[i];
}
}
function* getTeams(teams) {
for (let i = 0; i < teams.length; i++) {
// ✨ SOMETHING IS MISSING HERE ✨
}
}
const obj = getTeams(teams);
obj.next(); // { value: "Paul", done: false }
obj.next(); // { value: "Lisa", done: false }
複製代碼
yield getMembers(teams[i].members)
yield* getMembers(teams[i].members)
return getMembers(teams[i].members)
return yield getMembers(teams[i].members)
爲了遍歷 teams
數組中對象的屬性 members
中的每一項,咱們須要將 teams[i].members
傳遞給 Generator 函數 getMembers
。Generator 函數返回一個 generator 對象。爲了遍歷這個 generator 對象中的每一項,咱們須要使用 yield*
. 若是咱們沒有寫 yield
,return yield
或者 return
,整個 Generator 函數不會第一時間 return 當咱們調用 next
方法.
const person = {
name: "Lydia Hallie",
hobbies: ["coding"]
};
function addHobby(hobby, hobbies = person.hobbies) {
hobbies.push(hobby);
return hobbies;
}
addHobby("running", []);
addHobby("dancing");
addHobby("baking", person.hobbies);
console.log(person.hobbies);
複製代碼
["coding"]
["coding", "dancing"]
["coding", "dancing", "baking"]
["coding", "running", "dancing", "baking"]
函數 addHobby
接受兩個參數,hobby
和有着對象 person
中數組 hobbies
默認值的 hobbies
。 首相,咱們調用函數 addHobby
,並給 hobby
傳遞 "running"
以及給 hobbies
傳遞一個空數組。由於咱們給 hobbies
傳遞了空數組,"running"
被添加到這個空數組。 而後,咱們調用函數 addHobby
,並給 hobby
傳遞 "dancing"
。咱們不向 hobbies
傳遞值,所以它獲取其默認值 —— 對象 person
的 屬性 hobbies
。咱們向數組 person.hobbies
push dancing
。 最後,咱們調用函數 addHobby
,並向 hobby
傳遞 值 "bdaking"
,而且向 hobbies
傳遞 person.hobbies
。咱們向數組 person.hobbies
push dancing
。 pushing dancing
和 baking
以後,person.hobbies
的值爲 ["coding", "dancing", "baking"]
class Bird {
constructor() {
console.log("I'm a bird. 🦢");
}
}
class Flamingo extends Bird {
constructor() {
console.log("I'm pink. 🌸");
super();
}
}
const pet = new Flamingo();
複製代碼
I'm pink. 🌸
I'm pink. 🌸
I'm a bird. 🦢
I'm a bird. 🦢
I'm pink. 🌸
咱們建立了類 Flamingo
的實例 pet
。當咱們實例化這個實例,Flamingo
中的 constructor
被調用。首相,輸出 "I'm pink. 🌸"
, 以後咱們調用super()
。super()
調用父類的構造函數,Bird
。Bird
的構造函數被調用,並輸出 "I'm a bird. 🦢"
。
const emojis = ["🎄", "🎅🏼", "🎁", "⭐"];
/* 1 */ emojis.push("🦌");
/* 2 */ emojis.splice(0, 2);
/* 3 */ emojis = [...emojis, "🥂"];
/* 4 */ emojis.length = 0;
複製代碼
const
關鍵字意味着咱們不能 重定義 變量中的值,它 僅可讀。而然,值自己不可修改。數組 emojis
中的值可被修改,如 push 新的值, 拼接,又或者將數組的長度設置爲0。
person
添加什麼,以至執行 [...person]
時得到形如 ["Lydia Hallie", 21]
的輸出?const person = {
name: "Lydia Hallie",
age: 21
}
[...person] // ["Lydia Hallie", 21]
複製代碼
*[Symbol.iterator]() { for (let x in this) yield* this[x] }
*[Symbol.iterator]() { for (let x in this) yield* Object.values(this) }
*[Symbol.iterator]() { for (let x in this) yield this }
對象默認並非可迭代的。若是迭代規則被定義,則一個對象是可迭代的(An iterable is an iterable if the iterator protocol is present)。咱們能夠經過添加迭代器symbol [Symbol.iterator]
來定義迭代規則,其返回一個 generator 對象,好比說構建一個 generator 函數 *[Symbol.iterator]() {}
。若是咱們想要返回數組 ["Lydia Hallie", 21]
: yield* Object.values(this)
,這個 generator 函數必定要 yield 對象 person
的Object.values
。
原文出自Lydia Hallie的 Instagram,若是沒法訪問,請移步到這裏。
❤️ 看完三件事:
若是你以爲這篇內容對你挺有啓發,我想邀請你幫我個小忙: