做者: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
。