JavaScript 进阶问题列表(译)
我在我的 Instagram 上每天都会发布 JavaScript 的多选问题,并且同时也会在这个仓库中发布。
从基础到进阶,测试你有多了解 JavaScript,刷新你的知识,或者帮助你的 coding 面试! :muscle: :rocket: 我每周都会在这个仓库下更新新的问题。
答案在问题下方的折叠部分,点击即可展开问题。祝你好运 ❤️
101.输出什么?
const one = (false || {} || null)
const two = (null || false || "")
const three = ([] || 0 || true)
console.log(one, two, three)
- A:
falsenull[] - B:
null""true - C:
{}""[] - D:
nullnulltrue
答案
答案: C
使用||运算符,我们可以返回第一个真值。 如果所有值都是假值,则返回最后一个值。
(false || {} || null):空对象{}是一个真值。 这是第一个(也是唯一的)真值,它将被返回。one等于{}。
(null || false ||“”):所有值都是假值。 这意味着返回传递的值""。 two等于""。
([] || 0 ||“”):空数组[]是一个真值。 这是第一个返回的真值。 three等于[]。
102. 依次输出什么?
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()
- A:
I have resolved!,secondandI have resolved!,second - B:
second,I have resolved!andsecond,I have resolved! - C:
I have resolved!,secondandsecond,I have resolved! - D:
second,I have resolved!andI have resolved!,second
答案
答案: D
有了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才开始执行。
103. 输出什么?
const set = new Set()
set.add(1)
set.add("Lydia")
set.add({ name: "Lydia" })
for (let item of set) {
console.log(item + 2)
}
- A:
3,NaN,NaN - B:
3,7,NaN - C:
3,Lydia2,[Object object]2 - D:
"12",Lydia2,[Object object]2
答案
答案: C
“+”运算符不仅用于数值相加,还可以使用它来连接字符串。 每当JavaScript引擎发现一个或多个值不是数字时,就会将数字强制转化为字符串。
第一个是数字1, 1 + 2返回数字3。
但是,第二个是字符串Lydia。 Lydia是字符串,2是数字:2被强制转换为字符串。 Lydia和2被连接起来,产生字符Lydia2。
{name:“Lydia”}是一个对象。 数字和对象都不是字符串,因此将二者都字符串化。 每当我们对常规对象进行字符串化时,它就会变成“[Object object]”。
104. 结果是什么?
Promise.resolve(5)
- A:
5 - B:
Promise {<pending>: 5} - C:
Promise {<resolved>: 5} - D:
Error
答案
答案: C
我们可以将我们想要的任何类型的值传递Promise.resolve,无论是否promise。 该方法本身返回带有已解析值的Promise。 如果您传递常规函数,它将是具有常规值的已解决promise。 如果你通过了promise,它将是一个已经resolved的且带有传的值的promise。
上述情况,我们传了数字5,因此返回一个resolved状态的promise,resolve值为5
105. 输出什么?
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)
- A:
Not the same! - B:
They are the same! - C:
ReferenceError - D:
SyntaxError
答案
答案: B
对象通过引用传递。 当我们检查对象的严格相等性(===)时,我们正在比较它们的引用。
我们将person2的默认值设置为person对象,并将person对象作为person1的值传递。
这意味着两个值都引用内存中的同一位置,因此它们是相等的。
运行“ else”语句中的代码块,并记录They are the same! 。
106. 输出什么?
const colorConfig = {
red: true,
blue: false,
green: true,
black: true,
yellow: false,
}
const colors = ["pink", "red", "blue"]
console.log(colorConfig.colors[1])
- A:
true - B:
false - C:
undefined - D:
TypeError
答案
答案: D
在JavaScript中,我们有两种访问对象属性的方法:括号表示法或点表示法。 在此示例中,我们使用点表示法(colorConfig.colors)代替括号表示法(colorConfig [“ colors”])。
使用点表示法,JavaScript会尝试使用该确切名称在对象上查找属性。 在此示例中,JavaScript尝试在colorconfig对象上找到名为colors的属性。 没有名为“colors”的属性,因此返回“undefined”。
然后,我们尝试使用[1]访问第一个元素的值。 我们无法对未定义的值执行此操作,因此会抛出Cannot read property '1' of undefined。
JavaScript解释(或取消装箱)语句。 当我们使用方括号表示法时,它会看到第一个左方括号[并一直进行下去,直到找到右方括号]。 只有这样,它才会评估该语句。 如果我们使用了colorConfig [colors [1]],它将返回colorConfig对象上red属性的值。
107. 输出什么?
console.log('❤️' === '❤️')
- A:
true - B:
false
答案
答案: A
在内部,表情符号是unicode。 心表情符号的unicode是U+2764 U+FE0F。 对于相同的表情符号,它们总是相同的,因此我们正在将两个相等的字符串相互比较,这将返回true。
108. 哪些方法修改了原数组?
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, '✨')
- A:
All of them - B:
mapreduceslicesplice - C:
mapslicesplice - D:
splice
答案
答案: D
使用splice方法,我们通过删除,替换或添加元素来修改原始数组。 上述情况,我们从索引1中删除了2个元素(删除了'🥑'和'😍'),并添加了✨emoji表情。
map,filter和slice返回一个新数组,find返回一个元素,而reduce返回一个减小的值。
109. 输出什么?
const food = ['🍕', '🍫', '🥑', '🍔']
const info = { favoriteFood: food[0] }
info.favoriteFood = '🍝'
console.log(food)
- A:
['🍕', '🍫', '🥑', '🍔'] - B:
['🍝', '🍫', '🥑', '🍔'] - C:
['🍝', '🍕', '🍫', '🥑', '🍔'] - D:
ReferenceError
答案
答案: A
我们将info对象上的favoriteFood属性的值设置为披萨表情符号“🍕”的字符串。字符串是原始数据类型。在JavaScript中,原始数据类型通过值起作用
在这种情况下,我们将info对象上的favoriteFood属性的值设置为等于food数组中的第一个元素的值,字符串为披萨表情符号('🍕' )。字符串是原始数据类型,并且通过值进行交互,我们更改info对象上favoriteFood属性的值。 food数组没有改变,因为favoriteFood的值只是该数组中第一个元素的值的复制,并且与该元素上的元素没有相同的内存引用食物[0]。当我们记录食物时,它仍然是原始数组['🍕','🍫','🥑','🍔']。
110. 这个函数干了什么?
JSON.parse()
- A: Parses JSON to a JavaScript value
- B: Parses a JavaScript object to JSON
- C: Parses any JavaScript value to JSON
- D: Parses JSON to a JavaScript object only
答案
答案: A
使用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' }
111. 输出什么?
let name = 'Lydia'
function getName() {
console.log(name)
let name = 'Sarah'
}
getName()
- A: Lydia
- B: Sarah
- C:
undefined - D:
ReferenceError
答案
答案: D
每个函数都有其自己的执行上下文。 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
112. 输出什么?
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:
aanda - B:
aandundefined - C:
['a', 'b', 'c']anda - D:
aand['a', 'b', 'c']
答案
答案: 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
113. 输出什么?
console.log(`${(x => x)('I love')} to program`)
- A:
I love to program - B:
undefined to program - C:
${(x => x)('I love') to program - D:
TypeError
答案
答案: A
带有模板字面量的表达式首先被执行。相当于字符串会包含表达式,这个立即执行函数 (x => x)('I love') 返回的值. 我们向箭头函数 x => x 传递 'I love' 作为参数。x 等价于返回的 'I love'。这就是结果 I love to program。
114. 将会发生什么?
let config = {
alert: setInterval(() => {
console.log('Alert!')
}, 1000)
}
config = null
- A:
setInterval的回调不会被调用 - B:
setInterval的回调被调用一次 - C:
setInterval的回调仍然会被每秒钟调用 - D: 我们从没调用过
config.alert(), config 为null
答案
答案: C
一般情况下当我们将对象赋值为 null, 那些对象会被进行 垃圾回收(garbage collected) 因为已经没有对这些对象的引用了。然而,setInterval的参数是一个箭头函数(所以上下文绑定到对象 config 了),回调函数仍然保留着对 config的引用。只要存在引用,对象就不会被垃圾回收。因为没有被垃圾回收,setInterval 的回调每1000ms (1s)会被调用一次。
115. 哪一个方法会返回 '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')
- A: 1
- B: 2
- C: 2 and 3
- D: All of them
答案
答案: B
当通过 set 方法添加一个键值对,一个传递给 set方法的参数将会是键名,第二个参数将会是值。在这个case里,键名为 函数 () => 'greeting',值为'Hello world'。 myMap 现在就是 { () => 'greeting' => 'Hello world!' }。
1 是错的,因为键名不是 'greeting' 而是 () => 'greeting'。
3 是错的,因为我们给get 方法传递了一个新的函数。对象受 引用 影响。函数也是对象,因此两个函数严格上并不等价,尽管他们相同:他们有两个不同的内存引用地址。
116. 输出什么?
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)
- A:
{name: "Sarah", age: 22} - B:
{name: "Sarah", age: 23} - C:
{name: "Lydia", age: 22} - D:
{name: "Lydia", age: 23}
答案
答案: C
函数 changeAge 和函数 changeAgeAndName 有着不同的参数,定义一个 新 生成的对象 { ...person }。这个对象有着所有 person 对象 中 k/v 值的副本。
首项, 我们调用 changeAge 函数并传递 person 对象作为它的参数。这个函数对 age 属性进行加一操作。person 现在是 { name: "Lydia", age: 22 }。
然后,我们调用函数 changeAgeAndName ,然而我们没有传递参数。取而代之,x 的值等价 new 生成的对象: { ...person }。因为它是一个新生成的对象,它并不会对对象 person 造成任何副作用。person 仍然等价于 { name: "Lydia", age: 22 }。
117. 下面那个选项将会返回 6?
function sumValues(x, y, z) {
return x + y + z;
}
- A:
sumValues([...1, 2, 3]) - B:
sumValues([...[1, 2, 3]]) - C:
sumValues(...[1, 2, 3]) - D:
sumValues([1, 2, 3])
答案
答案: C
通过展开操作符 ...,我们可以 暂开 单个可迭代的元素。函数 sumValues function 接收三个参数: x, y 和 z。...[1, 2, 3] 的执行结果为 1, 2, 3,将会传递给函数 sumValues。
118. 输出什么?
let num = 1;
const list = ["🥳", "🤠", "🥰", "🤪"];
console.log(list[(num += 1)]);
- A:
🤠 - B:
🥰 - C:
SyntaxError - D:
ReferenceError
答案
答案: B
通过 += 操作符,我们对值 num 进行加 1 操作。 num 有初始值 1,因此 1 + 1 的执行结果为 2。数组 list 的第二项为 🥰,console.log(list[2]) 输出 🥰.
119. 输出什么?
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?.());
- A:
undefinedundefinedundefinedundefined - B:
MaraundefinedLydia Hallieundefined - C:
MaranullLydia Hallienull - D:
nullReferenceErrornullReferenceError
答案
答案: B
通过 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.
120. 输出什么?
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!`);
}
- A: We have to buy bananas!
- B: We don't have to buy bananas
- C:
undefined - D:
1
答案
答案: B
我们传递了一个状态 groceries.indexOf("banana") 给if条件语句。groceries.indexOf("banana") 返回 0, 一个 falsy 的值。因为if条件语句的状态为 falsy,else 块区内的代码执行,并且 We don't have to buy bananas! 被输出.
121. 输出什么?
const config = {
languages: [],
set language(lang) {
return this.languages.push(lang);
}
};
console.log(config.language);
- A:
function language(lang) { this.languages.push(lang } - B:
0 - C:
[] - D:
undefined
答案
答案: D
方法 language 是一个 setter。Setters 并不保存一个实际值,它们的使命在于 修改 属性。当调用方法 setter, 返回 undefined。
122. 输出什么?
const name = "Lydia Hallie";
console.log(!typeof name === "object");
console.log(!typeof name === "string");
- A:
falsetrue - B:
truefalse - C:
falsefalse - D:
truetrue
答案
答案: C
typeof name 返回 "string"。字符串 "string" 是一个 truthy 的值,因此 !typeof name 返回一个布尔值 false。 false === "object" 和 false === "string" 都返回 false。
(如果我们想检测一个值的类型,我们不应该用 !== 而不是 !typeof)
123. 输出什么?
const add = x => y => z => {
console.log(x, y, z);
return x + y + z;
};
add(4)(5)(6);
- A:
456 - B:
654 - C:
4functionfunction - D:
undefinedundefined6
答案
答案: A
函数 add 是一个返回 返回箭头函数的箭头函数 的箭头函数(still with me?)。第一个函数接收一个值为 4 的参数 x。我们调用第二个函数,它接收一个值为 5 的参数 y。然后我们调用第三个函数,它接收一个值为 6 的参数 z。当我们尝试在最后一个箭头函数中获取 x, y 和 z 的值,JS 引擎根据作用域链去找 x 和 y 的值。得到 4 5 6.
124. 输出什么?
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);
}
})();
- A:
Promise {1}Promise {2}Promise {3} - B:
Promise {<pending>}Promise {<pending>}Promise {<pending>} - C:
123 - D:
undefinedundefinedundefined
答案
答案: C
我们给 函数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.
125. 输出什么?
const myFunc = ({ x, y, z }) => {
console.log(x, y, z);
};
myFunc(1, 2, 3);
- A:
123 - B:
{1: 1}{2: 2}{3: 3} - C:
{ 1: undefined }undefinedundefined - D:
undefinedundefinedundefined
答案
答案: D
myFunc 期望接收一个包含 x, y 和 z 属性的对象作为它的参数。因为我们仅仅传递三个单独的数字值 (1, 2, 3) 而不是一个含有 x, y 和 z 属性的对象 ({x: 1, y: 2, z: 3}), x, y 和 z 有着各自的默认值 undefined.
126. 输出什么?
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))
- A: The driver drove 130 and has to pay 300
- B: The driver drove 130 mph and has to pay $300.00
- C: The driver drove undefined and has to pay undefined
- D: The driver drove 130.00 and has to pay 300.00
答案
答案: B
通过方法 Intl.NumberFormat,我们可以格式化任意区域的数字值。我们对数字值 130 进行 mile-per-hour 作为 unit 的 en-US 区域 格式化,结果为 130 mph。对数字值 300 进行 USD 作为 currentcy 的 en-US 区域格式化,结果为 $300.00.
127. 输出什么?
const spookyItems = ["👻", "🎃", "🕸"];
({ item: spookyItems[3] } = { item: "💀" });
console.log(spookyItems);
- A:
["👻", "🎃", "🕸"] - B:
["👻", "🎃", "🕸", "💀"] - C:
["👻", "🎃", "🕸", { item: "💀" }] - D:
["👻", "🎃", "🕸", "[object Object]"]
答案
答案: B
通过解构对象们,我们可以从右手边的对象中拆出值,并且将拆出的值分配给左手边对象同名的属性。在这种情况下,我们将值 "💀" 分配给 spookyItems[3]。相当于我们正在篡改数组 spookyItems,我们给它添加了值 "💀"。当输出 spookyItems 时,结果为 ["👻", "🎃", "🕸", "💀"]。
128. 输出什么?
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));
- A:
truefalsetruefalse - B:
truefalsefalsefalse - C:
falsefalsetruefalse - D:
falsetruefalsetrue
答案
答案: C
通过方法 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.
129. 输出什么?
const randomValue = 21;
function getInfo() {
console.log(typeof randomValue);
const randomValue = "Lydia Hallie";
}
getInfo();
- A:
"number" - B:
"string" - C:
undefined - D:
ReferenceError
答案
答案: D
通过 const 关键字声明的变量在被初始化之前不可被引用:这被称之为 暂时性死去。在函数 getInfo 中, 变量 randomValue 声明在getInfo 的作用域的此法环境中。在想要对 typeof randomValue 进行log之前,变量 randomValue 仍未被初始化: 错误ReferenceError 被抛出! JS引擎并不会根据作用域链网上寻找该变量,因为我们已经在 getInfo 函数中声明了 randomValue 变量。
130. 输出什么?
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!");
}
})();
- A:
Woah some cool data - B:
Oh finally! - C:
Woah some cool dataOh finally! - D:
Oops didn't workOh finally!
答案
答案: C
在 try 块区,我们打印 myPromise 变量的 awaited 值: "Woah some cool data"。因为try 块区没有错误抛出,catch 块区的代码并不执行。finally 块区的代码 总是 执行,"Oh finally!" 被输出。
131. 输出什么?
const emojis = ["🥑", ["✨", "✨", ["🍕", "🍕"]]];
console.log(emojis.flat(1));
- A:
['🥑', ['✨', '✨', ['🍕', '🍕']]] - B:
['🥑', '✨', '✨', ['🍕', '🍕']] - C:
['🥑', ['✨', '✨', '🍕', '🍕']] - D:
['🥑', '✨', '✨', '🍕', '🍕']
答案
答案: B
通过方法 flat, 我们可以创建一个新的, 已被扁平化的数组。被扁平化的深度取决于我们传递的值。在这个case里,我们传递了值 1 (并不必要,这是默认值),相当于只有第一层的数组才会被连接。即这个 case 里的 ['🥑'] and ['✨', '✨', ['🍕', '🍕']]。连接这两个数组得到结果 ['🥑', '✨', '✨', ['🍕', '🍕']].
132. 输出什么?
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);
- A:
0 - B:
1 - C:
2 - D:
3
答案
答案: D
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。
133. 输出什么?
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();
- A:
Promise! Last line! Promise! Last line! Last line! Promise! - B:
Last line! Timeout! Promise! Last line! Timeout! Promise! - C:
Promise! Last line! Last line! Promise! Timeout! Timeout! - D:
Last line! Promise! Promise! Last line! Timeout! Timeout!
答案
答案: D
首先,我们调用 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!
134. 我们怎样才能在 index.js 中调用 sum.js? 中的 sum?
// sum.js
export default function sum(x) {
return x + x;
}
// index.js
import * as sum from "./sum";
- A:
sum(4) - B:
sum.sum(4) - C:
sum.default(4) - D: 默认导出不用
*来导入,只能具名导出
答案
答案: C
使用符号 *,我们引入文件中的所有值,包括默认和具名。如果我们有以下文件:
// 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 来调用该函数
135. 输出什么?
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;
- A:
Added a new property! - B:
Accessed a property! - C:
Added a new property!Accessed a property! - D: 没有任何输出
答案
答案: C
使用 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!"。
136. 以下哪一项会对对象 person 有副作用?
const person = { name: "Lydia Hallie" };
Object.seal(person);
- A:
person.name = "Evan Bacon" - B:
person.age = 21 - C:
delete person.name - D:
Object.assign(person, { age: 21 })
答案
答案: A
使用 Object.seal 我们可以防止新属性 被添加,或者存在属性 被移除.
然而,你仍然可以对存在属性进行更改。
137. 以下哪一项会对对象 person 有副作用?
const person = {
name: "Lydia Hallie",
address: {
street: "100 Main St"
}
};
Object.freeze(person);
- A:
person.name = "Evan Bacon" - B:
delete person.address - C:
person.address.street = "101 Main St" - D:
person.pet = { name: "Mara" }
答案
答案: C
使用方法 Object.freeze 对一个对象进行 冻结。不能对属性进行添加,修改,删除。
然而,它仅 对对象进行 浅 冻结,意味着只有 对象中的 直接 属性被冻结。如果属性是另一个 object,像案例中的 address,address 中的属性没有被冻结,仍然可以被修改。
138. 以下哪一项会对对象 person 有副作用?
const person = {
name: "Lydia Hallie",
address: {
street: "100 Main St"
}
};
Object.freeze(person);
- A:
person.name = "Evan Bacon" - B:
delete person.address - C:
person.address.street = "101 Main St" - D:
person.pet = { name: "Mara" }
答案
答案: C
使用方法 Object.freeze 对一个对象进行 冻结。不能对属性进行添加,修改,删除。
然而,它仅 对对象进行 浅 冻结,意味着只有 对象中的 直接 属性被冻结。如果属性是另一个 object,像案例中的 address,address 中的属性没有被冻结,仍然可以被修改。
139. 输出什么?
const add = x => x + x;
function myFunc(num = 2, value = add(num)) {
console.log(num, value);
}
myFunc();
myFunc(3);
- A:
24and36 - B:
2NaNand3NaN - C:
2Errorand36 - D:
24and3Error
答案
答案: A
首先我们不传递任何参数调用 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 的值。
140. 输出什么?
class Counter {
#number = 10
increment() {
this.#number++
}
getNum() {
return this.#number
}
}
const counter = new Counter()
counter.increment()
console.log(counter.#number)
- A:
10 - B:
11 - C:
undefined - D:
SyntaxError
答案
答案: D
在 ES2020 中,通过 # 我们可以给 class 添加私有变量。在 class 的外部我们无法获取该值。当我们尝试输出 counter.#number,语法错误被抛出:我们无法在 class Counter 外部获取它!
141. 选择哪一个?
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 }
- A:
yield getMembers(teams[i].members) - B:
yield* getMembers(teams[i].members) - C:
return getMembers(teams[i].members) - D:
return yield getMembers(teams[i].members)
答案
答案: B
为了遍历 teams 数组中对象的属性 members 中的每一项,我们需要将 teams[i].members 传递给 Generator 函数 getMembers。Generator 函数返回一个 generator 对象。为了遍历这个 generator 对象中的每一项,我们需要使用 yield*.
如果我们没有写 yield,return yield 或者 return,整个 Generator 函数不会第一时间 return 当我们调用 next 方法.
142. 输出什么?
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);
- A:
["coding"] - B:
["coding", "dancing"] - C:
["coding", "dancing", "baking"] - D:
["coding", "running", "dancing", "baking"]
答案
答案: C
函数 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"]
143. 输出什么?
class Bird {
constructor() {
console.log("I'm a bird. 🦢");
}
}
class Flamingo extends Bird {
constructor() {
console.log("I'm pink. 🌸");
super();
}
}
const pet = new Flamingo();
- A:
I'm pink. 🌸 - B:
I'm pink. 🌸I'm a bird. 🦢 - C:
I'm a bird. 🦢I'm pink. 🌸 - D: Nothing, we didn't call any method
答案
答案: B
我们创建了类 Flamingo 的实例 pet。当我们实例化这个实例,Flamingo 中的 constructor 被调用。首相,输出 "I'm pink. 🌸", 之后我们调用super()。super() 调用父类的构造函数,Bird。Bird 的构造函数被调用,并输出 "I'm a bird. 🦢"。
144. 哪一个选项会导致报错?
const emojis = ["🎄", "🎅🏼", "🎁", "⭐"];
/* 1 */ emojis.push("🦌");
/* 2 */ emojis.splice(0, 2);
/* 3 */ emojis = [...emojis, "🥂"];
/* 4 */ emojis.length = 0;
- A: 1
- B: 1 and 2
- C: 3 and 4
- D: 3
答案
答案: D
const 关键字意味着我们不能 重定义 变量中的值,它 仅可读。而然,值本身不可修改。数组 emojis 中的值可被修改,如 push 新的值, 拼接,又或者将数组的长度设置为0。
145. 我们需要向对象 person 添加什么,以致执行 [...person] 时获得形如 ["Lydia Hallie", 21] 的输出?
const person = {
name: "Lydia Hallie",
age: 21
}
[...person] // ["Lydia Hallie", 21]
- A: 不需要,对象默认就是可迭代的
- B:
*[Symbol.iterator]() { for (let x in this) yield* this[x] } - C:
*[Symbol.iterator]() { for (let x in this) yield* Object.values(this) } - D:
*[Symbol.iterator]() { for (let x in this) yield this }
答案
答案: C
对象默认并不是可迭代的。如果迭代规则被定义,则一个对象是可迭代的(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。
146. 输出什么?
let count = 0;
const nums = [0, 1, 2, 3];
nums.forEach(num => {
if (num) count += 1
})
console.log(count)
- A: 1
- B: 2
- C: 3
- D: 4
答案
答案: C
forEach循环中的if条件检查num的值是真还是假。 由于nums数组中的第一个数字为0,即:伪造的值,因此if语句的代码块将不会执行。 对于nums数组中的其他3个数字,count只会增加,分别为1、2和3。 由于count被增加了1 3倍,因此count的值是3。
147. 输出什么?
function getFruit(fruits) {
console.log(fruits?.[1]?.[1])
fruits?(console.log(.[1]);)
}
getFruit([['🍊', '🍌'], ['🍍']])
getFruit()
getFruit([['🍍'], ['🍊', '🍌']])
- A:
null,undefined, 🍌 - B:
[],null, 🍌 - C:
[],[], 🍌 - D:
undefined,undefined, 🍌
答案
答案: D
“?”允许我们选择访问对象内更深层的嵌套属性。我们正在尝试将子项记录在fruits数组的索引“ 1”上的子数组中的索引“ 1”上。如果fruits数组中的索引1上的子数组不存在,它将简单地返回undefined。如果存在“水果”数组中索引“ 1”上的子数组,但是此子数组在其“ 1”索引上没有任何项,则它还将返回undefined。
首先,我们试图将第二项记录在[[['🍊','🍌'],['🍍']]]的['🍍']`子数组中。此子数组仅包含一个项目,这意味着索引“ 1”上没有任何项目,并返回“ undefined”。
然后,我们在调用getFruits函数时未传递值作为参数,这意味着默认情况下,fruits的值为undefined。由于我们有条件地链接“水果”的索引“ 1”上的项目,因此它返回“未定义”,因为索引“ 1”上的该项目不存在。
最后,我们尝试将第二项记录在['🍍'],['🍊','🍌']的['🍊','🍌']子数组中。该子数组中索引“ 1”上的项是“🍌”,将其记录下来。
148. 输出什么?
class Calc {
constructor() {
this.count = 0
}
increase() {
this.count ++
}
}
const calc = new Calc()
new Calc().increase()
console.log(calc.count)
- A:
0 - B:
1 - C:
undefined - D:
ReferenceError
答案
答案: A
我们将变量calc设置为等于Calc类的新实例。 然后,我们实例化一个新的Calc实例,并对该实例调用increase方法。 由于count属性位于Calc类的构造函数中,因此count属性不会在Calc的原型上共享。 这意味着对于calc所指向的实例,count的值尚未更新,count仍为0。
149. 输出什么?
const user = {
email: "e@mail.com",
password: "12345"
}
const updateUser = ({ email, password }) => {
if (email) {
Object.assign(user, { email })
}
if (password) {
user.password = password
}
return user
}
const updatedUser = updateUser({ email: "new@email.com" })
console.log(updatedUser === user)
- A:
false - B:
true - C:
TypeError - D:
ReferenceError
答案
答案: B
如果将用户的“ email”和“ password”属性的值传递给函数,则“ updateUser”函数将其值更新,此后该函数将返回“ user”对象。 函数“ updateUser”的返回值是“ user”对象,这意味着updatedUser的值是对“ user”指向的同一“ user”对象的引用。 “ updatedUser === user”等于“ true”。
150. 输出什么?
const fruit = ['🍌', '🍊', '🍎']
fruit.slice(0, 1)
fruit.splice(0, 1)
fruit.unshift('🍇')
- A:
['🍌', '🍊', '🍎'] - B:
['🍊', '🍎'] - C:
['🍇', '🍊', '🍎'] - D:
['🍇', '🍌', '🍊', '🍎']
答案
答案: C
首先,我们在fruit数组上调用slice方法。 slice方法不会修改原始数组,但会返回从数组中切出的值:香蕉表情符号。
然后,我们在fruit数组上调用splice方法。 拼接方法确实修改了原始数组,这意味着fruit数组现在由[[🍊],'🍎']组成。
最后,我们在“fruit”数组上调用“ unshift”方法,该方法通过添加提供的值“🍇”作为数组中的第一个元素来修改原始数组。 水果数组现在由“ ["🍇”,“🍊”,“🍎”]组成。
151. 输出什么?
const animals = {};
let dog = { emoji: '🐶' }
let cat = { emoji: '🐈' }
animals[dog] = { ...dog, name: "Mara" }
animals[cat] = { ...cat, name: "Sara" }
console.log(animals[dog])
- A:
{ emoji: "🐶", name: "Mara" } - B:
{ emoji: "🐈", name: "Sara" } - C:
undefined - D:
ReferenceError
答案
答案: B
对象键将转换为字符串。
由于“ dog”的值是一个对象,因此“ animals [dog]”实际上意味着我们正在创建一个名为““ Object Object””的新属性,该属性等于新对象。 “ animals [“ object Object”]]现在等于{emoji:“🐶”,名称:“ Mara”}。
“ cat”也是一个对象,这意味着“ animals [cat]”实际上意味着我们正在使用新的cat属性覆盖“ animals [“” object Object“”“]]的值。
因为将dog对象转换为字符串结果“ object Object”,所以记录animals [dog]或实际上是“ animals [” object Object“]都会返回{emoji:”🐈“,名称:” Sara”}`。
152. 输出什么?
const user = {
email: "my@email.com",
updateEmail: email => {
this.email = email
}
}
user.updateEmail("new@email.com")
console.log(user.email)
- A:
my@email.com - B:
new@email.com - C:
undefined - D:
ReferenceError
答案
答案: A
updateEmail函数是一个箭头函数,未绑定到user对象。 这意味着关键字“ this”不是在引用“ user”对象,而是在这种情况下引用了全局范围。 用户对象内的电子邮件值不会被更新。 记录“ user.email”的值时,将返回“ my@email.com”的原始值。
153. 输出什么?
const promise1 = Promise.resolve('First')
const promise2 = Promise.resolve('Second')
const promise3 = Promise.reject('Third')
const promise4 = Promise.resolve('Fourth')
const runPromises = async () => {
const res1 = await Promise.all([promise1, promise2])
const res2 = await Promise.all([promise3, promise4])
return [res1, res2]
}
runPromises()
.then(res => console.log(res))
.catch(err => console.log(err))
- A:
[['First', 'Second'], ['Fourth']] - B:
[['First', 'Second'], ['Third', 'Fourth']] - C:
[['First', 'Second']] - D:
'Third'
答案
答案: D
Promise.all方法并行运行传递的promise。 如果一个Promise失败,则Promise.all方法(_rejects)带有rejected的promise的值。 在这种情况下,“ promise3”reject返回了“Third”。 我们正在对runPromises调用进行链接的catch方法中捕获被拒绝的值,以捕获runPromises函数中的所有错误。 因为promise3拒绝了这个值,所以只有Third被记录。
154.当method是下列选项中的那项的时候,将会输出 { name: "Lydia", age: 22 }?
const keys = ["name", "age"]
const values = ["Lydia", 22]
const method = /* ?? */
Object[method](keys.map((_, i) => {
return [keys[i], values[i]]
})) // { name: "Lydia", age: 22 }
- A:
entries - B:
values - C:
fromEntries - D:
forEach
答案
答案: C
fromEntries方法将二维数组转换为对象。 每个子数组中的第一个元素将是键,每个子数组中的第二个元素将是值。 在这种情况下,我们将在“ keys”数组上进行映射,该数组将返回一个数组,其中第一个元素是当前索引上的键数组的项目,第二个元素是当前索引上的值数组的项目。
这将创建一个包含正确键和值的子数组数组,从而导致`{name:“ Lydia”,age:22}
155. 输出什么?
const createMember = ({ email, address = {}}) => {
const validEmail = /.+\@.+\..+/.test(email)
if (!validEmail) throw new Error("Valid email pls")
return {
email,
address: address ? address : null
}
}
const member = createMember({ email: "my@email.com" })
console.log(member)
- A:
{ email: "my@email.com", address: null } - B:
{ email: "my@email.com" } - C:
{ email: "my@email.com", address: {} } - D:
{ email: "my@email.com", address: undefined }
答案
答案: C
“address”的默认值是一个空对象“ {}”。 当我们将变量member设置为等于createMember函数返回的对象时,我们没有传递address的值,这意味着address的值是默认的空对象{}。 空对象是真实值,这意味着“address? address:null`,条件返回“ true”。 address的值为空对象“ {}”。
156. 输出什么?
let randomValue = { name: "Lydia" }
randomValue = 23
if (!typeof randomValue === "string") {
console.log("It's not a string!")
} else {
console.log("Yay it's a string!")
}
- A:
It's not a string! - B:
Yay it's a string! - C:
TypeError - D:
undefined
答案
答案: B
“ if”语句中的条件检查“!typeof randomValue”的值是否等于““ string””。 “!”运算符将值转换为布尔值。 如果值为真,则返回的值为“ false”;如果值为虚假,则返回的值为“ true”。 在这种情况下,“ typeof randomValue”的返回值是真实值““ string””,这意味着“!typeof randomValue”的值是布尔值“ false”。
!! typeof randomValue ===“ string”总是返回false,因为我们实际上是在检查“ false ===” string“。 由于条件返回了“ false”,因此将运行“ else”语句的代码块,并记录“是的,是字符串!”。
❤️ 看完三件事: 如果你觉得这篇内容对你挺有启发,我想邀请你帮我个小忙:
点赞,让更多的人也能看到这篇内容,也方便自己随时找到这篇内容(收藏不点赞,都是耍流氓 -_-) 关注我们,不定期分好文章。 也看看其它文章
👏欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。