JavaScript 题库

mac2024-04-21  7

1. 输出是什么?

const shape = { radius: 10, diameter() { return this.radius * 2 }, perimeter: () => 2 * Math.PI * this.radius } shape.diameter() shape.perimeter()

答案: 20 and NaN

注意 diameter 的值是一个常规函数,但是 perimeter 的值是一个箭头函数。

对于箭头函数,this 关键字指向的是它当前周围作用域(简单来说是包含箭头函数的常规函数,如果没有常规函数的话就是全局对象),这个行为和常规函数不同。这意味着当我们调用 perimeter 时,this 不是指向 shape 对象,而是它的周围作用域(在例子中是 window)。

在 window 中没有 radius 这个属性,因此返回 undefined。

知识点:

普通函数和箭头函数的区别:

箭头函数没有prototype,所以箭头函数本身没有this箭头函数的this在定义的时候继承自外层第一个普通函数的this如果箭头函数外层没有普通函数,this会指向window全局对象箭头函数本身的this不能改变,但可以修改他要继承的对象的this箭头函数的this指向全局,使用arguments会报未声明的错箭头函数的this指向普通函数时,他的arguments继承于该普通函数使用new调用箭头函数会报错,因为箭头函数没有constructor箭头函数不支持重命名函数参数,普通函数支持箭头函数更加简洁优雅

 2. 输出是什么?

class Chameleon { static colorChange(newColor) { this.newColor = newColor return this.newColor } constructor({ newColor = 'green' } = {}) { this.newColor = newColor } } const freddie = new Chameleon({ newColor: 'purple' }) freddie.colorChange('orange')

答案: TypeError

colorChange 是一个静态方法。静态方法被设计为只能被创建它们的构造器使用(也就是 Chameleon),并且不能传递给实例。因为 freddie 是一个实例,静态方法不能被实例使用,因此抛出了 TypeError 错误。

知识点:

静态方法  在方法前加static关键字。表示该方法不会被实例继承,而是直接通过类来调用静态属性  在属性前加static关键字。指的是class是本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性

 3. 输出是什么?

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());

答案是:TypeError

你不能像常规对象那样,给构造函数添加属性。如果你想一次性给所有实例添加特性,你应该使用原型。因此本例中,使用如下方式:

Person.prototype.getFullName = function () { return `${this.firstName} ${this.lastName}`; }

这才会使 member.getFullName() 起作用。为什么这么做有益的?假设我们将这个方法添加到构造函数本身里。也许不是每个 Person 实例都需要这个方法。这将浪费大量内存空间,因为它们仍然具有该属性,这将占用每个实例的内存空间。相反,如果我们只将它添加到原型中,那么它只存在于内存中的一个位置,但是所有实例都可以访问它!

4. 输出是什么?

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)

 答案是:Person {firstName: "Lydia", lastName: "Hallie"} and undefined

对于 sarah,我们没有使用 new 关键字。当使用 new 时,this 引用我们创建的空对象。当未使用 new 时,this 引用的是全局对象(global object)。

