来源:
javascript-questions/zh-CN/README-zh_CN.md at master · lydiahallie/javascript-questions · GitHub
记录一些有趣的题。
系列:
【前端】35道JavaScript进阶问题(1)
【前端】20道JavaScript进阶问题(2)
1
[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
答:
1 2
undefined 3
undefined 4
解析:
reduce 方法接收一个回调函数和一个可选的初始值。 在你的例子中,回调函数是 (x, y) => console.log(x, y),没有提供初始值。
reduce 方法会对数组中的每个元素执行回调函数。 在每次调用回调函数时,它都会接收两个参数:累积值(我们这里称之为 x)和当前元素(我们这里称之为 y)。然后,回调函数的返回值会成为下一次调用回调函数时的累积值。
在你的例子中,回调函数并没有返回任何值,所以在第二次及以后调用回调函数时,x 的值都是 undefined。
以下是详细的步骤:
-
首先,reduce 方法会取数组的第一个元素(也就是 1)作为累积值 x,然后取数组的第二个元素(也就是 2)作为当前元素 y,然后调用回调函数。所以,第一次调用 console.log(x, y) 会输出 1 2。
-
然后,因为回调函数没有返回任何值,所以下一次调用回调函数时,x 的值是 undefined,y 的值是数组的第三个元素(也就是 3)。所以,第二次调用 console.log(x, y) 会输出 undefined 3。
-
同样,因为回调函数没有返回任何值,所以下一次调用回调函数时,x 的值还是 undefined,y 的值是数组的第四个元素(也就是 4)。所以,第三次调用 console.log(x, y) 会输出 undefined 4。
-
所以,你的代码会依次输出 1 2、undefined 3 和 undefined 4。
2
使用哪个构造函数可以成功继承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;
}
};
答:
2
解析:
在 JavaScript 中,如果你想要继承一个类,你需要在子类的构造函数中首先调用 super 函数。 super 函数会调用父类的构造函数,这是必须的,否则 JavaScript 会不能访问到this
关键字。
所以,在你的例子中,只有第二个构造函数可以成功继承 Dog 类:
constructor(name, size) {
super(name);
this.size = size;
}
在这个构造函数中,首先调用了 super(name) 来调用 Dog 类的构造函数,然后设置了 size 属性。这样就成功地创建了一个 Labrador 对象,它既有 Dog 类的 name 属性,也有自己的 size 属性。
3
输出为?
// 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 sum.js
, running index.js
, 3
解析:
import命令是编译阶段执行的。在代码运行之前。因此这意味着被导入的模块会先运行,而导入模块的文件会后执行。
这是 CommonJS 中require()
和import
之间的区别。使用require(),您可以在运行代码时根据需要加载依赖项。 如果我们使用require
而不是import
,running index.js
,running sum.js
,3
会被依次打印。
4
console.log(Number(2) === Number(2))
console.log(Boolean(false) === Boolean(false))
console.log(Symbol('foo') === Symbol('foo'))
答:
true
, true
, false
解析:
Number(2)
会返回一个数值 2,因此第一个为true。
若是new Number(2),则会返回一个对象。
console.log(new Number(2) === new Number(2)); // false
每个Symbol都是完全唯一的。
传递给Symbol
的参数只是给Symbol
的一个描述。Symbol
的值不依赖于传递的参数。当我们测试相等时,我们创建了两个全新的符号:第一个Symbol('foo')
,第二个Symbol('foo')
,这两个值是唯一的,彼此不相等,因此返回false
。
5
const name = "Lydia Hallie"
console.log(name.padStart(13))
console.log(name.padStart(2))
答:
Lydia Hallie
Lydia Hallie
解析:
padStart传入的参数表示前面添加空格后总字符串的长度。小于原有长度的不处理。
6
console.log("🥑" + "💻");
- A:
"🥑💻"
- B:
257548
- C: A string containing their code points
- D: Error
答:
A。
7
如何能打印出console.log
语句后注释掉的值?
function* startGame() {
const ans = yield "Do you love JavaScript?";
if (ans !== "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 ❤️
答:
console.log(game.next().value); // Do you love JavaScript?
console.log(game.next('Yes').value); // JavaScript loves you back ❤️
在第一次调用game.next()
时,生成器函数会运行到第一个yield
语句,并返回其右侧的值。然后,在第二次调用game.next('Yes')
时,yield
表达式的结果将被替换为传递给next()
的参数(在这种情况下是"Yes"),然后函数继续执行,直到遇到下一个yield
或者结束,返回其值。
8
console.log(String.raw`Hello\nworld`);
答:
Hello\nworld
String.raw
是一个模板字符串的标签函数,它会将所有的反斜杠(\)视为普通字符。因此,\n不会被解析为换行符,而是会被视为两个独立的字符:反斜杠和n。
但是,要直接接一个字符串,而不是变量:
const path = `C:\Documents\Projects\table.html`
console.log(String.raw`${path}`) // C:DocumentsProjects able.html
console.log(String.raw`C:\Documents\Projects\table.html`) // C:\Documents\Projects\table.html
9
async function getData() {
return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);
答:
Promise { <pending> }
异步函数始终返回一个 promise。await
仍然需要等待 promise 的解决:当我们调用getData()
并将其赋值给data
,此时data
为getData
方法返回的一个挂起pending
的 promise,该 promise 并没有解决。
如果我们想要访问已解决的值"I made it!"
,可以在data
上使用.then()
方法:
data.then(res => console.log(res))
这样将打印 "I made it!"
10
const box = { x: 10, y: 20 };
Object.freeze(box);
const shape = box;
shape.x = 100;
console.log(shape)
答:
{ x: 10, y: 20 }
Object.freeze
使得无法添加、删除或修改对象的属性(除非属性的值是另一个对象)。
当我们创建变量shape
并将其设置为等于冻结对象box
时,shape
指向的也是冻结对象。你可以使用Object.isFrozen
检查一个对象是否被冻结,上述情况,Object.isFrozen(shape)
将返回true
。
由于shape
被冻结,并且x
的值不是对象,所以我们不能修改属性x
。x
仍然等于10
,{x:10,y:20}
被打印。
注意,上述例子我们对属性x
进行修改,可能会导致抛出 TypeError 异常(最常见但不仅限于严格模式下时)。
11
const { name: myName } = { name: "Lydia" };
console.log(name);
答:
ReferenceError
当我们从右侧的对象解构属性name
时,我们将其值Lydia
分配给名为myName
的变量。
使用{name:myName}
,我们是在告诉 JavaScript 我们要创建一个名为myName
的新变量,并且其值是右侧对象的name
属性的值。
当我们尝试打印name
,一个未定义的变量时,就会引发ReferenceError
。
const { name: myName } = { name: "Lydia" };
console.log(name); // ReferenceError: name is not defined
console.log(myName); // Lydia
12
const myLifeSummedUp = ["☕", "💻", "🍷", "🍫"]
for (let item in myLifeSummedUp) {
console.log(item)
}
for (let item of myLifeSummedUp) {
console.log(item)
}
答:
0
1
2
3
☕
💻
🍷
🍫
通过for-in
循环,我们可以遍历一个对象自有的、继承的、可枚举的、非 Symbol 的属性。在数组中,可枚举属性是数组元素的“键”,即它们的索引。类似于下面这个对象:
{0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}
其中键则是可枚举属性,因此 0
,1
,2
,3
被记录。
通过for-of
循环,我们可以迭代可迭代对象(包括 Array
,Map
,Set
,String
,arguments
等)。当我们迭代数组时,在每次迭代中,不同属性的值将被分配给变量item
,因此“☕”
,“💻”
,“🍷”
,“🍫”
被打印。
13
var status = "😎"
setTimeout(() => {
const status = "😍"
const data = {
status: "🥑",
getStatus() {
return this.status
}
}
console.log(data.getStatus())
console.log(data.getStatus.call(this))
}, 0)
答:
🥑
undefined
这段代码中,data.getStatus()
和data.getStatus.call(this)
的返回值取决于函数调用时的上下文(也就是this的值)。
-
data.getStatus()
:在这种情况下,this指向的是data对象,因此this.status返回的是data对象的status属性,即"🥑"。所以,console.log(data.getStatus())
将打印出"🥑"。 -
data.getStatus.call(this)
:Function.prototype.call
方法允许你改变函数调用时this的值。在这个例子中,由于call方法被调用时没有提供参数,所以this的值将是全局对象(在非严格模式下)。然而,由于这段代码是在一个箭头函数中执行的,this的值实际上是从包围它的普通(非箭头)函数(在这种情况下是全局作用域)继承来的。在全局作用域中,this通常指向全局对象(在浏览器中是window),但是由于status不是全局对象的属性,this.status将返回undefined。所以,console.log(data.getStatus.call(this))
将打印出undefined。
需要注意的是,这个行为可能会因JavaScript运行环境的不同而有所差异。例如,在Node.js中,全局this的值是一个空对象,而不是global对象。
14
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))
答:
ReferenceError: message is not defined
const
和let
声明的变量是具有块级作用域的,块是大括号({}
)之间的任何东西,即上述情况if / else
语句的花括号。
在return的地方已经没有message这个变量了。
15
打印什么?
fetch('https://www.website.com/api/user/1')
.then(res => res.json())
.then(res => console.log(res))
- A:
fetch
方法的结果 - B: 第二次调用
fetch
方法的结果 - C: 前一个
.then()
中回调方法返回的结果 - D: 总是
undefined
答:
C。
解析:
第二个.then
中res
的值等于前一个.then
中的回调函数返回的值。你可以像这样继续链接.then
,将值传递给下一个处理程序。
16
// 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" }
使用import * as name
语法,我们将module.js
文件中所有export
导入到index.js
文件中,并且创建了一个名为data
的新对象。在module.js
文件中,有两个导出:默认导出和命名导出。默认导出是一个返回字符串“Hello World”的函数,命名导出是一个名为name
的变量,其值为字符串“Lydia”
。
data
对象具有默认导出的default
属性,其他属性具有指定 exports 的名称及其对应的值。
17
class Person {
constructor(name) {
this.name = name
}
}
const member = new Person("John")
console.log(typeof member)
答:
object
解析:
类是构造函数的语法糖,如果用构造函数的方式来重写Person
类则将是:
function Person() {
this.name = name
}
通过new
来调用构造函数,将会生成构造函数Person
的实例,对实例执行typeof
关键字将返回"object"
,上述情况打印出"object"
。
18
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: ...} undefined
解析:
在JavaScript中,只有通过function关键字定义的函数才有prototype属性。箭头函数并没有prototype属性。
所以,console.log(giveLydiaPizza.prototype)
将打印出giveLydiaPizza函数的原型对象,通常是一个空对象,但包含一个constructor属性指向该函数本身。
而console.log(giveLydiaChocolate.prototype)
将打印出undefined
,因为giveLydiaChocolate是一个箭头函数,它没有prototype属性。
19
function getItems(fruitList, ...args, favoriteFruit) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange")
答:
SyntaxError: Rest parameter must be last formal parameter
解析:
... args
是剩余参数,剩余参数的值是一个包含所有剩余参数的数组,并且只能作为最后一个参数。
20
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
undefined
b is bigger
undefined
解析:
相当于:
function nums(a, b) {
if (a > b) console.log("a is bigger");
else console.log("b is bigger");
return;
a + b;
}
a+b永远走不到,只return一个undefined。