10个JavaScript测试问题和答案

77 阅读6分钟

我们可以挑战自己,使自己成长为JavaScript开发者的一个方法是用测验题来练习!下面的问题旨在挑战和指导我们的工作。下面的问题旨在具有挑战性和指导性。如果你确切地知道如何回答每一个问题,那很好,但是如果你错了一些,并且知道了你为什么会错,我认为那就更好了!

问题1:数组排序比较

考虑以下数组。在各种排序条件下,什么会被记录下来?

const arr1 = ['a', 'b', 'c'];
const arr2 = ['b', 'c', 'a'];

console.log(
  arr1.sort() === arr1,
  arr2.sort() == arr2,
  arr1.sort() === arr2.sort()
);

答案和解释

答案:真,真,假

这里有几个概念在起作用。首先,数组sort 方法对你的原始数组进行排序,同时返回该数组的引用。这意味着当你写arr2.sort()arr2 数组对象就被排序了。

然而,事实证明,在你比较对象时,数组的排序顺序并不重要。因为arr1.sort()arr1 在内存中指向同一个对象,所以第一个平等测试返回true 。这对第二次比较也是如此:arr2.sort()arr2 在内存中指向同一个对象。

在第三个测试中,arr1.sort()arr2.sort() 的排序是相同的;但是,它们仍然指向内存中的不同对象。因此,第三次测试的结果是:false

问题2:一个对象集

考虑以下Set 的对象分散到一个新的数组中。什么会被记录下来?

const mySet = new Set([{ a: 1 }, { a: 1 }]);
const result = [...mySet];
console.log(result);

答案和解释

答案:是的。 [{a: 1}, {a: 1}]

虽然一个Set 对象确实会删除重复的东西,但是我们创建Set 的两个值是对内存中不同对象的引用,尽管有相同的键值对。这与{ a: 1 } === { a: 1 }false 的原因相同。

应该注意的是,如果这个集合是用一个对象变量创建的,比如说obj = { a: 1 }new Set([ obj, obj ]) 将只有一个元素,因为数组中的两个元素都引用了内存中的同一个对象。

问题3:深度对象可变性

考虑以下对象,代表一个用户,Joe,和他的狗,Buttercup。我们使用Object.freeze 来保存我们的对象,然后尝试突变Buttercup的名字。什么会被记录下来?

const user = {
  name: 'Joe',
  age: 25,
  pet: {
    type: 'dog',
    name: 'Buttercup',
  },
};

Object.freeze(user);

user.pet.name = 'Daffodil';

console.log(user.pet.name);

答案和解释

答案:Daffodil

Object.freeze 将对一个对象进行浅层冻结,但不会保护深层属性不被变异。在这个例子中,我们将不能突变 ,但我们突变 是没有问题的。如果我们觉得需要保护一个对象不被 "一直向下 "突变,我们可以递归应用 ,或者使用一个现有的 "深度冻结 "库。user.age user.pet.name Object.freeze

问题4:原型继承

在这个问题中,我们有一个Dog 的构造函数。我们的狗显然知道说话的命令。在下面的例子中,当我们要求Pogo说话时,什么会被记录下来?

function Dog(name) {
  this.name = name;
  this.speak = function () {
    return 'woof';
  };
}

const dog = new Dog('Pogo');

Dog.prototype.speak = function () {
  return 'arf';
};

console.log(dog.speak());

答案和解释

答案:woof

每次我们创建一个新的Dog 实例时,我们都会将该实例的speak 属性设置为一个返回字符串woof 的函数。由于每次我们创建一个新的Dog 实例时都会设置这个属性,解释器就不必在原型链上找一个speak 属性。因此,Dog.prototype.speak 上的speak 方法从未被使用。

问题5:Promise.all Resolve Order

在这个问题中,我们有一个timer 函数,该函数返回一个Promise ,该函数在一个随机的时间后解析。我们用Promise.all 来解决一个数组的timers 。什么会被记录下来,还是随机的?

const timer = (a) => {
  return new Promise((res) =>
    setTimeout(() => {
      res(a);
    }, Math.random() * 100)
  );
};

const all = Promise.all([timer('first'), timer('second')]).then((data) =>
  console.log(data)
);

答案和解释

答案:是的。 ["first", "second"]

Promises解析的顺序对Promise.all 并不重要。我们可以可靠地指望它们以数组参数中提供的相同顺序返回。

问题6:减少数学

数学时间!什么会被记录下来?

const arr = [(x) => x * 1, (x) => x * 2, (x) => x * 3, (x) => x * 4];

console.log(arr.reduce((agg, el) => agg + el(agg), 1));

答案和解释

答案是120

使用Array#reduce ,聚合器的初始值(这里命名为agg )在第二个参数中给出。在这种情况下,就是1 。然后我们可以对我们的函数进行如下迭代。

1 + 1 * 1 = 2(聚合器在下一次迭代中的值)
2 + 2 * 2 = 6(聚合器在下一次迭代中的值)
6 + 6 * 3 = 24(聚合器在下一次迭代中的值)
24 + 24 * 4 = 120(最终值)。

那么,就是120了!

问题7:短路通知(s)

让我们给我们的用户显示一些通知!在下面的片段中,什么会被记录下来?

const notifications = 1;

console.log(
  `You have ${notifications} notification${notifications !== 1 && 's'}`
);

答案和解释

答案:"你有1个通知false"

不幸的是,我们的短路评估在这里不会如愿:notifications !== 1 && 's' 评估为false ,这意味着我们实际上将记录You have 1 notificationfalse 。如果我们想让我们的片段正确工作,我们可以考虑使用条件运算符:${notifications === 1 ? '' : 's'}

问题8:传播和重命名

考虑下面这个带有单个对象的数组。当我们展开该数组并改变0-index对象的firstName 属性时会发生什么?

const arr1 = [{ firstName: 'James' }];
const arr2 = [...arr1];
arr2[0].firstName = 'Jonah';

console.log(arr1);

答案和解释

答案:传播 [{ firstName: "Jonah" }]

展开创建了一个数组的浅层拷贝,意味着arr2 中包含的对象仍然指向内存中arr1 对象所指向的同一个对象。所以,改变一个数组中的对象的firstName 属性,会被另一个数组中的对象也反映出来。

问题9:数组方法绑定

在下面的情况下,什么会被记录下来?

const map = ['a', 'b', 'c'].map.bind([1, 2, 3]);
map((el) => console.log(el));

答案和解释

答案:1 2 31 2 3

['a', 'b', 'c'].map this ,当被调用时,将调用Array.prototype.map ,其值为['a', 'b', 'c'] 。但是,当作为一个引用,而不是被调用时,['a', 'b', 'c'].map 只是一个对Array.prototype.map 的引用。

Function.prototype.bind 将把函数的 绑定到第一个参数上(在这个例子中,就是 ),用这样的 ,调用 ,结果是这些项目被迭代和记录。this [1, 2, 3] this Array.prototype.map

问题10:集合唯一性和排序

在下面的问题中,我们使用Set 对象和传播语法来创建一个新的数组。什么会被记录下来(要考虑:项目是否被强制要求是唯一的? 它们是否被排序?)

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

答案和解释

答案:4 2

Set 对象会强制要求元素唯一(已经在集合中的重复元素会被忽略),但不会改变顺序。结果arr 数组将是[3, 1, 2, 4] ,意味着arr.length4arr[2] (数组的第三个元素)是2