类型问题
-
var let 变量
function sayHi() { console.log(name); console.log(age); var name = 'Lydia'; let age = 21; } sayHi();
- A:
Lydia
andundefined
- B:
Lydia
andReferenceError
- C:
ReferenceError
and21
- D:
undefined
andReferenceError
Answer
Answer: D
在函数中,我们使用了
var
关键词来声明name
变量,这导致变量被提升(内存空间在创建阶段设置),默认值为undefined
,直到该变量被定义的那一行。而带有
let
关键字 (和const
)的变量会被提升,但和var
不同的是,它不会被初始化。在我们初始化它们之前,它们是不可访问的,这被称为“时间死区”。当我们尝试在声明变量之前访问变量时,JavaScript 会抛出
ReferenceError
。 - A:
-
对象解释
const bird = { size: 'small', }; const mouse = { name: 'Mickey', small: true, };
- A:
mouse.bird.size
is not valid - B:
mouse[bird.size]
is not valid - C:
mouse[bird["size"]]
is not valid - D: All of them are valid
Answer
Answer: A
在 JavaScript 中,所有对象键都是字符串(除非它是
Symbol
)。尽管我们可能不会将它们输入为字符串,但它们总是会在底层转换为字符串。 当使用方括号表示法时,JavaScript会进行解释(或解包)语句。它看到第一个开放的方括号[
,然后继续查找直到找到闭合的方括号]
。只有在找到闭合的方括号后,它才会评估语句。在这个例子中,
mouse[bird.size]
首先评估bird.size
,它的值是"small"
。然后,mouse["small"]
返回true
。然而,使用点表示法时,这种情况不会发生。
mouse
没有一个名为bird
的键,这意味着mouse.bird
是undefined
的。然后,我们使用点表示法来获取size
:mouse.bird.size
。由于mouse.bird
是undefined
的,实际上我们正在请求undefined.size
。这是无效的,并且会引发类似于Cannot read property "size" of undefined
。 - A:
-
引用类型赋值
let c = { greeting: 'Hey!' }; let d; d = c; c.greeting = 'Hello'; console.log(d.greeting);
- A:
Hello
- B:
Hey!
- C:
undefined
- D:
ReferenceError
- E:
TypeError
Answer
**Answer: A**
Answer: A
在JavaScript中,当将一个对象赋值给另一个对象时,它们之间是通过引用进行交互的。这意味着,当我们将一个对象赋值给另一个变量时,实际上是将该对象的引用复制到了新变量中,而不是将对象本身复制到新变量中。
在这个例子中,变量
c
保存了一个对象的值。稍后,我们将d
赋值为与c
具有相同引用的对象。这意味着,c
和d
现在都引用同一个对象,因此对该对象的任何更改都会反映在c和d中。这种交互方式可以节省内存,因为它避免了在内存中复制大量的对象。但需要注意的是,如果我们想要复制一个对象而不是引用它,我们需要使用深拷贝(deep copy)或浅拷贝(shallow copy)的技术。当你改变一个对象时,所有引用该对象的变量都会改变。
- A:
-
变量赋值
let greeting; greetign = {}; // Typo! console.log(greetign);
- A:
{}
- B:
ReferenceError: greetign is not defined
- C:
undefined
Answer
Answer: A
它记录了对象,因为我们刚刚在全局对象上创建了一个空对象!当我们将
greeting
拼写错误为greetign
时,JS解释器实际上将其视为:- 在Node.js中,
global.greetign = {}
。 - 在浏览器中,
window.greetign = {}
,frames.geetign = {}
和self.greetign
。 - 在Web Workers中,
self.greetign
。 - 在所有环境中
globalThis.greetign
。 - 为了避免这种情况,我们可以使用
"use strict"
。这可以确保在将变量设置为任何值之前已经声明了它。
- A:
-
var 关键字
var num = 8; var num = 10; console.log(num);
- A:
8
- B:
10
- C:
SyntaxError
- D:
ReferenceError
Answer
Answer: B
使用
var
关键字,您可以声明多个具有相同名称的变量。变量将保存最新的值。使用
let
或const
无法这样做,因为它们是块作用域的。 - A:
-
检测键的存在
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);
- A:
false
true
false
true
- B:
false
true
true
true
- C:
true
true
false
true
- D:
true
true
true
true
Answer
Answer: C
所有对象的键(不包括 Symbols)在底层都是字符串,即使您没有将其键入为字符串。这就是为什么
obj.hasOwnProperty('1')
也返回true
的原因。但是对于集合来说,情况并非如此。我们的集合中没有
'1'
,所有set.has('1')
返回false
。它具有数字类型1
,set.has(1)
返回true
。 - A:
-
重复的键
const obj = { a: 'one', b: 'two', a: 'three' }; console.log(obj);
- A:
{ a: "one", b: "two" }
- B:
{ b: "two", a: "three" }
- C:
{ a: "three", b: "two" }
- D:
SyntaxError
Answer
**Answer: B**
Answer: B
如果您有两个具有相同名称的键,则该键将被替换。它仍将位于其第一个位置,但具有最后指定的值。
- A:
-
原型方法
String.prototype.giveLydiaPizza = () => { return 'Just give Lydia pizza already!'; }; const name = 'Lydia'; console.log(name.giveLydiaPizza())
- A:
"Just give Lydia pizza already!"
- B:
TypeError: not a function
- C:
SyntaxError
- D:
undefined
Answer
Answer: A
String
是一个内置的构造函数,我们可以向其添加属性。我刚刚向其原型添加了一个方法。原始字符串会自动转换为由字符串原型函数生成的字符串对象。因此,所有字符串(字符串对象)都可以访问该方法!这意味着,如果您向字符串原型添加一个方法,那么所有字符串都可以使用该方法。 - A:
-
将对象作为键
const a = {}; const b = { key: 'b' }; const c = { key: 'c' }; a[b] = 123; a[c] = 456; console.log(a[b]);
- A:
123
- B:
456
- C:
undefined
- D:
ReferenceError
Answer
Answer: B
这段代码中,我们尝试将一个对象作为键设置到对象
a
中,并将其值设置为123
。然而,当我们将一个对象转换为字符串时,它会变成"[object Object]"
。因此,我们实际上是在说a["[object Object]"] = 123
。然后,我们尝试再次做同样的事情。c是另一个我们隐式转换为字符串的对象。因此,a["[object Object]"] = 456
。然后,我们记录a[b]
,实际上是a["[object Object]"]
。我们刚刚将其设置为456
,因此它返回456
。这段代码中的关键点是对象键自动转换为字符串,因此需要小心处理对象键。
- A:
-
类型判断再判断
console.log(typeof typeof 1);
- A:
"number"
- B:
"string"
- C:
"object"
- D:
"undefined"
Answer
Answer: B
typeof 1
返回"number"
.typeof "number"
返回"string"
- A:
-
数组定义
const numbers = [1, 2, 3]; numbers[10] = 11; console.log(numbers);
- A:
[1, 2, 3, null x 7, 11]
- B:
[1, 2, 3, 11]
- C:
[1, 2, 3, empty x 7, 11]
- D:
SyntaxError
Answer
Answer: C
当你给数组中的一个元素设置一个超过数组长度的值时,JavaScript会创建一些被称为"empty slots"(空槽)的东西。实际上,它们的值是undefined,但你会看到类似于以下的结果:
[1, 2, 3, 空 x 7, 11]
具体的显示方式取决于你运行代码的环境(不同浏览器、Node等)。
- A:
-
类型判断
!!null; !!''; !!1;
- A:
false
true
false
- B:
false
false
true
- C:
false
true
true
- D:
true
true
false
Answer
Answer: B
null
是假值。!null
返回true
。!true
返回false
。""
是假值。!""
返回true
。!true
返回false
。1
是真值。!1
返回false
。!false
返回true
。 - A:
-
对象的引用
let person = { name: 'Lydia' }; const members = [person]; person = null; console.log(members);
- A:
null
- B:
[null]
- C:
[{}]
- D:
[{ name: "Lydia" }]
Answer
Answer: D
首先,我们声明一个变量
person
,并赋值为一个具有name
属性的对象。然后,我们声明一个名为
members
的变量。我们将该数组的第一个元素设置为person
变量的值。当将对象相互赋值时,它们通过引用进行交互。当将一个变量的引用赋给另一个变量时,会复制该引用。(请注意,它们不具有相同的引用!)然后,我们将变量
person
的值设置为null
。我们只修改了
person
变量的值,而没有修改数组中的第一个元素,因为该元素具有与对象的不同(已复制的)引用。members数组中的第一个元素仍然保留对原始对象的引用。当我们记录(log)members数组时,第一个元素仍然保留着对象的值,并将其记录下来。 - A:
-
对象循环
const person = { name: 'Lydia', age: 21, }; for (const item in person) { console.log(item); }
- A:
{ name: "Lydia" }, { age: 21 }
- B:
"name", "age"
- C:
"Lydia", 21
- D:
["name", "Lydia"], ["age", 21]
Answer
Answer: B
使用
for-in
循环,我们可以迭代对象的键(在这种情况下为name和age)。在底层,对象的键是字符串(如果它们不是Symbol类型)。在每次循环中,我们将item的值设置为当前迭代的键。首先,item等于name,并进行记录。然后,item等于age,并进行记录。 - A:
-
箭头函数和类型判断
(() => { let x = (y = 10); })(); console.log(typeof x); console.log(typeof y);
- A:
"undefined", "number"
- B:
"number", "number"
- C:
"object", "number"
- D:
"number", "undefined"
Answer
Answer: A
let x = (y = 10);
实际上是以下代码的简写形式:y = 10; let x = y;
当我们将
y
设置为10
时,实际上是向全局对象添加了一个属性y
(在浏览器中是window
对象,在Node中是global
对象)。在浏览器中,window.y
现在等于10
。然后,我们声明一个变量
x
,其值为y
,即10
。使用let
关键字声明的变量是块级作用域的,它们只在声明它们的块中定义;在这种情况下是立即调用的函数表达式(IIFE)。当我们使用typeof
运算符时,操作数x
未定义:我们试图在声明它的块之外访问x
。这意味着x
未定义。尚未被赋值或声明的值的类型是"undefined"
。console.log(typeof x)
返回"undefined"
。然而,当我们将
y
设置为10
时,我们创建了一个全局变量y
。这个值可以在我们的代码的任何地方访问。y
已经定义,并且具有"number"
类型的值。console.log(typeof y)
返回"number"
。 - A:
-
数组赋值
const numbers = [1, 2, 3, 4, 5]; const [y] = numbers; console.log(y);
- A:
[[1, 2, 3, 4, 5]]
- B:
[1, 2, 3, 4, 5]
- C:
1
- D:
[1]
Answer
Answer: C
我们可以通过解构来从数组中解包值或从对象中解包属性。例如:
[a, b] = [1, 2];
现在
a
的值是1
,b
的值是2
。在问题中实际上我们做的是:[y] = [1, 2, 3, 4, 5];
这意味着
y
的值等于数组中的第一个值,即数字1
。当我们记录y
时,返回的是1
。 - A:
-
类型比较
console.log(Number(2) === Number(2)) console.log(Boolean(false) === Boolean(false)) console.log(Symbol('foo') === Symbol('foo'))
- A:
true
,true
,false
- B:
false
,true
,false
- C:
true
,false
,true
- D:
true
,true
,true
Answer
Answer: A
每个
Symbol
都是完全唯一的。传递给Symbol
的参数只是给Symbol
的一个描述。Symbol
的值不依赖于传递的参数。 当我们测试相等时,我们创建了两个全新的符号:第一个Symbol('foo')
,第二个Symbol('foo')
,这两个值是唯一的,彼此不相等,因此返回false
。 - A:
-
对象解构
const { name: myName } = { name: "Lydia" }; console.log(name);
- A:
"Lydia"
- B:
"myName"
- C:
undefined
- D:
ReferenceError
Answer
Answer: D
当我们从右侧的对象解构属性
name
时,我们将其值Lydia
分配给名为myName
的变量。使用
{name:myName}
,我们是在告诉 JavaScript 我们要创建一个名为myName
的新变量,并且其值是右侧对象的name
属性的值。当我们尝试打印
name
,一个未定义的变量时,就会引发ReferenceError
。 - A:
-
对象引用
const person = { name: "Lydia", age: 21 } let city = person.city city = "Amsterdam" console.log(person)
- A:
{ name: "Lydia", age: 21 }
- B:
{ name: "Lydia", age: 21, city: "Amsterdam" }
- C:
{ name: "Lydia", age: 21, city: undefined }
- D:
"Amsterdam"
Answer
Answer: A
我们将变量
city
设置为等于person
对象上名为city
的属性的值。 这个对象上没有名为city
的属性,因此变量city
的值为undefined
。请注意,我们没有引用
person
对象本身,只是将变量city
设置为等于person
对象上city
属性的当前值。然后,我们将
city
设置为等于字符串“Amsterdam”
。 这不会更改 person 对象:没有对该对象的引用。因此打印
person
对象时,会返回未修改的对象。 - A:
-
Symbol类型
const info = { [Symbol('a')]: 'b' } console.log(info) console.log(Object.keys(info))
- A:
{Symbol('a'): 'b'}
and["{Symbol('a')"]
- B:
{}
and[]
- C:
{ a: "b" }
and["a"]
- D:
{Symbol('a'): 'b'}
and[]
Answer
Answer: D
Symbol
类型是不可枚举的。Object.keys
方法返回对象上的所有可枚举的键属性。Symbol
类型是不可见的,并返回一个空数组。 记录整个对象时,所有属性都是可见的,甚至是不可枚举的属性。这是
Symbol
的众多特性之一:除了表示完全唯一的值(防止对象意外名称冲突,例如当使用 2 个想要向同一对象添加属性的库时),您还可以隐藏这种方式对象的属性(尽管不完全。你仍然可以使用Object.getOwnPropertySymbols()
方法访问Symbol
。 - A:
-
错误类型
const name = "Lydia" console.log(name())
- A:
SyntaxError
- B:
ReferenceError
- C:
TypeError
- D:
undefined
Answer
Answer: C
变量
name
保存字符串的值,该字符串不是函数,因此无法调用。当值不是预期类型时,会抛出
TypeErrors
。 JavaScript 期望name
是一个函数,因为我们试图调用它。 但它是一个字符串,因此抛出TypeError
:name is not a function
当你编写了一些非有效的 JavaScript 时,会抛出语法错误,例如当你把
return
这个词写成retrun
时。 当 JavaScript 无法找到您尝试访问的值的引用时,抛出ReferenceErrors
。 - A:
-
或运算符
const one = (false || {} || null) const two = (null || false || "") const three = ([] || 0 || true) console.log(one, two, three)
- A:
false
null
[]
- B:
null
""
true
- C:
{}
""
[]
- D:
null
null
true
Answer
Answer: C
使用
||
运算符,我们可以返回第一个真值。 如果所有值都是假值,则返回最后一个值。(false || {} || null)
:空对象{}
是一个真值。 这是第一个(也是唯一的)真值,它将被返回。one
等于{}
。(null || false ||"")
:所有值都是假值。 这意味着返回传递的值""
。two
等于""
。([] || 0 ||"")
:空数组[]
是一个真值。 这是第一个返回的真值。three
等于[]
。 - A:
-
与运算符
// 🎉✨ This is my 100th question! ✨🎉 const output = `${[] && 'Im'}possible! You should${'' && `n't`} see a therapist after so much JavaScript lol`
- A:
possible! You should see a therapist after so much JavaScript lol
- B:
Impossible! You should see a therapist after so much JavaScript lol
- C:
possible! You shouldn't see a therapist after so much JavaScript lol
- D:
Impossible! You shouldn't see a therapist after so much JavaScript lol
Answer
Answer: B
[]
是一个真值。 使用&&
运算符,如果左侧值是真值,则返回右侧值。 在这种情况下,左侧值[]
是一个真值,所以返回Im
。""
是一个假值。 如果左侧值是假的,则不返回任何内容。n't
不会被退回。 - A:
-
变量提升和函数作用域
var x = 1; function foo() { x = 10; return; function x() {} } foo(); console.log(x);
Answer
函数声明的提升优先级高于变量声明。如果在同一个作用域内,函数声明和变量声明的名字相同,那么函数声明会覆盖变量声明。
但是此处
var x
和function foo()
是在全局作用域中,而function x()
是在foo
函数的局部作用域中。所以,function foo()
和var x
的提升是在全局作用域中进行的,而function x()
的提升是在foo
函数的局部作用域中进行的。所以提升的顺序如下:
var x = undefined; function foo() { function x() {} x = 10; return; } x = 1; foo(); console.log(x);
当 JavaScript 引擎执行到
x = 10
这一行时,它实际上是修改了foo
函数作用域内的x
,将其从一个函数改变为了一个数值10
。但是,这个改变并不影响外部的变量
x
,因此,当执行console.log(x)
时,输出的仍然是1
,这是因为外部的变量x
并没有被改变。 -
函数内部变量提升
var name = "Lokesh Prajapati"; (function () { console.log(name); var name = "foo"; })();
Answer
在 JavaScript 中,函数会创建自己的作用域,也被称为局部作用域。在函数内部声明的变量(包括参数)只在该函数内部可见,这被称为变量的局部性。这意味着在函数内部可以使用与外部全局变量同名的变量,而不会影响到全局变量。
变量和函数声明会被提升到它们所在的作用域的顶部
var name
在函数作用域内被提升,所以在函数内部的console.log(name)
执行时,name
已经被声明,但还没有被赋值,因此其值为undefined
。 -
函数提升、函数声明、变量声明顺序
function sayHelloWorld() { return sayGoodbyWorld(); var sayGoodbyWorld = function () { return "Hello, World!"; }; function sayGoodbyWorld() { return "Goodbye, World!"; } } console.log(sayHelloWorld());
Answer
函数声明和变量声明都被提升到函数作用域的顶部,但是变量的赋值并没有被提升,所以函数声明会覆盖变量声明。
所以最后返回的是函数
sayGoodbyWorld()
,输出"Goodbye, World!"
-
类型转换,真值和假值
console.log([] == ![]);
Answer
在 JavaScript 中,
==
运算符在比较两个值时,如果它们的类型不同,会进行类型转换。首先,
![]
的结果是false
,因为在 JavaScript 中,空数组是一个真值(truthy value),对其进行逻辑非操作得到false
。然后,
[] == false
的结果是true
。这是因为在进行比较时,两边的值都会被转换为数字。空数组[]
被转换为数字得到0
,false
被转换为数字也得到0
,所以0 == 0
的结果是true
。在 JavaScript 中,以下值被认为是假值(falsy):
false
0
和NaN
""
(空字符串)null
undefined
BigInt(0)
(仅在支持BigInt
的环境中)
-
s
-
是
类的问题
-
类中方法调用
class Chameleon { static colorChange(newColor) { this.newColor = newColor; return this.newColor; } constructor({ newColor = 'green' } = {}) { this.newColor = newColor; } } const freddie = new Chameleon({ newColor: 'purple' }); console.log(freddie.colorChange('orange'));
- A:
orange
- B:
purple
- C:
green
- D:
TypeError
Answer
Answer: D
colorChange
函数是一个静态方法,它只能存在于创建它的构造函数中,不能被传递给任何子类或在类实例上调用。因此,由于freddie
是Chameleon类的一个实例,不能在它上面调用colorChange函数,会抛出TypeError
错误。 - A:
-
原型添加方法与删除
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();
- A:
"Woof I am Mara"
,TypeError
- A:
-
B:
"Woof I am Mara"
,"Woof I am Mara"
- C:
"Woof I am Mara"
,undefined
- D:
TypeError
,TypeError
Answer
- C:
**Answer: A**
Answer: A
我们可以使用delete
关键字从对象中删除属性,也可以从原型上删除属性。通过从原型上删除属性,该属性将不再在原型链中可用。在这种情况下,在删除了Dog.prototype.bark
之后,bark
函数在原型上不再可用,但我们仍然尝试访问它。
当我们试图调用不是函数的东西时,会抛出TypeError
错误。在这种情况下,抛出的错误是TypeError: pet.bark is not a function
,因为pet.bark
是undefined
。
-
使用哪个构造函数可以成功继承
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; } };
- A: 1
- B: 2
- C: 3
- D: 4
Answer
Answer: B
在子类中,在调用
super
之前不能访问到this
关键字。 如果这样做,它将抛出一个ReferenceError
:1 和 4 将引发一个引用错误。使用
super
关键字,需要用给定的参数来调用父类的构造函数。 父类的构造函数接收name
参数,因此我们需要将name
传递给super
。Labrador
类接收两个参数,name
参数是由于它继承了Dog
,size
作为Labrador
类的额外属性,它们都需要传递给Labrador
的构造函数,因此使用构造函数 2 正确完成。 -
实例对象
class Person { constructor(name) { this.name = name } } const member = new Person("John") console.log(typeof member)
- A:
"class"
- B:
"function"
- C:
"object"
- D:
"string"
Answer
Answer: C
类是构造函数的语法糖,如果用构造函数的方式来重写
Person
类则将是:function Person() { this.name = name }
通过
new
来调用构造函数,将会生成构造函数Person
的实例,对实例执行typeof
关键字将返回"object"
,上述情况打印出"object"
。 - A:
-
类的设置
class Person { constructor() { this.name = "Lydia" } } Person = class AnotherPerson { constructor() { this.name = "Sarah" } } const member = new Person() console.log(member.name)
- A:
"Lydia"
- B:
"Sarah"
- C:
Error: cannot redeclare Person
- D:
SyntaxError
Answer
**Answer: B**
Answer: B
我们可以将类设置为等于其他类/函数构造函数。 在这种情况下,我们将
Person
设置为AnotherPerson
。 这个构造函数的名字是Sarah
,所以新的Person
实例member
上的 name 属性是Sarah
。 - A:
-
原型和继承
function Parent() {} function Child() {} Child.prototype = new Parent(); var obj = new Child(); console.log(obj instanceof Parent);
Answer
在 JavaScript 中,
instanceof
运算符用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上。Child.prototype = new Parent();
将Child
的原型设置为Parent
的一个实例。这意味着Child
的原型链上包含了Parent
的原型。然后,
var obj = new Child();
创建了一个Child
的实例obj
。由于Child
的原型链上包含了Parent
的原型,所以obj
的原型链上也包含了Parent
的原型。所以,当执行
console.log(obj instanceof Parent);
时,检测obj
的原型链上是否包含Parent
的原型,结果为true
,所以输出的是true
。 -
基于原型的继承和函数引用相等性
function Person(name) { this.name = name; } Person.prototype.greet = function() { return "Hello, my name is " + this.name; }; var person1 = new Person("张三"); var person2 = new Person("李四"); console.log(person1.greet === person2.greet);
Answer
在 JavaScript 中,当我们创建一个新的对象通过一个构造函数,这个对象会继承构造函数的
prototype
属性。所有的实例对象共享它们的构造函数的prototype
。这意味着它们可以访问相同的方法,这些方法是定义在构造函数的prototype
上的。在你的代码中,
Person.prototype.greet
是一个方法,所有的Person
实例都可以访问这个方法。所以,person1.greet
和person2.greet
实际上指向的是同一个函数。尽管
person1
和person2
的name
属性是不同的,但他们的greet
方法是相同的。如果你想比较
person1.greet()
和person2.greet()
的返回值是否相同,你可以直接比较他们的返回值。例如:console.log(person1.greet() === person2.greet());
-
上的
-
是
函数问题
-
循环变量
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); } for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); }
- A:
0 1 2
and0 1 2
- B:
0 1 2
and3 3 3
- C:
3 3 3
and0 1 2
Answer
Answer: C
在JavaScript中,由于事件队列的存在,
setTimeout
回调函数在循环执行后才被调用。在第一个循环中,变量i
使用var
关键字声明,因此该值是全局的。在循环期间,我们每次使用一元运算符++
将i
的值增加1
。因此,在循环中使用var
声明变量时,每次循环都会覆盖上一次循环中的变量值,最终导致所有的变量都共享同一个值。当setTimeout
回调函数被调用时,第一个示例中的i等于3。在第二个循环中,变量
i
使用let
关键字声明:使用let
(和const
)关键字声明的变量是块作用域的(块是{ }
之间的任何内容)。在每次迭代期间,i将具有新值,并且每个值都在循环内部作用域。 - A:
-
函数属性
function bark() { console.log('Woof!'); } bark.animal = 'dog';
- A: 无事发生,这种写法完全正确。
- B:
SyntaxError
。你不能以这种方式向函数添加属性。 - C: 输出的结果是字符串
"Woof"
。 - D:
ReferenceError
。
Answer
**Answer: A**
Answer: A
这在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());
- A:
TypeError
- B:
SyntaxError
- C:
Lydia Hallie
- D:
undefined
undefined
Answer
Answer: A
在JavaScript中,函数是对象。因此,方法
getFullName
被添加到构造函数对象本身中。因此,我们可以调用Person.getFullName()
,但是member.getFullName
会抛出TypeError
错误。如果你想让一个方法对所有对象实例都可用,你必须将它添加到原型属性中(这是因为原型属性是所有对象实例共享的):Person.prototype.getFullName = function() { return `${this.firstName} ${this.lastName}`; };
- A:
-
函数作用域
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);
- A:
Person {firstName: "Lydia", lastName: "Hallie"}
andundefined
- B:
Person {firstName: "Lydia", lastName: "Hallie"}
andPerson {firstName: "Sarah", lastName: "Smith"}
- C:
Person {firstName: "Lydia", lastName: "Hallie"}
and{}
- D:
Person {firstName: "Lydia", lastName: "Hallie"}
andReferenceError
Answer
Answer: A
对于
sarah
,我们没有使用new
关键字。当使用new
时,this
指的是我们创建的新空对象。然而,如果你不加new
,this
指的是全局对象!我们说
this.firstName
等于"Sarah"
,this.lastName
等于"Smith"
。实际上,我们定义了global.firstName='Sarah'
和global.lastName='Smith'
。sarah
本身是undefined
,因为我们没有从Person
函数中返回一个值。在JavaScript中,如果我们想要创建一个正确的对象,我们可以使用
return
语句或者使用new
关键字。如果我们使用return
语句,我们可以在函数内部返回一个新的对象,这个对象将成为函数的返回值。如果我们使用new
关键字,我们可以创建一个新的空对象,并将它作为函数内部的this
关键字的值。然后,我们可以在函数内部使用this
关键字来设置新对象的属性和方法。最后,函数将返回这个新对象。无论是使用return
语句还是使用new
关键字,我们都可以创建一个正确的对象。 - A:
-
函数参数
function getAge(...args) { console.log(typeof args); } getAge(21);
- A:
"number"
- B:
"array"
- C:
"object"
- D:
"NaN"
Answer
Answer: C
rest参数(…args)允许我们将所有剩余的参数“收集”到一个数组中。数组是一个对象,因此typeof args返回"object"。这意味着我们可以使用数组的方法来操作这些参数。例如,我们可以使用args.length来获取传递的参数的数量,或者使用args.forEach来遍历这些参数。使用rest参数可以使函数更加灵活,因为它允许我们接受任意数量的参数。
- A:
-
严格模式
function getAge() { 'use strict'; age = 21; console.log(age); } getAge();
- A:
21
- B:
undefined
- C:
ReferenceError
- D:
TypeError
Answer
**Answer: C**
Answer: C
使用"use strict"可以确保不会意外地声明全局变量。在上面的代码中,我们没有声明变量age,但由于我们使用了"use strict",它会抛出一个引用错误。如果我们没有使用"use strict",它仍然可以工作,因为属性age会被添加到全局对象中。使用"use strict"可以帮助我们避免一些常见的错误,例如意外地声明全局变量或使用未声明的变量。它还可以使代码更加严格,从而提高代码的可读性和可维护性。
- A:
-
异常捕获
(() => { let x, y; try { throw new Error(); } catch (x) { (x = 1), (y = 2); console.log(x); } console.log(x); console.log(y); })();
- A:
1
undefined
2
- B:
undefined
undefined
undefined
- C:
1
1
2
- D:
1
undefined
undefined
Answer
Answer: A
catch块接收参数x。这个x与我们传递参数时的变量不同。这个变量x是块级作用域的。
稍后,我们将这个块级作用域变量设置为1,并设置变量y的值。现在,我们记录块级作用域变量x的值,它等于1。
在catch块之外,x仍然是undefined,而y是2。当我们在catch块之外使用console.log(x)时,它返回undefined,而y返回2。
- A:
-
拓展运算符
[...'Lydia'];
- A:
["L", "y", "d", "i", "a"]
- B:
["Lydia"]
- C:
[[], "Lydia"]
- D:
[["L", "y", "d", "i", "a"]]
Answer
Answer: A
字符串是可迭代的。展开运算符将可迭代对象的每个字符映射到一个元素。
- A:
-
生成器函数
function* generator(i) { yield i; yield i * 2; } const gen = generator(10); console.log(gen.next().value); console.log(gen.next().value);
- A:
[0, 10], [10, 20]
- B:
20, 20
- C:
10, 20
- D:
0, 10 and 10, 20
Answer
Answer: C
常规函数在调用后无法在执行过程中被中止。然而,生成器函数可以在执行过程中被"停止",并且稍后可以从停止的地方继续执行。每当生成器函数遇到一个
yield
关键字时,函数会生成 yield 关键字后指定的值。需要注意的是,在这种情况下,生成器函数不返回该值,而是生成该值。首先,我们使用
i
等于10
初始化生成器函数。我们使用next()
方法调用生成器函数。第一次调用生成器函数时,i
等于10
。它遇到第一个yield
关键字:生成yield
后面指定的i
的值。生成器现在被"暂停",并且输出10
。然后,我们再次使用
next()
方法调用函数。它从上次停止的地方开始继续执行,仍然将i
设为10
。现在,它遇到下一个yield
关键字,并生成i * 2
的值。i
等于10
,所以返回10 * 2
,即20
。这样就得到了10, 20
的输出。 - A:
-
Promise 方法
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));
- A:
"one"
- B:
"two"
- C:
"two" "one"
- D:
"one" "two"
Answer
**Answer: B**
Answer: B
当我们将多个 Promise 传递给
Promise.race
方法时,它会解决(resolve)或拒绝(reject)第一个解决或拒绝的 Promise。对于setTimeout
方法,我们传递了两个定时器:第一个 Promise(firstPromise
)设置了 500 毫秒的定时器,而第二个 Promise(secondPromise
)设置了 100 毫秒的定时器。这意味着第二个 Promise(secondPromise
)会先解决,并带有值'two'
。此时,变量res
持有值'two'
,并被输出。 - A:
-
参数传递
function getInfo(member, year) { member.name = 'Lydia'; year = '1998'; } const person = { name: 'Sarah' }; const birthYear = '1997'; getInfo(person, birthYear); console.log(person, birthYear);
- A:
{ name: "Lydia" }, "1997"
- B:
{ name: "Sarah" }, "1998"
- C:
{ name: "Lydia" }, "1998"
- D:
{ name: "Sarah" }, "1997"
Answer
Answer: A
参数按值传递,除非其值是一个对象,那么它们将按引用传递。
birthYear
是一个字符串,而不是一个对象,所以它是按值传递的。当我们按值传递参数时,会创建该值的副本(参见问题46)。变量
birthYear
引用的是值"1997"
。参数year
也引用了值"1997"
,但它引用的值与birthYear
引用的不是同一个。当我们通过将year
设置为"1998"
来更新year
的值时,只是更新了year
的值,birthYear
仍然等于"1997"
。person
的值是一个对象。参数member
复制了对同一对象的引用。当我们修改member
引用的对象的属性时,person
的值也会被修改,因为它们都引用同一个对象。person
的name
属性现在等于值"Lydia"
。 - A:
-
自定义异常
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();
- A:
It worked! Hello world!
- B:
Oh no an error: undefined
- C:
SyntaxError: can only throw Error objects
- D:
Oh no an error: Hello world!
Answer
Answer: D
使用
throw
语句,我们可以创建自定义错误。通过该语句,您可以抛出异常。异常可以是字符串、数字、布尔值或对象。在这种情况下,我们的异常是字符串'Hello world!'
。使用
catch
语句,我们可以指定在try
块中抛出异常时要执行的操作。一个异常被抛出:字符串'Hello world!'
。现在,变量e
的值等于该字符串,我们将其记录下来。结果是'Oh an error: Hello world!'
。 - A:
-
默认参数和解构赋值
const value = { number: 10 }; const multiply = (x = { ...value }) => { console.log((x.number *= 2)); }; multiply(); multiply(); multiply(value); multiply(value);
- A:
20
,40
,80
,160
- B:
20
,40
,20
,40
- C:
20
,20
,20
,40
- D:
NaN
,NaN
,20
,40
Answer
**Answer: C** **Answer: C**
在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
。 - A:
-
生成器函数
function* startGame() { const 答案 = yield "Do you love JavaScript?"; if (答案 !== "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 ❤️
- A:
game.next("Yes").value
andgame.next().value
- B:
game.next.value("Yes")
andgame.next.value()
- C:
game.next().value
andgame.next("Yes").value
- D:
game.next.value()
andgame.next.value("Yes")
Answer
Answer: C
generator
函数在遇到yield
关键字时会 “暂停” 其执行。 首先,我们需要让函数产生字符串Do you love JavaScript?
,这可以通过调用game.next().value
来完成。上述函数的第一行就有一个yield
关键字,那么运行立即停止了,yield
表达式本身没有返回值,或者说总是返回undefined
,这意味着此时变量答案
为undefined
。next
方法可以带一个参数,该参数会被当作上一个yield
表达式的返回值。当我们调用game.next("Yes").value
时,先前的yield
的返回值将被替换为传递给next()
函数的参数"Yes"
。此时变量答案
被赋值为"Yes"
,if
语句返回false
,所以JavaScript loves you back ❤️
被打印。 - A:
-
Promise打印
async function getData() { return await Promise.resolve("I made it!"); } const data = getData(); console.log(data);
- A:
"I made it!"
- B:
Promise {<resolved>: "I made it!"}
- C:
Promise {<pending>}
- D:
undefined
Answer
Answer: C
异步函数始终返回一个 promise。
await
仍然需要等待 promise 的解决:当我们调用getData()
并将其赋值给data
,此时data
为getData
方法返回的一个挂起的 promise,该 promise 并没有解决。如果我们想要访问已解决的值
"I made it!"
,可以在data
上使用.then()
方法:data.then(res => console.log(res))
这样将打印
"I made it!"
- A:
-
记忆函数
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));
- A:
Calculated! 20
Calculated! 20
Calculated! 20
- B:
Calculated! 20
From cache! 20
Calculated! 20
- C:
Calculated! 20
From cache! 20
From cache! 20
- D:
Calculated! 20
From cache! 20
Error
Answer
Answer: C
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
被打印。 - A:
-
函数默认参数
function sayHi(name) { return `Hi there, ${name}` } console.log(sayHi())
- A:
Hi there,
- B:
Hi there, undefined
- C:
Hi there, null
- D:
ReferenceError
Answer
Answer: B
默认情况下,如果不给函数传参,参数的值将为
undefined
。 上述情况,我们没有给参数name
传值。name
等于undefined
,并被打印。在 ES6 中,我们可以使用默认参数覆盖此默认的
undefined
值。 例如:function sayHi(name = 'Lydia'){...}
在这种情况下,如果我们没有传递值或者如果我们传递
undefined
,name
总是等于字符串Lydia
- A:
-
块级作用域
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))
- A:
"Sorry, you're too young."
- B:
"Yay! You're old enough!"
- C:
ReferenceError
- D:
undefined
Answer
Answer: C
const
和let
声明的变量是具有块级作用域的,块是大括号({}
)之间的任何东西,即上述情况if / else
语句的花括号。 由于块级作用域,我们无法在声明的块之外引用变量,因此抛出ReferenceError
。 - A:
-
then的调用
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
Answer
**Answer: B**
Answer: B
第二个
.then
中res
的值等于前一个.then
中的回调函数返回的值。 你可以像这样继续链接.then
,将值传递给下一个处理程序。 - A:
-
默认参数的位置
function sum(num1, num2 = num1) { console.log(num1 + num2) } sum(10)
- A:
NaN
- B:
20
- C:
ReferenceError
- D:
undefined
Answer
Answer: B
您可以将默认参数的值设置为函数的另一个参数,只要另一个参数定义在其之前即可。 我们将值
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
- A:
-
箭头函数的原型
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)
- A:
{ constructor: ...}
{ constructor: ...}
- B:
{}
{ constructor: ...}
- C:
{ constructor: ...}
{}
- D:
{ constructor: ...}
undefined
Answer
Answer: D
常规函数,例如
giveLydiaPizza
函数,有一个prototype
属性,它是一个带有constructor
属性的对象(原型对象)。 然而,箭头函数,例如giveLydiaChocolate
函数,没有这个prototype
属性。 尝试使用giveLydiaChocolate.prototype
访问prototype
属性时会返回undefined
。 - A:
-
默认参数位置
function getItems(fruitList, ...args, favoriteFruit) { return [...fruitList, ...args, favoriteFruit] } getItems(["banana", "apple"], "pear", "orange")
- A:
["banana", "apple", "pear", "orange"]
- B:
[["banana", "apple"], "pear", "orange"]
- C:
["banana", "apple", ["pear"], "orange"]
- D:
SyntaxError
Answer
Answer: D
... args
是剩余参数,剩余参数的值是一个包含所有剩余参数的数组,并且只能作为最后一个参数。上述示例中,剩余参数是第二个参数,这是不可能的,并会抛出语法错误。function getItems(fruitList, favoriteFruit, ...args) { return [...fruitList, ...args, favoriteFruit] } getItems(["banana", "apple"], "pear", "orange")
上述例子是有效的,将会返回数组:
[ 'banana', 'apple', 'orange', 'pear' ]
- A:
-
语句分号
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:
a is bigger
,6
andb is bigger
,3
- B:
a is bigger
,undefined
andb is bigger
,undefined
- C:
undefined
andundefined
- D:
SyntaxError
Answer
Answer: B
在 JavaScript 中,我们不必显式地编写分号 (
;
),但是 JavaScript 引擎仍然在语句之后自动添加分号。这称为自动分号插入。例如,一个语句可以是变量,或者像throw
、return
、break
这样的关键字。在这里,我们在新的一行上写了一个
return
语句和另一个值a + b
。然而,由于它是一个新行,引擎并不知道它实际上是我们想要返回的值。相反,它会在return
后面自动添加分号。你可以这样看:return; a + b
这意味着永远不会到达
a + b
,因为函数在return
关键字之后停止运行。如果没有返回值,就像这里,函数返回undefined
。注意,在if/else
语句之后没有自动插入! - A:
-
箭头函数的返回值
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))
- 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 }
Answer
Answer: 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 })
- A:
-
作用域及闭包
var callbacks = [] for (var i = 0; i < 4; i++) { callbacks.push(function() { console.log(i); }); } callbacks.forEach(cb => cb());
- A:0, 1, 2, 3
- B:3, 3, 3, 3
- C:4, 4, 4, 4
- D:0, 0, 0, 0
Answer
Answer: C
所有的函数都是在同一个作用域中创建的,所以它们共享了同一个
i
变量。当这些函数被创建时,它们并没有保存i
的当前值,而是保存了对i
变量的引用。所以,当这些函数在forEach
循环中被调用时,它们都会打印出i
的当前值,也就是for
循环结束后的值4
。使用
let
关键字代替var
关键字来声明i
变量。let
关键字会在每次循环中创建一个新的i
变量,所以每个函数都会保存它被创建时的i
值。 -
闭包的特性
var x = 5; function outer() { var x = 10; function inner() { console.log(x); } return inner; } var finalResult = outer(); finalResult();
Answer
由于 JavaScript 的作用域规则,这里的
x
指的是outer
函数内部的x
,即10
。最后,outer
函数返回了inner
函数。即使
outer
函数已经执行完毕,inner
函数仍然可以访问outer
函数的作用域,这就是闭包的特性。在 JavaScript 中,只要闭包函数(在这个例子中是
inner
函数)还存在,它就可以访问其定义时的外部作用域(在这个例子中是outer
函数的作用域)。只有当闭包函数被垃圾回收时,它才不能访问其定义时的外部作用域。垃圾回收通常发生在以下情况:
- 闭包函数不再被引用。在这个例子中,如果
finalResult
被赋值为其他值或者finalResult
本身被删除,那么inner
函数就不再被引用,它和outer
函数的作用域都会被垃圾回收。 - 闭包函数的外部作用域不再被引用。在这个例子中,
outer
函数的作用域只被inner
函数引用,所以只有inner
函数被垃圾回收,outer
函数的作用域才会被垃圾回收。
请注意,垃圾回收的具体时间取决于 JavaScript 引擎的垃圾回收策略,我们不能精确控制它。
在 JavaScript 中,垃圾回收主要发生在以下情况:
- 局部变量:当函数执行完毕后,函数内部的局部变量通常会被垃圾回收。但如果这些变量被闭包引用,那么它们就不会被垃圾回收,直到闭包也不再被引用。
- 无引用的对象:如果一个对象没有被任何变量或者属性引用,那么这个对象就会被垃圾回收。
- 循环引用:如果一组对象互相引用,但整个组都不再被外部引用,那么这组对象就会被垃圾回收。
- DOM 元素:如果一个 DOM 元素被删除或者不再被引用,那么这个 DOM 元素就会被垃圾回收。
- 闭包函数不再被引用。在这个例子中,如果
-
函数返回值
function userData() { return userData; } console.log(typeof userData());
Answer
userData
函数返回它自己(函数引用)。因此,调用
typeof userData()
的结果将是'function'
。因为typeof
对除function
之外的引用数据类型均返回object
。 -
是
-
都是
this作用域
-
箭头函数的作用域
const shape = { radius: 10, diameter() { return this.radius * 2; }, perimeter: () => 2 * Math.PI * this.radius, }; console.log(shape.diameter()); console.log(shape.perimeter());
- A:
20
and62.83185307179586
- B:
20
andNaN
- C:
20
and63
- D:
NaN
and63
Answer
Answer: B
请注意,
diameter
的值是一个正则函数,而perimeter
的值是一个箭头函数。对于箭头函数,this
关键字指的是它当前周围的作用域,这与常规函数不同!这意味着当我们调用perimeter
时,它不是指形状对象,而是指它周围的范围(例如 window对象)。
该对象没有值radius
,返回NaN
。 - A:
-
绑定
this
作用域const person = { name: 'Lydia' }; function sayHi(age) { return `${this.name} is ${age}`; } console.log(sayHi.call(person, 21)); console.log(sayHi.bind(person, 21));
- A:
undefined is 21
Lydia is 21
- B:
function
function
- C:
Lydia is 21
Lydia is 21
- D:
Lydia is 21
function
Answer
Answer: D
我们可以使用
.call
和.bind
方法来指定函数中的this
关键字所引用的对象。但是,.call
方法会立即执行函数,而.bind
方法则会返回一个新的函数,该函数的上下文已经绑定好了,但不会立即执行。 - A:
-
函数返回
function Car() { this.make = 'Lamborghini'; return { make: 'Maserati' }; } const myCar = new Car(); console.log(myCar.make);
- A:
"Lamborghini"
- B:
"Maserati"
- C:
ReferenceError
- D:
TypeError
Answer
Answer: B
当您返回一个属性时,该属性的值等于返回的值,而不是构造函数中设置的值。我们返回字符串
"Maserati"
,所以myCar.make
等于"Maserati"
。 - A:
-
this指向与改变
var status = "😎" setTimeout(() => { const status = "😍" const data = { status: "🥑", getStatus() { return this.status } } console.log(data.getStatus()) console.log(data.getStatus.call(this)) }, 0)
- A:
"🥑"
and"😍"
- B:
"🥑"
and"😎"
- C:
"😍"
and"😎"
- D:
"😎"
and"😎"
Answer
Answer: B
this
关键字的指向取决于使用它的位置。 在函数中,比如getStatus
,this
指向的是调用它的对象,上述例子中data
对象调用了getStatus
,因此this
指向的就是data
对象。 当我们打印this.status
时,data
对象的status
属性被打印,即"🥑"
。使用
call
方法,可以更改this
指向的对象。data.getStatus.call(this)
是将this
的指向由data
对象更改为全局对象。在全局对象上,有一个名为status
的变量,其值为”😎“
。 因此打印this.status
时,会打印“😎”
。如果将此题的getStatus()
函数改为箭头函数,那么结果就不同了:箭头函数没有自己的this,this取决于外层代码块的,而且this无法被改变,所以输出“😍”
和“😍”
- A:
-
this指向判断
var name = "window"; var person = { name: "person", sayName: function () { console.log(this.name); } }; function sayName() { var sss = person.sayName; sss(); person.sayName(); (person.sayName)(); (b = person.sayName)(); } sayName();
Answer
sss()
:独立函数调用,没有和任何对象关联。输出window
person.sayName()
:和person对象关联。输出person
(person.sayName)()
:加了括号代表优先级,但在这里并没有什么用,同上。输出person
(b = person.sayName)()
:赋值应用默认绑定规则。输出window
-
this指向与改变
var name = 'window' var person1 = { name: 'person1', foo1: function () { console.log(this.name) }, foo2: () => console.log(this.name), foo3: function () { return function () { console.log(this.name) } }, foo4: function () { return () => { console.log(this.name) } } } var person2 = { name: 'person2' } person1.foo1(); person1.foo1.call(person2); person1.foo2(); person1.foo2.call(person2); person1.foo3()(); person1.foo3.call(person2)(); person1.foo3().call(person2); person1.foo4()(); person1.foo4.call(person2)(); person1.foo4().call(person2);
Answer
person1.foo1(); // person1 person1.foo1.call(person2); // person2 person1.foo2(); // window 箭头函数的this和外层代码块相关 person1.foo2.call(person2); // window 箭头函数的this不能被改变 person1.foo3()(); // window 作为普通函数被调用,没有明确的上下文对象 person1.foo3.call(person2)(); // window 虽然改变了foo3()的this指向,但其中的匿名函数仍然作为普通函数被调用 person1.foo3().call(person2); // person2 先返回了匿名函数,再改变这个函数的this指向,指向person2 person1.foo4()(); // person1 返回一个箭头函数,箭头函数的this和外层代码块相关,也就是person1的内部 person1.foo4.call(person2)(); // person2 改变了foo4的this指向 person1.foo4().call(person2); // person1 箭头函数的this无法被改变
-
this和原型链的配合
function A (cName) { if (cName) { this.name = cName } } A.prototype.name = 'XiaoMi' var a = new A() console.log('A', a.name) function B (cName) { this.name = cName } B.prototype.name = 'Xiaomi' var b = new B() console.log('B', b.name)
- A:A XiaoMi B Xiaomi
- B:A XiaoMi B undefined
- C:A undefined B undefined
- D:A undefined B XiaoMi
Answer
Answer: B
在
A
的构造函数中,只有当cName
参数存在时,才会在实例上设置name
属性。所以当我们使用new A()
创建一个新的A
实例a
时,因为没有传入cName
参数,所以a
实例上没有name
属性,当我们尝试访问a.name
时,JavaScript 会在A
的原型链上查找name
属性,所以console.log('A', a.name)
输出的是'A', 'XiaoMi'
。在
B
的构造函数中,无论cName
参数是否存在,都会在实例上设置name
属性。所以当我们使用new B()
创建一个新的B
实例b
时,即使没有传入cName
参数,b
实例上也会有一个name
属性,但是它的值是undefined
,因为cName
是undefined
。所以console.log('B', b.name)
输出的是'B', undefined
。 -
上的
-
s
-
是
运算问题
-
计算转换
+true; !'Lydia';
- A:
1
andfalse
- B:
false
andNaN
- C:
false
andfalse
Answer
Answer: A
一元加号运算符(unary plus)会尝试将操作数转换为数字。在JavaScript中,
true
被转换为1
,false
被转换为0
。字符串
'Lydia'
,它是一个真值(truthy value),也就是说它在布尔上下文中会被视为true。因此,我们实际上在问“这个真值是否为假值?”这个表达式的结果是false
,因为“Lydia”是一个真值,不是假值。 - A:
-
类型比较
let a = 3; let b = new Number(3); let c = 3; console.log(a == b); console.log(a === b); console.log(b === c);
- A:
true
false
true
- B:
false
false
true
- C:
true
false
false
- D:
false
true
true
Answer
Answer: C
new Number()
是一个内置的函数构造器。虽然它看起来像一个数字,但它实际上不是一个数字:它有许多额外的特性,并且是一个对象。当我们使用
==
运算符(相等运算符)时,它只检查它们是否具有相同的值。它们都具有值3
,因此返回true
。然而,当我们使用
===
运算符(严格相等运算符)时,值和类型都应该相同。但实际上它们不同:new Number()
不是一个数字,而是一个对象。因此两者都返回false
。 - A:
-
数值运算
function sum(a, b) { return a + b; } sum(1, '2');
- A:
NaN
- B:
TypeError
- C:
"12"
- D:
3
Answer
**Answer: C**
Answer: C
JavaScript是一种动态类型语言:不指定某些变量的类型。值可以自动转换为另一种类型,这被称为隐式类型转换。转换是从一种类型转换为另一种类型。
在这个例子中,JavaScript将数字
1
转换为字符串,以便函数有意义并返回一个值。在数字类型(1
)和字符串类型('2'
)相加时,数字被视为字符串。我们可以像"Hello"+"World"
一样连接字符串,所以这里发生的是"1"+"2"
,返回"12"
。 - A:
-
运算符
let number = 0; console.log(number++); console.log(++number); console.log(number);
- A:
1
1
2
- B:
1
2
2
- C:
0
2
2
- D:
0
1
2
Answer
Answer: C
后缀一元运算符
++
:返回值(此处返回
0
)
增加值(数字现在为1
)
前缀一元运算符++
:增加值(数字现在为
2
)
返回值(此处返回2
)
这将返回0 2 2
。 - A:
-
标记模板字面量
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`;
- A:
"Lydia"
21
["", " is ", " years old"]
- B:
["", " is ", " years old"]
"Lydia"
21
- C:
"Lydia"
["", " is ", " years old"]
21
Answer
Answer: B
如果你使用标记模板字面量,第一个参数的值总是一个字符串值的数组。其余的参数获取传递的表达式的值!
- A:
-
对象比较
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 });
- A:
You are an adult!
- B:
You are still an adult.
- C:
Hmm.. You don't have an age I guess
Answer
Answer: C
JavaScript中比较相等性时,原始值是按照它们的值进行比较的,而对象是按照它们的引用进行比较的。JavaScript会检查这些对象是否具有指向内存中相同位置的引用。
我们比较的两个对象没有这个引用:我们作为参数传递的对象与我们用来检查相等性的对象在内存中引用的位置不同。因此,它们被认为是不相等的。
在JavaScript中,==和===都用于比较两个值的相等性。==运算符比较两个值是否相等,但它会进行类型转换。如果两个值的类型不同,==运算符会尝试将它们转换为相同的类型,然后再进行比较。例如,如果我们比较数字1和字符串"1",==运算符会将字符串"1"转换为数字1,然后再进行比较。如果两个值的类型相同,==运算符会直接比较它们的值。
这就是为什么
{ age: 18 } === { age: 18 }
和{ age: 18 } == { age: 18 }
返回false
. - A:
-
eval方法
const sum = eval('10*10+5');
- A:
105
- B:
"105"
- C:
TypeError
- D:
"10*10+5"
Answer
Answer: A
eval
函数评估作为字符串传递的代码。如果它是一个表达式,就像这个例子一样,它会评估这个表达式。这个表达式是10 * 10 + 5
。这将返回数字105
。 - A:
-
类型判断
function sayHi() { return (() => 0)(); } console.log(typeof sayHi());
- A:
"object"
- B:
"number"
- C:
"function"
- D:
"undefined"
Answer
Answer: B
sayHi
函数返回立即调用的函数表达式(IIFE)的返回值。这个函数返回了0
,它的类型是“number”
。另外,typeof操作符可以返回以下值:
undefined
、boolean
、number
、bigint
、string
、symbol
、function
和object
。需要注意的是,typeof null
返回的是"object"
。 - A:
-
加法运算
console.log(3 + 4 + '5');
- A:
"345"
- B:
"75"
- C:
12
- D:
"12"
Answer
Answer: B
运算符的结合性(associativity)是编译器按照左到右或右到左的顺序计算表达式的方式。这仅在所有运算符具有相同的优先级时才会发生。在这里,我们只有一种运算符:
+
。对于加法运算,其结合性是从左到右。首先计算
3 + 4
,结果为数字7
。接着计算
7 + '5'
,结果为"75"
,这是由于类型转换。JavaScript将数字7
转换为字符串,参见问题15。我们可以使用+
运算符来连接两个字符串。"7" + "5"
的结果是"75"
。 - A:
-
解构赋值
const user = { name: 'Lydia', age: 21 }; const admin = { admin: true, ...user }; console.log(admin);
- A:
{ admin: true, user: { name: "Lydia", age: 21 } }
- B:
{ admin: true, name: "Lydia", age: 21 }
- C:
{ admin: true, user: ["Lydia", 21] }
- D:
{ admin: true }
Answer
**Answer: B**
Answer: B
使用展开运算符(spread operator)
...
可以将对象进行合并。它可以创建一个对象的键/值对的副本,并将其添加到另一个对象中。在这种情况下,我们创建了user
对象的副本,并将其添加到admin
对象中。admin
对象现在包含了复制的键/值对,结果为{ admin: true, name: "Lydia", age: 21 }
。 - A:
-
运算符
let num = 10; const increaseNumber = () => num++; const increasePassedNumber = number => number++; const num1 = increaseNumber(); const num2 = increasePassedNumber(num1); console.log(num1); console.log(num2);
- A:
10
,10
- B:
10
,11
- C:
11
,11
- D:
11
,12
Answer
**Answer: A**
Answer: A
一元运算符
++
首先返回操作数的值,然后递增操作数的值。num1
的值是10
,因为increaseNumber
函数首先返回num
的值,即10
,然后才递增num
的值。num2
的值是10
,因为我们将num1
传递给了increasePassedNumber
函数。number
等于10
(即num1
的值)。同样,一元运算符++
首先返回操作数的值,然后递增操作数的值。number
的值是10
,所以num2
等于10
。 - A:
-
运算优先级
var a = 1 + (1 > 2)?1:2;
- A: 1
- B: 2
- C: 3
- D: undefined
Answer
Answer: A
?:
条件运算符的优先级只比赋值运算符和逗号运算符更高,所以该代码表示:(1 + 0)?1:2
,所以答案应该选A -
函数表达式和type of 运算符
var x = 1; if (function test() {}) { x += typeof test; } console.log(x);
Answer
在 JavaScript 中,
if (function test() {})
是一个立即执行的函数表达式(IIFE)。这个函数表达式创建了一个名为test
的函数,但这个函数只在其自身的函数体内部可见,外部无法访问到这个函数。因此,当执行
x += typeof test;
时,test
是未定义的,typeof test
返回的是 “undefined”。所以,
x += typeof test;
实际上是x += "undefined";
,由于x
是一个数字,所以 JavaScript 会将x
转换为字符串,然后进行字符串连接,得到 “1undefined”。 -
是
-
上的
内置方法
-
reduce方法
[[0, 1], [2, 3]].reduce( (acc, cur) => { return acc.concat(cur); }, [1, 2], );
- A:
[0, 1, 2, 3, 1, 2]
- B:
[6, 1, 2]
- C:
[1, 2, 0, 1, 2, 3]
- D:
[1, 2, 6]
Answer
**Answer: C** **Answer: C**
[1, 2]
是我们的初始值。这是我们开始时的值,也是第一个累加器(acc
)的值。在第一轮中,acc
是[1, 2]
,cur
是[0, 1]
。我们将它们连接起来,结果为[1, 2, 0, 1]
。接下来,
[1, 2, 0, 1]
成为新的acc
,[2, 3]
成为cur
。我们将它们连接起来,得到[1, 2, 0, 1, 2, 3]
。 - A:
-
parseInt
方法const num = parseInt('7*6', 10);
- A:
42
- B:
"42"
- C:
7
- D:
NaN
Answer
**Answer: C**
Answer: C
只有字符串中的第一个数字被返回。基于基数(作为第二个参数,用于指定要将其解析为的数字类型:十进制、十六进制、八进制、二进制等),
parseInt
函数会检查字符串中的字符是否有效。一旦遇到在基数中无效的字符,它就会停止解析并忽略后面的字符。*
不是一个有效的数字。它只解析"7"
为十进制数7
。现在,变量num
的值为7
。 - A:
-
map
方法[1, 2, 3].map(num => { if (typeof num === 'number') return; return num * 2; });
- A:
[]
- B:
[null, null, null]
- C:
[undefined, undefined, undefined]
- D:
[ 3 x empty ]
Answer
Answer: C
在对数组进行映射(mapping)时,变量num的值等于当前正在循环的元素。在这种情况下,元素是数字,因此if语句的条件typeof num === "number"返回true。map函数创建一个新数组,并将从函数返回的值插入其中。
然而,我们没有返回一个值。当我们从函数中没有返回值时,函数将返回undefined。对于数组中的每个元素,函数块都会被调用,因此对于每个元素,我们都返回undefined。
- A:
-
set方法
const set = new Set([1, 1, 2, 3, 4]); console.log(set);
- A:
[1, 1, 2, 3, 4]
- B:
[1, 2, 3, 4]
- C:
{1, 1, 2, 3, 4}
- D:
{1, 2, 3, 4}
Answer
Answer: D
Set
对象是一个包含唯一值的集合:在一个集合中,一个值只能出现一次。我们传递了可迭代对象
[1, 1, 2, 3, 4]
,其中有一个重复值1
。由于在一个集合中不能有两个相同的值,其中一个被移除。结果是{1, 2, 3, 4}
。 - A:
-
delete方法
const name = 'Lydia'; age = 21; console.log(delete name); console.log(delete age);
- A:
false
,true
- B:
"Lydia"
,21
- C:
true
,true
- D:
undefined
,undefined
Answer
Answer: A
delete
运算符返回一个布尔值:如果删除成功则返回true
,否则返回false
。然而,使用var
、const
或let
关键字声明的变量无法使用delete
运算符删除。name
变量是使用const
关键字声明的,所以删除操作不成功:返回false
。当我们将age
设置为21
时,实际上是向全局对象添加了一个名为age
的属性。你可以通过这种方式成功删除对象的属性,也可以删除全局对象,因此delete age
返回true
。 - A:
-
Object.defineProperty和Object.keys()方法
const person = { name: 'Lydia' }; Object.defineProperty(person, 'age', { value: 21 }); console.log(person); console.log(Object.keys(person));
- A:
{ name: "Lydia", age: 21 }
,["name", "age"]
- B:
{ name: "Lydia", age: 21 }
,["name"]
- C:
{ name: "Lydia"}
,["name", "age"]
- D:
{ name: "Lydia"}
,["age"]
Answer
Answer: B
使用
defineProperty
方法,我们可以向对象添加新属性或修改现有属性。当我们使用defineProperty
方法向对象添加属性时,默认情况下它们是不可枚举的。Object.keys
方法从对象中返回所有可枚举的属性名,本例中只有"name"
。使用
defineProperty
方法添加的属性默认是不可变的。您可以使用writable
、configurable
和enumerable
属性来覆盖此行为。这样,defineProperty
方法使您对要添加到对象中的属性具有更多控制权。 - A:
-
stringify方法
const settings = { username: 'lydiahallie', level: 19, health: 90, }; const data = JSON.stringify(settings, ['level', 'health']); console.log(data);
- A:
"{"level":19, "health":90}"
- B:
"{"username": "lydiahallie"}"
- C:
"["level", "health"]"
- D:
"{"username": "lydiahallie", "level":19, "health":90}"
Answer
Answer: A
JSON.stringify
的第二个参数是replacer。replacer可以是一个函数或一个数组,它允许您控制值的字符串化方式和内容。如果replacer是一个array,只有数组中包含的属性名将被添加到JSON字符串中。在这种情况下,只有名为
"level"
和"health"
的属性被包含在内,"username"
被排除在外。data
现在等于"{ "level": 19, "health": 90 }"
。如果replacer是一个函数,该函数将在您对对象进行字符串化时调用。从该函数返回的值将成为将属性添加到JSON字符串时的属性值。如果值为
undefined
,则该属性将被排除在JSON字符串之外。 - A:
-
reduce方法
[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
- A:
1
2
and3
3
and6
4
- B:
1
2
and2
3
and3
4
- C:
1
undefined
and2
undefined
and3
undefined
and4
undefined
- D:
1
2
andundefined
3
andundefined
4
Answer
**Answer: D**
Answer: D
reduce
方法接收的第一个参数是累加器,本例中为x
。第二个参数是当前值,为y
。使用reduce
方法,我们在数组中的每个元素上执行一个回调函数,最终可能会得到一个单一的值。在这个例子中,我们并没有返回任何值,只是记录了累加器和当前值的值。
累加器的值等于回调函数先前返回的值。如果您没有将可选的
initialValue
参数传递给reduce
方法,第一次调用时累加器等于第一个元素。在第一次调用时,累加器(
x
)为1
,当前值(y
)为2
。我们没有从回调函数返回值,而是记录了累加器和当前值:输出为1
和2
。如果您没有从函数中返回值,它将返回
undefined
。在下一次调用时,累加器为undefined
,当前值为3
。输出为undefined
和3
。在第四次调用时,我们再次没有从回调函数返回值。累加器再次为
undefined
,当前值为4
。输出为undefined
和4
。 - A:
-
padStart 方法
const name = "Lydia Hallie" console.log(name.padStart(13)) console.log(name.padStart(2))
- 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"
Answer
Answer: C
使用
padStart
方法,我们可以在字符串的开头添加填充。传递给此方法的参数是字符串的总长度(包含填充)。字符串Lydia Hallie
的长度为12
,因此name.padStart(13)
在字符串的开头只会插入 1(13 - 12 = 1
)个空格。如果传递给
padStart
方法的参数小于字符串的长度,则不会添加填充。 - A:
-
String.raw 方法
console.log(String.raw`Hello\nworld`);
- A:
Hello world!
- B:
Hello
world
- C:
Hello\nworld
- D:
Hello\n
world
Answer
Answer: C
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
被打印出。 - A:
-
list.push 方法
function addToList(item, list) { return list.push(item); } const result = addToList("apple", ["banana"]); console.log(result);
- A:
['apple', 'banana']
- B:
2
- C:
true
- D:
undefined
Answer
Answer: B
push()
方法返回新数组的长度。一开始,数组包含一个元素(字符串"banana"
),长度为 1。 在数组中添加字符串"apple"
后,长度变为 2,并将从addToList
函数返回。push
方法修改原始数组,如果你想从函数返回数组而不是数组长度,那么应该在 pushitem
之后返回list
。 - A:
-
Object.freeze方法
const box = { x: 10, y: 20 }; Object.freeze(box); const shape = box; shape.x = 100; console.log(shape)
- A:
{ x: 100, y: 20 }
- B:
{ x: 10, y: 20 }
- C:
{ x: 100 }
- D:
ReferenceError
Answer
**Answer: B**
Answer: B
Object.freeze
使得无法添加、删除或修改对象的属性(除非属性的值是另一个对象)。当我们创建变量
shape
并将其设置为等于冻结对象box
时,shape
指向的也是冻结对象。你可以使用Object.isFrozen
检查一个对象是否被冻结,上述情况,Object.isFrozen(shape)
将返回true
。由于
shape
被冻结,并且x
的值不是对象,所以我们不能修改属性x
。x
仍然等于10
,{x:10,y:20}
被打印。注意,上述例子我们对属性
x
进行修改,可能会导致抛出 TypeError 异常(最常见但不仅限于严格模式下时)。 - A:
-
Object.entries方法
const person = { name: "Lydia", age: 21 } for (const [x, y] of Object.entries(person)) { console.log(x, y) }
- A:
name
Lydia
andage
21
- B:
["name", "Lydia"]
and["age", 21]
- C:
["name", "age"]
andundefined
- D:
Error
Answer
Answer: 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
。 - A:
-
日期函数的输出
new Date(2020,12,1).getMonth()
的结果是()- 12
- 11
- 0
- 13
Answer
Answer: C
new Date()
的参数的带构造器的形式为new Date(year, monthIndex, day)
,也就是月份索引从0开始,如果超过11,那么相当于到第二年,即12相应的索引为0。注意:如果直接
new Date().getMonth()
返回的结果为现实的月份 - 1。 -
s
-
是
概念问题
-
事件传播的三个阶段顺序?
- A: 目标阶段 > 捕获阶段 > 冒泡阶段
- B: 冒泡阶段 > 目标阶段 > 捕获阶段
- C: 目标阶段 > 冒泡阶段 > 捕获阶段
- D: 捕获阶段 > 目标阶段 > 冒泡阶段
Answer
Answer: D
在捕获阶段中,事件从祖先元素开始向下传播到目标元素。然后,事件到达目标元素并触发。接着,冒泡阶段开始,事件从目标元素开始向上冒泡到最外层的元素。因此,在事件传播的过程中,捕获阶段和冒泡阶段都是必要的,它们共同构成了事件传播的完整过程。
-
所有对象都有原型么?
- A: 正确
- B: 错误
Answer
Answer: B
所有对象都有原型,除了基础对象。基础对象是由用户创建的对象,或使用
new
关键字创建的对象。基础对象可以访问一些方法和属性,例如.toString
方法。这就是为什么可以使用内置的JavaScript方法的原因!所有类似的方法都在原型上可用。虽然JavaScript无法直接在对象上找到它,但它会沿着原型链向下查找并在那里找到它,这使得它对您可用。 -
cool_secret 可以访问多长时间?
sessionStorage.setItem('cool_secret', 123);
- A: 永远,数据不会丢失。
- B: 当用户关闭选项卡时。
- C: 当用户关闭整个浏览器时,不仅仅是选项卡。
- D: 当用户关闭他们的计算机时。
Answer
Answer: B
存储在
sessionStorage
中的数据在关闭选项卡后会被删除。如果您使用了
localStorage
,除非例如调用localStorage.clear()
,否则数据将永久存在。 -
全局执行上下文
JavaScript全局执行上下文为您创建了两个东西:全局对象和“this”关键字。
- A: 正确
- B: 错误
- C: 取决于环境
Answer
Answer: A
基本执行上下文是全局执行上下文:它是您代码中任何地方都可以访问的内容。
-
事件循环
const foo = () => console.log('First'); const bar = () => setTimeout(() => console.log('Second')); const baz = () => console.log('Third'); bar(); foo(); baz();
- A:
First
Second
Third
- B:
First
Third
Second
- C:
Second
First
Third
- D:
Second
Third
First
Answer
**Answer: B**
Answer: B
事件循环相关知识。
- A:
-
点击按钮时哪一个是event.target?
<div onclick="console.log('first div')"> <div onclick="console.log('second div')"> <button onclick="console.log('button')"> Click! </button> </div> </div>
- A: 外层的
div
- B: 内层的
div
- C:
button
- D: 所有嵌套元素的数组
Answer
Answer: C
当点击一个元素时,事件会从该元素开始向上冒泡,直到达到文档根节点。在这个过程中,每个祖先元素都会收到该事件。事件的目标元素是最深层次的嵌套元素,它是直接触发事件的元素。您可以使用
event.target
属性来访问该元素。如果您想停止事件冒泡,可以使用event.stopPropagation()
方法。这将阻止事件继续向上冒泡,从而只触发目标元素的事件处理程序。 - A: 外层的
-
判断为
false
的值0; new Number(0); (''); (' '); new Boolean(false); undefined;
- A:
0
,''
,undefined
- B:
0
,new Number(0)
,''
,new Boolean(false)
,undefined
- C:
0
,''
,new Boolean(false)
,undefined
- D: All of them are falsy
Answer
**Answer: A**
Answer: A
有以下8种为false的数值:
undefined
null
NaN
false
''
(empty string)0
-0
0n
(BigInt(0))
像new Number和new Boolean这样的函数构造器被视为真值
- A:
-
所有在JavaScript中的元素只属于?
- A: 原始类型或对象
- B: 函数或对象
- C: 只有对象
- D: 数字或对象
Answer
Answer: A
-
setInterval
在浏览器中返回什么?setInterval(() => console.log('Hi'), 1000);
- A: 一个唯一的id
- B: 指定的毫秒数
- C: 传递的函数
- D:
undefined
Answer
Answer: A
它返回一个唯一的 ID。此 ID 可用于通过
clearInterval()
函数清除该间隔。 -
模块的导入与导出
// counter.js let counter = 10; export default counter;
// index.js import myCounter from './counter'; myCounter += 1; console.log(myCounter);
- A:
10
- B:
11
- C:
Error
- D:
NaN
Answer
Answer: C
导入的模块是只读的:你不能修改导入的模块。只有导出它们的模块可以改变它们的值。
当我们尝试增加
myCounter
的值时,会抛出一个错误:myCounter
是只读的,不能被修改。 - A:
-
import和require的执行顺序
// 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;
- A:
running index.js
,running sum.js
,3
- B:
running sum.js
,running index.js
,3
- C:
running sum.js
,3
,running index.js
- D:
running index.js
,undefined
,running sum.js
Answer
Answer: B
import
命令是编译阶段执行的,在代码运行之前。因此这意味着被导入的模块会先运行,而导入模块的文件会后执行。这是 CommonJS 中
require()
和import
之间的区别。使用require()
,您可以在运行代码时根据需要加载依赖项。 如果我们使用require
而不是import
,running index.js
,running sum.js
,3
会被依次打印。 - A:
-
纯函数判断
function sum(a, b) { return a + b; }
- A: Yes
- B: No
Answer
Answer: A
纯函数在相同的输入值时,需产生相同的输出,其输出的结果,与输入值以外的其他隐藏信息或状态无关,也和由 I/O 设备产生的外部输出无关。 纯函数不会产生副作用。
纯函数与副作用的定义可参考: https://zh.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)
-
模块导入
// module.js export default () => "Hello world" export const name = "Lydia" // index.js import * as data from "./module" console.log(data)
- A:
{ default: function default(), name: "Lydia" }
- B:
{ default: function default() }
- C:
{ default: "Hello world", name: "Lydia" }
- D: Global object of
module.js
Answer
Answer: A
使用
import * as name
语法,我们将module.js
文件中所有export
导入到index.js
文件中,并且创建了一个名为data
的新对象。 在module.js
文件中,有两个导出:默认导出和命名导出。 默认导出是一个返回字符串 “Hello World” 的函数,命名导出是一个名为name
的变量,其值为字符串"Lydia"
。data
对象具有默认导出的default
属性,其他属性具有指定 exports 的名称及其对应的值。 - A:
-
都是
-
都是
-
都是
-
都是
正则表达式
-
非捕获组
下面能完全覆盖0~10000(不含10000)的整数或2位以内小数的正则表达式是:
- A:
/^-?\d{1,4}\.\d{1,2}?$/
- B:
/^-?\d{5}(?:\.\d{1,2})?$/
- C:
/^-?\d{1,4}\.\d{2}?$/
- D:
/^-?\d{1,4}(?:\.\d{1,2})?$/
Answer
Answer: D
- 首先注意是0-9999,即一位数字到四位数字:
d(1,4)
(?:)
是非捕获组,不进行存储供以后使用,这保证了正则表达式的性能,同时这在使用 “或” 字符 (|) 来组合一个模式的各个部分是很有用。例如,'industr(?:y|ies)'
就是一个比'industry|industries'
更简略的表达式。\ .\d{1,2}
表示匹配一个小数点和一个一到二位的整数(?: ...)?
末尾的?
表示或的意思,表示前面的内容可以出现0次或1次-?
表示负号可以出现0次或1次
- A:
-
是
-
是
-
是
-
是