您好,登錄后才能下訂單哦!
這篇文章運用簡單易懂的例子給大家介紹JavaScript中常見的易錯題,代碼非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
1. 數組排序比較
看以下數組,在各種排序操作后都下輸出什么?
const arr1 = ['a', 'b', 'c']; const arr2 = ['b', 'c', 'a']; console.log( arr1.sort() === arr1, arr2.sort() == arr2, arr1.sort() === arr2.sort() );
答案和解析
答案: true, true, false
這里有幾個概念在起作用。首先,array 的 sort
方法對原始數組進行排序,并返回對該數組的引用。這意味著當你調用 arr2.sort()
時,arr2
數組內的對象將會被排序。
當你比較對象時,數組的排序順序并不重要。由于 arr1.sort()
和 arr1
指向內存中的同一對象,因此第一個相等測試返回 true
。第二個比較也是如此:arr2.sort()
和 arr2 指向內存中的同一對象。
在第三個測試中,arr1.sort()
和 arr2.sort()
的排序順序相同;但是,它們指向內存中的不同對象。因此,第三個測試的評估結果為 false
。
2. set 的對象
把下面的 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 的 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
或使用現有的“深度凍結”庫。
4. 原型繼承
在下面的代碼中,有一個 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
方法。
5. Promise.all 的解決順序
在這個問題中,我們有一個 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 無關。我們能夠可靠地依靠它們按照數組參數中提供的相同順序返回。
6. Reduce 數學
數學時間!輸出什么?
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. 短路通知(Short-Circuit Notification(s))
讓我們向用戶顯示一些通知。以下代碼段輸出了什么?
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'}
。
8. 展開操作和重命名
查看以下代碼中有單個對象的數組。當我們擴展該數組并更改 0 索引對象上的 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 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
將會導致這些項目被迭代并輸出。
10. set 的唯一性和順序
在下面的代碼中,我們用 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
。
關于JavaScript的易錯題和答案解析就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。