Javascript 语法错题集
- 题库来源: JavaScript 进阶问题列表
# 1. 输出是什么?
const shape = {
radius: 10,
diameter() {
return this.radius * 2;
},
perimeter: () => 2 * Math.PI * this.radius,
};
shape.diameter();
shape.perimeter();
2
3
4
5
6
7
8
9
10
- A:
20
and62.83185307179586
- B:
20
andNaN
- C:
20
and63
- D:
NaN
and63
答案
答案:B
注意 diameter
的值是一个常规函数,但是 perimeter
的值是一个箭头函数。
对于箭头函数,this
关键字指向的是它当前周围作用域(简单来说是包含箭头函数的常规函数,如果没有常规函数的话就是全局对象),这个行为和常规函数不同。这意味着当我们调用 perimeter
时,this
不是指向 shape
对象,而是它的周围作用域(在例子中是 window
)。
在 window
中没有 radius
这个属性,因此返回 undefined
。
# 2.所有对象都有原型
- A:对
- B:错
答案
答案:B 除了基本对象(base object),所有对象都有原型。基本对象可以访问一些方法和属性,比如 .toString。这就是为什么你可以使用内置的 JavaScript 方法!所有这些方法在原型上都是可用的。虽然 JavaScript 不能直接在对象上找到这些方法,但 JavaScript 会沿着原型链找到它们,以便于你使用。
# 3.输出是什么?
[1, 2, 3].map((num) => {
if (typeof num === "number") return;
return num * 2;
});
2
3
4
- A:
[]
- B:
[null,null,null]
- C:
[undefined,undefined,undefined]
- D:
[3 x empty]
答案
对数组进行映射的时候,num就是当前循环到的元素。在这个例子中,所有的映射都是 number 类型,所以 if 中的判断typeof num === "number"结果都是true.map 函数创建了新数组并且将函数的返回值插入数组。
但是,没有任何值返回。当函数没有返回任何值时,即默认返回 undefined.对数组中的每一个元素来说,函数块都得到了这个返回值,所以结果中每一个元素都是 undefined.
# 4. 输出什么?
const name = "Lydia Hallie";
console.log(name.padStart(13));
console.log(name.padStart(2));
2
3
- A:
"Lydia Hallie"
,"Lydia Hallie"
- B:
" Lydia Hallie"
," Lydia Hallie"
("[13x whitespace]Lydia Hallie"
,"[2x whitespace]Lydia Hallie"
) - C:
" Lydia Hallie"
,"Lydia Hallie"
("[1x whitespace]Lydia Hallie"
,"Lydia Hallie"
) - D:
"Lydia Hallie"
,"Lyd"
答案
答案:C
使用padStart
方法,我们可以在字符串的开头添加填充。传递给此方法的参数是字符串的总长度(包含填充)。字符串Lydia Hallie
的长度为12
,因此name.padStart(13)
在字符串的开头只会插入 1(13 - 12 = 1
)个空格。
如果传递给padStart
方法的参数小于字符串的长度,则不会添加填充。
# 5. 输出什么?
async function getData() {
return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);
2
3
4
5
6
- A:
"I made it!"
- B:
Promise {<resolved>: "I made it!"}
- C:
Promise {<pending>}
- D:
undefined
答案
答案:C
异步函数始终返回一个 promise。await
仍然需要等待 promise 的解决:当我们调用getData()
并将其赋值给data
,此时data
为getData
方法返回的一个挂起的 promise,该 promise 并没有解决。
如果我们想要访问已解决的值"I made it!"
,可以在data
上使用.then()
方法:
data.then(res => console.log(res))
这样将打印 "I made it!"
# 6. 输出什么?
const box = { x: 10, y: 20 };
Object.freeze(box);
const shape = box;
shape.x = 100;
console.log(shape);
2
3
4
5
6
7
- A:
{ x: 100, y: 20 }
- B:
{ x: 10, y: 20 }
- C:
{ x: 100 }
- D:
ReferenceError
答案
答案:B
Object.freeze
使得无法添加、删除或修改对象的属性(除非属性的值是另一个对象)。
当我们创建变量shape
并将其设置为等于冻结对象box
时,shape
指向的也是冻结对象。你可以使用Object.isFrozen
检查一个对象是否被冻结,上述情况,Object.isFrozen(shape)
将返回true
。
由于shape
被冻结,并且x
的值不是对象,所以我们不能修改属性x
。x
仍然等于10
,{x:10,y:20}
被打印。
注意,上述例子我们对属性x
进行修改,可能会导致抛出 TypeError 异常(最常见但不仅限于严格模式下时)。
# 7. 输出什么?
const { name: myName } = { name: "Lydia" };
console.log(name);
2
3
- A:
"Lydia"
- B:
"myName"
- C:
undefined
- D:
ReferenceError
答案
答案:D
当我们从右侧的对象解构属性name
时,我们将其值Lydia
分配给名为myName
的变量。
使用{name:myName}
,我们是在告诉 JavaScript 我们要创建一个名为myName
的新变量,并且其值是右侧对象的name
属性的值。
当我们尝试打印name
,一个未定义的变量时,就会引发ReferenceError
。
# 8. 输出什么?
var status = "😎";
setTimeout(() => {
const status = "😍";
const data = {
status: "🥑",
getStatus() {
return this.status;
},
};
console.log(data.getStatus());
console.log(data.getStatus.call(this));
}, 0);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- A:
"🥑"
and"😍"
- B:
"🥑"
and"😎"
- C:
"😍"
and"😎"
- D:
"😎"
and"😎"
答案
答案:B
this
关键字的指向取决于使用它的位置。在函数中,比如getStatus
,this
指向的是调用它的对象,上述例子中data
对象调用了getStatus
,因此this
指向的就是data
对象。当我们打印this.status
时,data
对象的status
属性被打印,即"🥑"
。
使用call
方法,可以更改this
指向的对象。data.getStatus.call(this)
是将this
的指向由data
对象更改为全局对象。在全局对象上,有一个名为status
的变量,其值为”😎“
。因此打印this.status
时,会打印“😎”
。
# 9. 输出什么?
const person = {
name: "Lydia",
age: 21,
};
for (const [x, y] of Object.entries(person)) {
console.log(x, y);
}
2
3
4
5
6
7
8
- A:
name
Lydia
andage
21
- B:
["name", "Lydia"]
and["age", 21]
- C:
["name", "age"]
andundefined
- D:
Error
答案
答案:A
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
。
# 10. 输出什么?
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))
2
3
4
5
6
7
8
- A:
[1, [2, 3, 4]]
andSyntaxError
- B:
[1, [2, 3, 4]]
and{ name: "Lydia", age: 21 }
- C:
[1, 2, 3, 4]
and{ name: "Lydia", age: 21 }
- D:
Error
and{ name: "Lydia", age: 21 }
答案
答案:A
getList
函数接收一个数组作为其参数。在getList
函数的括号之间,我们立即解构这个数组。您可以将其视为:
[x, ...y] = [1, 2, 3, 4]
使用剩余的参数... y
,我们将所有剩余参数放在一个数组中。在这种情况下,其余的参数是2
,3
和4
。 y
的值是一个数组,包含所有其余参数。在这种情况下,x
的值等于1
,所以当我们打印[x,y]
时,会打印[1,[2,3,4]]
。
getUser
函数接收一个对象。对于箭头函数,如果只返回一个值,我们不必编写花括号。但是,如果您想从一个箭头函数返回一个对象,您必须将它写在圆括号之间,否则两个花括号之间的所有内容都将被解释为一个块语句!在这种情况下,花括号之间的代码不是有效的 JavaScript 代码,因此会抛出 SyntaxError。
以下函数将返回一个对象:
const getUser = user => ({ name: user.name, age: user.age })
# 11. 输出什么?
const name = "Lydia";
console.log(name());
2
3
- A:
SyntaxError
- B:
ReferenceError
- C:
TypeError
- D:
undefined
答案
答案:C
变量name
保存字符串的值,该字符串不是函数,因此无法调用。
当值不是预期类型时,会抛出TypeErrors
。JavaScript 期望name
是一个函数,因为我们试图调用它。但它是一个字符串,因此抛出TypeError
:name is not a function
当你编写了一些非有效的 JavaScript 时,会抛出语法错误,例如当你把return
这个词写成retrun
时。
当 JavaScript 无法找到您尝试访问的值的引用时,抛出ReferenceErrors
。
# 12.输出什么?
const one = false || {} || null;
const two = null || false || "";
const three = [] || 0 || true;
console.log(one, two, three);
2
3
4
5
- A:
false
null
[]
- B:
null
""
true
- C:
{}
""
[]
- D:
null
null
true
答案
答案:C
使用||
运算符,我们可以返回第一个真值。如果所有值都是假值,则返回最后一个值。
(false || {} || null)
:空对象{}
是一个真值。这是第一个(也是唯一的)真值,它将被返回。one
等于{}
。
(null || false ||“”)
:所有值都是假值。这意味着返回传递的值""
。two
等于""
。
([] || 0 ||“”)
:空数组[]
是一个真值。这是第一个返回的真值。three
等于[]
。
# 13. 输出什么?
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);
2
3
4
5
6
7
8
9
10
11
12
13
- A:
a
anda
- B:
a
andundefined
- C:
['a', 'b', 'c']
anda
- D:
a
and['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
2
在函数 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
2
3
4
# 14. 将会发生什么?
let config = {
alert: setInterval(() => {
console.log("Alert!");
}, 1000),
};
config = null;
2
3
4
5
6
7
- A:
setInterval
的回调不会被调用 - B:
setInterval
的回调被调用一次 - C:
setInterval
的回调仍然会被每秒钟调用 - D: 我们从没调用过
config.alert()
, config 为null
答案
答案:C
一般情况下当我们将对象赋值为 null
,那些对象会被进行 垃圾回收(garbage collected) 因为已经没有对这些对象的引用了。然而,setInterval
的参数是一个箭头函数(所以上下文绑定到对象 config
了),回调函数仍然保留着对 config
的引用。只要存在引用,对象就不会被垃圾回收。因为没有被垃圾回收,setInterval
的回调每 1000ms (1s) 会被调用一次。
# 15. 输出什么?
const name = "Lydia Hallie";
console.log(!typeof name === "object");
console.log(!typeof name === "string");
2
3
4
- A:
false
true
- B:
true
false
- C:
false
false
- D:
true
true
答案
答案:C
typeof name
返回 "string"
。字符串 "string"
是一个 truthy 的值,因此 !typeof name
返回一个布尔值 false
。false === "object"
和 false === "string"
都返回 false
。
(如果我们想检测一个值的类型,我们应该用 !==
而不是 !typeof
)
# 16. 输出什么?
const myFunc = ({ x, y, z }) => {
console.log(x, y, z);
};
myFunc(1, 2, 3);
2
3
4
5
- A:
1
2
3
- B:
{1: 1}
{2: 2}
{3: 3}
- C:
{ 1: undefined }
undefined
undefined
- D:
undefined
undefined
undefined
答案
答案:D
myFunc
期望接收一个包含 x
, y
和 z
属性的对象作为它的参数。因为我们仅仅传递三个单独的数字值 (1, 2, 3) 而不是一个含有 x
, y
和 z
属性的对象 ({x: 1, y: 2, z: 3}),x
, y
和 z
有着各自的默认值 undefined
.
# 17. 输出什么?
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));
2
3
4
5
6
7
8
- A:
true
false
true
false
- B:
true
false
false
false
- C:
false
false
true
false
- D:
false
true
false
true
答案
答案: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
.
# 18. 以下哪一项会对对象 person
有副作用?
const person = { name: "Lydia Hallie" };
Object.seal(person);
2
3
- A:
person.name = "Evan Bacon"
- B:
person.age = 21
- C:
delete person.name
- D:
Object.assign(person, { age: 21 })
答案
答案:A
使用 Object.seal
我们可以防止新属性 被添加,或者存在属性 被移除.
然而,你仍然可以对存在属性进行更改。
# 19. 输出什么?
class Bird {
constructor() {
console.log("I'm a bird. 🦢");
}
}
class Flamingo extends Bird {
constructor() {
console.log("I'm pink. 🌸");
super();
}
}
const pet = new Flamingo();
2
3
4
5
6
7
8
9
10
11
12
13
14
- 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. 🦢"
。
# 20. 哪一个选项会导致报错?
const emojis = ["🎄", "🎅🏼", "🎁", "⭐"];
/* 1 */ emojis.push("🦌");
/* 2 */ emojis.splice(0, 2);
/* 3 */ emojis = [...emojis, "🥂"];
/* 4 */ emojis.length = 0;
2
3
4
5
6
- A: 1
- B: 1 and 2
- C: 3 and 4
- D: 3
答案
答案:D
const
关键字意味着我们不能 重定义 变量中的值,它 仅可读。然而,值本身不可修改。数组 emojis
中的值可被修改,如 push 新的值,拼接,又或者将数组的长度设置为 0。
# 21. 输出什么?
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);
2
3
4
5
6
7
8
9
10
11
12
- 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 的值为默认的空对象 {}
。一个空对象是一个 truthy 值,意味着 address ? address : null
条件会返回 true
。address 的值为空对象 {}
。
# 22. 输出什么?
const user = {
email: "my@email.com",
updateEmail: (email) => {
this.email = email;
},
};
user.updateEmail("new@email.com");
console.log(user.email);
2
3
4
5
6
7
8
9
- A:
my@email.com
- B:
new@email.com
- C:
undefined
- D:
ReferenceError
答案
答案:A
updateEmail
函数是一个箭头函数,它没有和 user
对象绑定。这就意味着 this
关键字不会引用到 user
对象,但是会引用到全局对象。user
对象内部的 email
的值不会更新。当打印 user.email
的时候,原始值 my@email.com
被返回。