知识点:new 一个对象的过程

 5. 输出是什么?

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`

答案:["", " is ", " years old"] "Lydia" 21

知识点:

如果使用标记模板字面量,第一个参数的值总是包含字符串的数组。其余的参数获取的是传递的表达式的值!

6. 输出是什么?

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

知识点:在测试相等性时,基本类型通过它们的值(value)进行比较,而对象通过它们的引用(reference)进行比较。JavaScript 检查对象是否具有对内存中相同位置的引用。

题目中我们正在比较的两个对象不是同一个引用:作为参数传递的对象引用的内存位置,与用于判断相等的对象所引用的内存位置并不同。

这也是 { age: 18 } === { age: 18 } 和 { age: 18 } == { age: 18 } 都返回 false 的原因。

7. 输出是什么?

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)在底层都是字符串,即使你自己没有将其作为字符串输入。这就是为什么 obj.hasOwnProperty('1') 也返回 true。

对于集合,它不是这样工作的。在我们的集合中没有 '1':set.has('1') 返回 false。它有数字类型为 1,set.has(1) 返回 true。

8. 输出是什么?

const a = {} const b = { key: 'b' } const c = { key: 'c' } a[b] = 123 a[c] = 456 console.log(a[b])

答案:456 

对象的键会自动转换为字符串,我们试图将对象b设置为对象a的键。

当将一个对象转化为字符串的的时候,会变成"[object Object]" 所以,a["[object Object]"] = 123 .然后,我们再一次做了同样的事情,c 是另外一个对象,这里也有隐式字符串化,于是,a["[object Object]"] = 456。然后,我们打印 a[b],也就是 a["[object Object]"]。之前刚设置为 456,因此返回的是 456。

9. 下面哪些值是falsy?

0 new Number(0) ('') (' ') new Boolean(false) undefined

答案:0, '', undefined

知识点:

只有 6 种 falsy 值:

undefinednullNaN0'' (empty string)false

Function 构造函数, 比如 new Number 和 new Boolean,是 truthy。

10 .输出是什么

console.log(typeof typeof 1)

答案:"string"

typeof 1 返回 "number"。 typeof "number" 返回 "string"。

11. 输出是什么?

(() => { let x, y try { throw new Error() } catch (x) { (x = 1), (y = 2) console.log(x) } console.log(x) console.log(y) })()

答案:1 undefined 2  

catch 代码块接收参数 x。当我们传递参数时,这与之前定义的变量 x 不同 。这个 x 是属于 catch 块级作用域的。

然后,我们将块级作用域中的变量赋值为 1,同时也设置了变量 y 的值。现在,我们打印块级作用域中的变量 x,值为 1。

catch 块之外的变量 x 的值仍为 undefined, y 的值为 2。当我们在 catch 块之外执行 console.log(x) 时,返回 undefined,y 返回 2。

12.输出是什么?

const person = { name: "Lydia", age: 21 }; for (const item in person) { console.log(item); }

答案:"name", "age"

在for-in循环中,我们可以通过对象的key来进行迭代,也就是这里的name和age。

13. num的值是什么?

const num = parseInt("7*6", 10);

答案:7

只返回了字符串中第一个字母. 设定了 进制 后 (也就是第二个参数,指定需要解析的数字是什么进制: 十进制、十六机制、八进制、二进制等等……),parseInt 检查字符串中的字符是否合法. 一旦遇到一个在指定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符。

*就是不合法的数字字符。所以只解析到"7",并将其解析为十进制的7. num的值即为7.

14.输出是什么?

[1, 2, 3].map(num => { if (typeof num === "number") return; return num * 2; });

答案是:[undefined, undefined, undefined]

对数组进行映射的时候,num就是当前循环到的元素. 在这个例子中,所有的映射都是number类型,所以if中的判断typeof num === "number"结果都是true.map函数创建了新数组并且将函数的返回值插入数组。

但是,没有任何值返回。当函数没有返回任何值时,即默认返回undefined.对数组中的每一个元素来说,函数块都得到了这个返回值,所以结果中每一个元素都是undefined.

15.输出是什么?

function getInfo(member, year) { member.name = "Lydia"; year = "1998"; } const person = { name: "Sarah" }; const birthYear = "1997"; getInfo(person, birthYear); console.log(person, birthYear);

答案:{ name: "Lydia" }, "1997"

普通参数都是 值 传递的,而对象则不同,是 引用 传递。所以说,birthYear是值传递,因为他是个字符串而不是对象。当我们对参数进行值传递时,会创建一份该值的 复制 。(可以参考问题46)

变量birthYear有一个对"1997"的引用,而传入的参数也有一个对"1997"的引用,但二者的引用并不相同。当我们通过给 year赋值"1998"来更新year的值的时候我们只是更新了year(的引用)。此时birthYear仍然是"1997".

而person是个对象。参数member引用与之 相同的 对象。当我们修改member所引用对象的属性时,person的相应属性也被修改了,因为他们引用了相同的对象. person的 name属性也变成了 "Lydia".

16.输出是什么?

function Car() { this.make = "Lamborghini"; return { make: "Maserati" }; } const myCar = new Car(); console.log(myCar.make);

答案是:"Maserati"

返回属性的时候,属性的值等于 返回的 值,而不是构造函数中设定的值。我们返回了字符串 "Maserati",所以 myCar.make等于"Maserati".

17. 输出是什么?

(() => { let x = (y = 10); })(); console.log(typeof x); console.log(typeof y);

答案:"undefined", "number"

let x = y = 10; 是下面这个表达式的缩写:

y = 10; let x = y;

我们设定y等于10时,我们实际上增加了一个属性y给全局对象(浏览器里的window, Nodejs里的global)。在浏览器中, window.y等于10.

然后我们声明了变量x等于y,也是10.但变量是使用 let声明的,它只作用于 块级作用域, 仅在声明它的块中有效;就是案例中的立即调用表达式(IIFE)。使用typeof操作符时, 操作值 x没有被定义:因为我们在x声明块的外部,无法调用它。这就意味着x未定义。未分配或是未声明的变量类型为"undefined". console.log(typeof x)返回"undefined".

而我们创建了全局变量y,并且设定y等于10.这个值在我们的代码各处都访问的到。 y已经被定义了,而且有一个"number"类型的值。 console.log(typeof y)返回"number".

18.输出是什么?

const set = new Set([1, 1, 2, 3, 4]); console.log(set);

答案: {1, 2, 3, 4}

Set对象是独一无二的值的集合:也就是说同一个值在其中仅出现一次。

我们传入了数组[1, 1, 2, 3, 4],他有一个重复值1.以为一个集合里不能有两个重复的值,其中一个就被移除了。所以结果是 {1, 2, 3, 4}.

19. 输出是什么?

// counter.js let counter = 10; export default counter; // index.js import myCounter from "./counter"; myCounter += 1; console.log(myCounter);

答案:Error

引入的模块是 只读 的: 你不能修改引入的模块。只有导出他们的模块才能修改其值。

当我们给myCounter增加一个值的时候会抛出一个异常: myCounter是只读的,不能被修改。

20.输出是什么?

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.

21.输出是什么?

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方法添加的属性有了更多的控制权。

22. 输出是什么?

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)是个 数组 ,那么就只有包含在数组中的属性将会被转化为字符串。在本例中,只有名为"level" 和 "health" 的属性被包括进来, "username"则被排除在外。 data 就等于 "{"level":19, "health":90}".

而如果替代者(replacer)是个 函数,这个函数将被对象的每个属性都调用一遍。 函数返回的值会成为这个属性的值,最终体现在转化后的JSON字符串中(译者注:Chrome下,经过实验,如果所有属性均返回同一个值的时候有异常,会直接将返回值作为结果输出而不会输出JSON字符串),而如果返回值为undefined,则该属性会被排除在外。

23. 输出是什么?

let num = 10; const increaseNumber = () => num++; const increasePassedNumber = number => number++; const num1 = increaseNumber(); const num2 = increasePassedNumber(num1); console.log(num1); console.log(num2);

答案:10, 10

一元操作符 ++ 先返回 操作值, 再累加 操作值。num1的值是10, 因为increaseNumber函数首先返回num的值,也就是10,随后再进行 num的累加。

num2是10因为我们将 num1传入increasePassedNumber. number等于10(num1的值。同样道理,++ 先返回 操作值, 再累加 操作值。) number是10,所以num2也是10.

24.输出是什么?

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。

25. 输出是什么?

[1, 2, 3, 4].reduce((x, y) => console.log(x, y));

答案:1 2 and undefined 3 and undefined 4

reducer 函数接收4个参数:

Accumulator (acc) (累计器)Current Value (cur) (当前值)Current Index (idx) (当前索引)Source Array (src) (源数组)

reducer 函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。

reducer 函数还有一个可选参数initialValue, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供initialValue,则将使用数组中的第一个元素。

在上述例子,reduce方法接收的第一个参数(Accumulator)是x, 第二个参数(Current Value)是y。

在第一次调用时,累加器x为1,当前值“y”为2,打印出累加器和当前值:1和2。

例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回undefined。 在下一次调用时,累加器为undefined,当前值为“3”, 因此undefined和3被打印出。

在第四次调用时,回调函数依然没有返回值。 累加器再次为 undefined ,当前值为“4”。 undefined和4被打印出。

26. 输出是什么?

// 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会被依次打印。

27. 输出是什么?

console.log(Number(2) === Number(2)) console.log(Boolean(false) === Boolean(false)) console.log(Symbol('foo') === Symbol('foo'))

答案:true, true, false

每个symbol都是唯一的,传递给symbol的参数只是给symbol的一个描述。symbol的值不依赖于传递的参数。

28. 输出是什么?

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方法返回的一个挂起的promise,该promise还没有解决。

如果想要访问已解决的值“I made it !”,可以在data上使用.then方法:data.then(res => console.log(res)),这样就能打印“I made it !”

29.输出是什么?

function addToList(item, list) { return list.push(item); } const result = addToList("apple", ["banana"]); console.log(result);

答案:2

解析:push()方法返回新数组的长度,push方法修改原数组,如果想从函数返回数组而不是数组长度,应该在push item之后返回list 

30. 输出是什么?

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异常(最常见但不仅限于严格模式下时)。

31. 输出是什么?

const { name: myName } = { name: "Lydia" }; console.log(name);

答案:ReferenceError

解析:

当我们从右侧的对象解构属性name时,我们将其值Lydia分配给名为myName的变量。

使用{name:myName},我们是在告诉JavaScript我们要创建一个名为myName的新变量,并且其值是右侧对象的name属性的值。

当我们尝试打印name,一个未定义的变量时,就会引发ReferenceError。

32. 输出是什么?

const person = { name: "Lydia", age: 21 } let city = person.city city = "Amsterdam" console.log(person)

答案:{ name: "Lydia", age: 21 }

解析:

我们将变量city设置为等于person对象上名为city的属性的值。 这个对象上没有名为city的属性,因此变量city的值为undefined。

请注意,我们没有引用person对象本身,只是将变量city设置为等于person对象上city属性的当前值。

然后,我们将city设置为等于字符串“Amsterdam”。 这不会更改person对象:没有对该对象的引用。

因此打印person对象时,会返回未修改的对象。

33. 输出是什么?

// 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的名称及其对应的值。

34. 输出是什么?

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

解析:常规函数,例如giveLydiaPizza函数,有一个prototype属性,它是一个带有constructor属性的对象(原型对象)。 然而,箭头函数,例如giveLydiaChocolate函数,没有这个prototype属性。 尝试使用giveLydiaChocolate.prototype访问prototype属性时会返回undefined。

35. 输出是什么?

const person = { name: "Lydia", age: 21 } for (const [x, y] of Object.entries(person)) { console.log(x, y) }

答案:name Lydia and age 21

解析:

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。

36.输出是什么?

function getItems(fruitList, ...args, favoriteFruit) { return [...fruitList, ...args, favoriteFruit] } getItems(["banana", "apple"], "pear", "orange")

答案:SyntaxError

解析:

... args是剩余参数,剩余参数的值是一个包含所有剩余参数的数组,并且只能作为最后一个参数。上述示例中,剩余参数是第二个参数,这是不可能的,并会抛出语法错误。

function getItems(fruitList, favoriteFruit, ...args) { return [...fruitList, ...args, favoriteFruit] } getItems(["banana", "apple"], "pear", "orange")

37.输出是什么?

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 and b is bigger, undefined

解析:

在JavaScript中,我们不必显式地编写分号(;),但是JavaScript引擎仍然在语句之后自动添加分号。这称为自动分号插入。例如,一个语句可以是变量,或者像throw、return、break这样的关键字。

在这里,我们在新的一行上写了一个return语句和另一个值a + b 。然而,由于它是一个新行,引擎并不知道它实际上是我们想要返回的值。相反,它会在return后面自动添加分号。你可以这样看:

return; a + b

这意味着永远不会到达a + b,因为函数在return关键字之后停止运行。如果没有返回值,就像这里,函数返回undefined。注意,在if/else语句之后没有自动插入!

38. 输出是什么?

class Person { constructor() { this.name = "Lydia" } } Person = class AnotherPerson { constructor() { this.name = "Sarah" } } const member = new Person() console.log(member.name)

答案:"Sarah"

解析:

我们可以将类设置为等于其他类/函数构造函数。 在这种情况下,我们将Person设置为AnotherPerson。 这个构造函数的名字是Sarah,所以新的Person实例member上的name属性是Sarah。

39. 输出是什么?

const one = (false || {} || null) const two = (null || false || "") const three = ([] || 0 || true) console.log(one, two, three)

答案:{} “” []

解析:使用 || 运算符,可以返回第一个真值。如果所有的值是假值,则返回最后一个值

(false || {} || null):空对象{}是一个真值。 这是第一个(也是唯一的)真值,它将被返回。one等于{}。

(null || false ||“”):所有值都是假值。 这意味着返回传递的值""。 two等于""。

([] || 0 ||“”):空数组[]是一个真值。 这是第一个返回的真值。 three等于[]。

最新回复(0)