做者:Nick Scialli翻译:疯狂的技术宅javascript
原文:https://typeofnan.dev/10-java...前端
未经容许严禁转载java
刷题是咱们提升本身技术的一种好方法。下面的问题颇有挑战性和“指导性”。若是你知道该怎样回答,那意味着本身的水平很好,可是若是你发现本身答错了,并可以搞清楚为何错,我认为那会更好!程序员
看如下数组,在各类排序操做后都下输出什么?面试
const arr1 = ['a', 'b', 'c']; const arr2 = ['b', 'c', 'a']; console.log( arr1.sort() === arr1, arr2.sort() == arr2, arr1.sort() === arr2.sort() );
答案: true, true, falsesegmentfault
这里有几个概念在起做用。首先,array 的 sort
方法对原始数组进行排序,并返回对该数组的引用。这意味着当你调用 arr2.sort()
时,arr2
数组内的对象将会被排序。数组
当你比较对象时,数组的排序顺序并不重要。因为 arr1.sort()
和 arr1
指向内存中的同一对象,所以第一个相等测试返回 true
。第二个比较也是如此:arr2.sort()
和 arr2 指向内存中的同一对象。服务器
在第三个测试中,arr1.sort()
和 arr2.sort()
的排序顺序相同;可是,它们指向内存中的不一样对象。所以,第三个测试的评估结果为 false
。微信
把下面的 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])
将会只有一个元素,由于数组中的两个元素都引用了内存中的同一对象。
下面的对象表明用户 Joe 和他的狗 Buttercup。咱们用 Object.freeze
保存对象,而后尝试更改 Buttercup 的 name。最后会输出什么?
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
或使用现有的“深度冻结”库。
在下面的代码中,有一个 Dog
构造函数。咱们的 dog 显然有 speak 这个操做。当咱们调用 Pogo 的 speak 时,会输出什么?
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
方法。
在这个问题中,咱们有一个 timer
函数,它返回一个 Promise
,该 Promise 在随机时间后解析。咱们用 Promise.all
解析一系列的 timer
。最后的输出是什么,是随机的吗?
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"]
Promise 解决的顺序与 Promise.all 无关。咱们可以可靠地依靠它们按照数组参数中提供的相同顺序返回。
数学时间!输出什么?
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。
让咱们向用户显示一些通知。如下代码段输出了什么?
const notifications = 1; console.log( `You have ${notifications} notification${notifications !== 1 && 's'}` );
答案:“You have 1 notificationfalse”
不幸的是,咱们的短路评估将没法按预期工做: notifications !== 1 && 's'
评估为 false
,这意味着咱们实际上将会输出 You have 1 notificationfalse
。若是但愿代码段正常工做,则能够考虑条件运算符: ${notifications === 1 ? '' : 's'}
。
查看如下代码中有单个对象的数组。当咱们扩展该数组并更改 0 索引对象上的 firstname
属性时会发生什么?
const arr1 = [{ firstName: 'James' }]; const arr2 = [...arr1]; arr2[0].firstName = 'Jonah'; console.log(arr1);
答案: [{ firstName: "Jonah" }]
展开操做符会建立数组的浅表副本,这意味着 arr2
中包含的对象与 arr1
所指向的对象相同。因此在一个数组中修改对象的 firstName
属性,也将会在另外一个数组中更改。
在如下状况下会输出什么?
const map = ['a', 'b', 'c'].map.bind([1, 2, 3]); map(el => console.log(el));
答案: 1 2 3
当 ['a', 'b', 'c'].map
被调用时,将会调用 this'
值为 '['a','b','c']
的 Array.prototype.map
。可是当用做 引用
时, Array.prototype.map
的引用。
Function.prototype.bind
会将函数的 this
绑定到第一个参数(在本例中为 [1, 2, 3]
),用 this
调用Array.prototype.map
将会致使这些项目被迭代并输出。
在下面的代码中,咱们用 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.length
为 4
,且 arr[2]
(数组的第三个元素)为 2
。