既上一篇 Function, 這一篇要來筆記 js garden 的 Array/Type, 當然一樣有看 Tommy 大大的影片導讀. 自己需要懂 js 英文專有名詞,所以有些會用英文寫囉
在 object 篇已經提到過包含繼承屬性的陣列,不要使用 for...in
因為 for in loop 會連上層的 chain 全部印出來, 唯一方法是使用 hasOwnProperty
過濾掉上層的屬性,但這樣會因此慢上 20 倍.新的方法可以用 for of 取代掉
1 2 3 4 5 6 7 8 9 |
Array.prototype.website = 'pjchender' var arr = ['John', 'Jane', 'Jim'] // 透過for...in輸出 // 發現連 prototype 繼承方法也被輸出 因為它會往上一直找 for (var prop in arr) { console.log(arr[prop]) } // 'John', 'Jane', 'Jim', 'pjchender' |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Solution 1 for (var prop in arr) { // 不是繼承而來的屬性,才輸出... if (arr.hasOwnProperty(prop)) { console.log(arr[prop]); } } // Solution 2 for (var prop of arr) { console.log(prop) } // Solution 3 classic for for (var i = 0; i < arr.length; i++) { console.log(arr[i]) } |
你會發現以下例子沒什麼特別的除了 l = list.length, 先抓住 list 的長度在效能上是有幫助的喔(筆記), 這樣不用每一次都去計算 list.length , 只要第一次計算就好. 但當然若在過程中有增減陣列裡的東西就不適合先存起來
1 2 3 4 |
var list = [1, 2, 3, 4, 5, ...... 100000000]; for(var i = 0, l = list.length; i < l; i++) { console.log(list[i]); } |
這蠻神奇的從來沒用過,原來 Array 的 length 是可以用來刪/加 array item 的
1 2 3 4 5 6 7 |
var arr = [1, 2, 3, 4, 5, 6]; arr.length = 3; arr; // [1, 2, 3] arr.length = 6; arr.push(4); arr; // [1, 2, 3, undefined, undefined, undefined, 4] |
這邊補充一個可以讓字串重覆 N 次的方法,es6 語意多了
1 2 3 4 5 6 |
// es5 // new Array(count +1).join('string') new Array(3+1).join('foo') // 'foofoofoo' // es6 'foo'.repeat(3) |
不要用 for in 以及用 classic for 時要 cache the length property
另外強烈建議用 array literal [] 建立 Array, 因為當你是用 new Array 建立,裡面又剛好是 Number 時, 他只會建立一個 length 為 這個 Number 的陣列,裡面值並不會被建立
1 2 |
[3]; // Result: [3] new Array(3); // Result: [ , , ] |
下面就是一連串會讓我覺得,疑為什麼是這樣的範例, 你也許會好奇到底是左邊轉成右邊型別還是右邊轉左邊型別(答案是不一定), 想知道規則的話可以看 ecma script 其實蠻有趣的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
"" == "0" // false new Number(10) == 10; // true, 10 === 10 Number object is converted // to a number primitive via implicit call of // Number.prototype.valueOf method isNaN(null) == false; // true, null converts to 0 // which of course is not NaN 0 == "" // true 0 === 0 0 == "0" // true 0 === 0 false == "false" // false 0 == "false" 0 === 1 false == "0" // true 0 == "0" 0 === 0 false == undefined // false 0 === undefined false == null // false 0 === null null == undefined // true true " \t\r\n" == 0 // true 10 == 010; // false 10 == '-10'; // false 10 === -10 // Constructors of Built-In Types // new Number() 換轉換成 Number 的 obj, // Number 只是轉換成數字的方法而已 new Number(10) === 10; // False, Object and Number Number(10) === 10; // True, Number and Number new Number(10) + 0 === 10; // True, due to implicit conversion |
1. 如果其中有一個值為「Boolean」的情況下,會將 true 轉型為「數字」的 1, false 則會變成數字的 0
2. 如果遇到字串與數字做比較的情況下,則會將字串透過 Number() 嘗試轉型為數字後,再進行比較。
3. 如果其中一方是 Object 型別,而另一方是 Primitives 型別的情況下,則會先透過物件的 valueOf() 方法取得對應的 Primitives 型別的值,再進行比較。 – 取自 kuro.tw 大大的結論
Equality ==
The Strict Equality Operator ===
1 2 3 4 5 6 7 8 9 |
"" === "0" // false 0 === "" // false 0 === "0" // false false === "false" // false false === "0" // false false === undefined // false false === null // false null === undefined // false " \t\r\n" === 0 // false |
1 2 3 4 5 |
{} === {}; // false new String('foo') === 'foo'; // false new Number(10) === 10; // false var foo = {}; foo === foo; // true |
除了會不會強制轉換型別以外, == 跟 === 運作原理是一樣的,除了在比較 Object (Array 也是 Obj 喔)
這不難懂有稍微深入 js 的都知道原始型別 Primitives 是 by value, Object 是 by reference 這個道理(可以參考這篇) 所以除非兩個 obj reference 是同一個不然比較之後一定是 false 的
Passing literals or non-object values will result in even more type coercion.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// Casting to a String '' + 10 === '10'; // true // Casting to a Number +'10' === 10; // true // Casting to a Boolean // By using the not operator twice, // a value can be converted to a boolean. !!'foo'; // true !!''; // false !!'0'; // true !!'1'; // true !!'-1' // true !!{}; // true !!true; // true |
作者說 typeof
跟 instanceof
是 js 上最大錯誤(疑 我還蠻常用的耶 XD),
自己覺得要搞清處再用.typeof 可檢查資料型別(尤其是 primitives 用 literal 建立時)
1 2 |
typeof 1 // Number typepf "Hi" // String |
當使用建構式建立字串、布林或數字等值時,建立的其實不是基本型別值而是物件,因此可用 instanceof 來檢查是由哪個建構式建立的,也可檢視該物件是屬於哪個子型別
1 2 3 |
new Number(1) instanceof Number // true new String(1) instanceof String // true [2] instanceof Array // true |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Value Class Type ------------------------------------- "foo" String string new String("foo") String object 1.2 Number number new Number(1.2) Number object true Boolean boolean new Boolean(true) Boolean object new Date() Date object new Error() Error object [1,2,3] Array object new Array(1, 2, 3) Array object new Function("") Function function /abc/g RegExp object (function in Nitro/V8) new RegExp("meow") RegExp object (function in Nitro/V8) {} Object object new Object() Object object |
instanceof Operator 用來檢測 constructor.prototype 是否存在於參數 object 的原型鏈上
1 2 3 4 5 6 7 8 9 10 11 |
function Foo() {} function Bar() {} Bar.prototype = new Foo(); new Bar() instanceof Bar; // true new Bar() instanceof Foo; // true // This just sets Bar.prototype to the function object Foo, // but not to an actual instance of Foo Bar.prototype = Foo; new Bar() instanceof Foo; // false |
1 2 3 4 5 6 7 |
const str = 'Hello World!'; str instanceof String // false str.length // 12 const strObj = new String('Hello World!'); strObj instanceof String // true strObj.length // 12 |
這邊建議用法是用 toString
1 2 3 4 5 |
let s = Object.prototype.toString; alert( s.call(123) ); // [object Number] alert( s.call(null) ); // [object Null] alert( s.call(alert) ); // [object Function] |