来源:
javascript-questions/zh-CN/README-zh_CN.md at master · lydiahallie/javascript-questions · GitHub
记录一些有趣的题。
1
输出是?
const shape = {radius: 10,diameter() {return this.radius * 2},perimeter: () => 2 * Math.PI * this.radius
}shape.diameter()
shape.perimeter()
答:
20 NaN
解析:
省流版:
shape.perimeter()是箭头函数,this不指向shape,而是指向window。window没有radius属性。
完整版:
注意diameter
的值是一个常规函数,但是perimeter
的值是一个箭头函数。
对于箭头函数,this
关键字指向的是它当前周围作用域(简单来说是包含箭头函数的常规函数,如果没有常规函数的话就是全局对象),这个行为和常规函数不同。这意味着当我们调用perimeter
时,this
不是指向shape
对象,而是它的周围作用域(在例子中是window
)。
在window
中没有radius
这个属性,因此返回undefined
。
2
正确的是:?
const bird = {size: 'small'
}const mouse = {name: 'Mickey',small: true
}
- A:
mouse.bird.size
是无效的 - B:
mouse[bird.size]
是无效的 - C:
mouse[bird["size"]]
是无效的 - D: 以上三个选项都是有效的
答:
A
解析:
省流版:
用.
取属性必须是存在的属性
完整版:
在 JavaScript 中,所有对象的 keys 都是字符串(除非对象是 Symbol)。尽管我们可能不会定义它们为字符串,但它们在底层总会被转换为字符串。
当我们使用括号语法时([]),JavaScript 会解释(或者 unboxes)语句。它首先看到第一个开始括号[
并继续前进直到找到结束括号]
。只有这样,它才会计算语句的值。
mouse[bird.size]
:首先计算bird.size
,这会得到small
。mouse["small"]
返回true
。
然后使用点语法的话,上面这一切都不会发生。mouse
没有bird
这个 key,这也就意味着mouse.bird
是undefined
。然后当我们使用点语法mouse.bird.size
时,因为mouse.bird
是undefined
,这也就变成了undefined.size
。这个行为是无效的,并且会抛出一个错误类似Cannot read property "size" of undefined
。
3
输出是?
let a = 3
let b = new Number(3)
let c = 3console.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
答:
C
解析:
==
只比值,===
比较值和数据类型。
a和c是number,b是包装类Number生成的对象,ac与b的数据类型不同。
4
输出是?
class Chameleon {static colorChange(newColor) {this.newColor = newColorreturn this.newColor}constructor({ newColor = 'green' } = {}) {this.newColor = newColor}
}const freddie = new Chameleon({ newColor: 'purple' })
freddie.colorChange('orange')
- A:
orange
- B:
purple
- C:
green
- D:
TypeError
答:
D。
解析:
colorChange是类Chameleon的静态方法,不能被实例freddie调用(类的静态方法不能传递给实例)。
5
输出是?
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
答:
A。
解析:
member
没有getFullName()
方法,代码中只是给Person
定义了getFullName
方法。
若想给所有实例添加特性,要使用原型。如:
Person.prototype.getFullName = function () {return `${this.firstName} ${this.lastName}`;
}
6
输出是?
function Person(firstName, lastName) {this.firstName = firstNamethis.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
答:
A。
解析:
new Person()
会创建一个新的Person对象,而Person()
不会创建新对象,因此它的this
会指向window。
对于sarah = Person('Sarah', 'Smith')
,它会让window.firstName='Sarah'
,window.lastName='Smith'
。构造函数中没有返回,因此sarah
为undefined
。
7
事件传播的三个阶段是什么?
- A: Target > Capturing > Bubbling
- B: Bubbling > Target > Capturing
- C: Target > Bubbling > Capturing
- D: Capturing > Target > Bubbling
答:
D。
解析:
在捕获Capturing 阶段中,事件从祖先元素往下传播到目标元素。当事件到达目标Target 后,向上冒泡Bubbling。
8
所有对象都有原型。
- A: 对
- B: 错
答:
B。
解析:
基本数据类型没有原型。
基本数据类型为:Number、String、Boolean、undefined、object、Null
。
还有新加的Symbol、bigInt
。
9
输出是?
function getPersonInfo(one, two, three) {console.log(one)console.log(two)console.log(three)
}const person = 'Lydia'
const age = 21getPersonInfo`${person} is ${age} years old`
答:
[ '', ' is ', ' years old' ]
Lydia
21
解析:
调用
getPersonInfo
方法,传参数为标记模板字面量,则第一个参数是总是包含字符串的数组,其余的参数获取的是传递的表达式的值。
10
输出是?
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 })
答:
Hmm.. You don't have an age I guess
解析:
对象比较的是引用。引用不管是
===
还是==
都不相同。
11
function getAge(...args) {console.log(typeof args)
}getAge(21)
- A:
"number"
- B:
"array"
- C:
"object"
- D:
"NaN"
答:
C。
解析:
拓展运算符
...
会返回实参组成的数组,尽管只有一个参数。
12
const sum = eval('10*10+5')
输出是?
答案:
eval 函数可以执行任何传递给它的 JavaScript 代码,这可能导致安全问题。除非你很清楚你在做什么,否则,不要使用此函数。
13
三段代码输出为?
let num = 8;
let num = 10;console.log(num);
const num = 8;
const num = 10;console.log(num);
var num = 8;
var num = 10;console.log(num);
答:
let和const都报错。不能在块级作用域里重复声明一个已经声明的变量。
var的输出为10,因为var是全局作用域,可以使用相同的名称声明多个变量,会覆盖。
14
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)
答:
true true false true
解析:
对象的所有键(不包括 Symbol)在底层都是字符串。 因此前两个是true。
15
const obj = { a: 'one', b: 'two', a: 'three' }
console.log(obj)
答:
{ a: "three", b: "two" }
解析:
如果你有两个名称相同的键,则新键会覆盖旧的。它仍然位于第一个键出现的位置,但是值是最后出现那个键的值。
16
String.prototype.giveLydiaPizza = () => {return 'Just give Lydia pizza already!'
}const name = 'Lydia'name.giveLydiaPizza()
答:
‘Just give Lydia pizza already!’
解析:
向 String.prototype
添加了一个新的方法 giveLydiaPizza
。这意味着所有的字符串实例都可以访问这个方法。
17
const a = {}
const b = { key: 'b' }
const c = { key: 'c' }a[b] = 123
a[c] = 456console.log(a[b])
答:
456
解析:
在 JavaScript 中,对象的键只能是字符串或者符号。当你试图使用一个对象作为键时,JavaScript 会自动将这个对象转换为字符串。默认情况下,所有对象都会被转换为字符串 “[object Object]
”。
无论 b 或 c 对象的内容是什么,它们都会被转换为字符串 “[object Object]
”。因此,a[b] = 123 和 a[c] = 456 实际上都是在设置同一个属性 a["[object Object]"]
。最后一次设置的值会覆盖前面的值,所以 a[b] 的值最终是 456。
18
当点击按钮时,event.target 是什么?
<div onclick="console.log('first div')"><div onclick="console.log('second div')"><button onclick="console.log('button')">Click!</button></div>
</div>
答:
button
解析:
event.target 属性是一个引用,指向触发事件的元素。是最初被点击的元素,即button。
但是上面两层的事件也会触发。
19
const person = { name: 'Lydia' }function sayHi(age) {console.log(`${this.name} is ${age}`)
}sayHi.call(person, 21)
sayHi.bind(person, 21)
答:
Lydia is 21
function
解析:
call 方法调用一个函数,并立即执行它。 它接受的第一个参数是 this 的值,后面的参数是传递给函数的参数。在这个例子中,this 被设置为 person 对象,所以 this.name 是 ‘Lydia’,并且 age 参数是 21。
然而,sayHi.bind(person, 21) 不会立即执行函数,而是返回一个新的函数,当这个新函数被调用时,this 的值将被设置为 person,并且 age 参数将被设置为 21。因此,sayHi.bind(person, 21) 本身不会有任何输出。如果你想看到 “Lydia is 21”,你需要调用返回的函数,像这样:sayHi.bind(person, 21)()。
20
下面哪些值是 falsy?
0
new Number(0)
('')
(' ')
new Boolean(false)
undefined
答:
0
(‘’)
undefined
解析:
这8项是falsy(假值)
undefined
null
NaN
false
''
(empty string)0
-0
0n
(BigInt(0))
选项中,new Number(0)
和new Boolean(false)
,都是对象。
21
const numbers = [1, 2, 3]
numbers[10] = 11
console.log(numbers)
答:
[1, 2, 3, <7 empty items>, 11]
解析:
在 JavaScript 中,当你尝试设置一个数组的索引值大于其当前长度时,JavaScript 会自动扩展数组,并在新创建的空位上填充 undefined
。但在控制台输出时通常显示为 empty
22
[[0, 1], [2, 3]].reduce((acc, cur) => {return acc.concat(cur)},[1, 2]
)
答:
[1, 2, 0, 1, 2, 3]
解析:
初始值为[1, 2]开始合并,即[1, 2, 0, 1, 2, 3]。
23
setInterval
方法的返回值是什么?
答:
一个唯一的id。此 id 可被用于 clearInterval
函数来取消定时。
24
function* generator(i) {yield i;yield i * 2;
}const gen = generator(10);console.log(gen.next().value);
console.log(gen.next().value);
答:
10 20
解析:
生成器函数与yield
。在遇到yield的时候停下,遇到next()方法时继续。
25
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));
答:
two
解析:
Promise.race
返回一个promise,以第一个完成的promise的结果为准。
26
const person = {name: "Lydia",age: 21
};for (const item in person) {console.log(item);
}
答:
“name”, “age”
解析: for … in …通过对象的key
进行迭代。
27
const num = parseInt("7*6", 10);
答:
解析:
只返回了字符串中第一个字母。设定了 进制 后(即第二个参数),parseInt
检查字符串中的字符是否合法。一旦遇到一个在指定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符。
*
就是不合法的数字字符。所以只解析到"7"
,并将其解析为十进制的7
. num
的值即为7
.
28
[1, 2, 3].map(num => {if (typeof num === "number") return;return num * 2;
});
答:
[undefined, undefined, undefined]
解析:
都走的return
,没有返回值即默认undefined。
29
const set = new Set([1, 1, 2, 3, 4]);console.log(set);
答:
{1, 2, 3, 4}
解析:
set去重,并返回一个set
对象。而不是数组。
30
// counter.js
let counter = 10;
export default counter;
// index.js
import myCounter from "./counter";myCounter += 1;console.log(myCounter);
答:
Error。
解析:
引入的模块是只读的,不能修改。
会抛出异常:myCounter
是只读的,不能被修改。
31
const name = "Lydia";
age = 21;console.log(delete name);
console.log(delete age);
答:
false
, true
解析:
delete
操作符返回一个布尔值:true
指删除成功,否则返回false
. 但是通过 var
, const
或 let
关键字声明的变量无法用 delete
操作符来删除。
name
变量由const
关键字声明,所以删除不成功:返回 false
.
而我们设定age
等于21
时,我们实际上添加了一个名为age
的属性给全局对象。对象中的属性是可以删除的,全局对象也是如此,所以delete age
返回true
.
32
const numbers = [1, 2, 3, 4, 5];
const [y] = numbers;console.log(y);
答:
1
解析:
数组的解构。y
等于数组的第一个值即数字1
。
若:
const numbers = [1, 2, 3, 4, 5];
const [y,x] = numbers;console.log(y,x); // 1 2
33
const person = { name: "Lydia" };Object.defineProperty(person, "age", { value: 21 });console.log(person);
console.log(Object.keys(person));
答:
{ name: "Lydia", age: 21 }
, ["name"]
解析:
通过defineProperty
方法,我们可以给对象添加一个新属性,或者修改已经存在的属性。而我们使用defineProperty
方法给对象添加了一个属性之后,属性默认为 不可枚举 (not enumerable)
Object.keys
方法仅返回对象中 可枚举 (enumerable) 的属性,因此只剩下了"name"
.
用defineProperty
方法添加的属性默认不可变。你可以通过writable
, configurable
和 enumerable
属性来改变这一行为。这样,defineProperty
方法可以让您更好地控制要添加到对象的属性。
如,若想让它可枚举:
Object.defineProperty(person, "age", { value: 21, enumerable: true });
34
const settings = {username: "lydiahallie",level: 19,health: 90
};const data = JSON.stringify(settings, ["level", "health"]);
console.log(data);
答:
“{“level”:19, “health”:90}”
解析:
JSON.stringify
的第二个参数是 替代者 (replacer). 替代者 (replacer) 可以是个函数或数组,用以控制哪些值如何被转换为字符串。
如果替代者 (replacer) 是个 数组,那么就只有包含在数组中的属性将会被转化为字符串。即本例。
而如果替代者 (replacer) 是个 函数,这个函数将被对象的每个属性都调用一遍。函数返回的值会成为这个属性的值,最终体现在转化后的 JSON 字符串中.
(译者注:Chrome 下,经过实验,如果所有属性均返回同一个值的时候有异常,会直接将返回值作为结果输出而不会输出 JSON 字符串),而如果返回值为
undefined
,则该属性会被排除在外。
35
const value = { number: 10 };const multiply = (x = { ...value }) => {console.log(x.number *= 2);
};multiply();
multiply();
multiply(value);
multiply(value);
答:
20
, 20
, 20
, 40
解析:
在 ES6 中,我们可以使用默认值初始化参数。如果没有给函数传参,或者传的参值为 "undefined"
,那么参数的值将是默认值。上述例子中,我们将 value
对象进行了解构并传到一个新对象中,因此 x
的默认值为 {number:10}
。
默认参数在调用时才会进行计算,每次调用函数时,都会创建一个新的对象。我们前两次调用 multiply
函数且不传递值,那么每一次 x
的默认值都为 {number:10}
,因此打印出该数字的乘积值为20
。
第三次调用 multiply
时,我们传递了一个参数,即对象value
。*=
运算符实际上是x.number = x.number * 2
的简写,我们修改了x.number
的值,并打印出值20
。
第四次,我们再次传递value
对象。x.number
之前被修改为20
,所以x.number * = 2
打印为40
。