簡介
基本上所有的程式設計師都使用過javascript,我們在web中使用javascript,我們在伺服器端使用nodejs,js給大家的第一映像就是簡單,但是可能並不是所有人都系統的瞭解過js中的內建物件和資料結構。
今天,一起來看看吧。
基礎型別js是一種弱型別的動態語言,雖然是弱型別的,但是js本身定義了很多種資料型別。
js中有7種基礎型別:分別是undefined,Boolean,Number,String,BigInt,Symbol和null。
undefinedundefined會自動賦值給剛剛宣告的變數。舉個例子:
var x; //create a variable but assign it no valueconsole.log("x's value is", x) //logs "x's value is undefined"
Boolean和Boolean物件
Boolean的值就是true 或者 false。
除了基礎型別的Boolean值外,還有一個Boolean物件,用來封裝boolean值。
如果是用new Boolean來構造Boolean物件的話,下面的例子中Boolean的初始值都是false:
var bNoParam = new Boolean();var bZero = new Boolean(0);var bNull = new Boolean(null);var bEmptyString = new Boolean('');var bfalse = new Boolean(false);
下面boolean物件的初始值都是true:
var btrue = new Boolean(true);var btrueString = new Boolean('true');var bfalseString = new Boolean('false');var bSuLin = new Boolean('Su Lin');var bArrayProto = new Boolean([]);var bObjProto = new Boolean({});
注意,我們不要使用Boolean物件來進行if條件的判斷,任何Boolean物件,即使是初始值是false的Boolean物件,if條件判斷,都是true:
var x = new Boolean(false);if (x) { // this code is executed}var x = false;if (x) { // this code is not executed}
如果非要使用if條件判斷,我們可以使用Boolean函式或者!!如下所示:
var x = Boolean(expression); // use this...var x = !!(expression); // ...or thisvar x = new Boolean(expression); // don't use this!
Number和BigInt
Number和BigInt是JS中的兩個數字型別,其中Number表示的雙精度64位二進位制格式,其範圍是-(253 − 1) and 253 − 1.
除此之外,Number還有三個值:+Infinity, -Infinity, 和 NaN。
前面兩個表示的是正負最大值。NaN表示的是 Not-A-Number。
我們可以透過isNaN來判斷是否是一個Number:
function sanitise(x) { if (isNaN(x)) { return NaN; } return x;}console.log(sanitise('1'));// expected output: "1"console.log(sanitise('NotANumber'));// expected output: NaN
BigInt表示任意精度的整數,使用BigInt可以進行超出Number精度整數的運算。
我們可以透過在整數後面加上n來表示BigInt。
> const x = 2n ** 53n;9007199254740992n> const y = x + 1n; 9007199254740993n
注意,和Boolean一樣,Number和BitInt也有wrapper物件型別。
看下Number的wrapper:
Number('123') // returns the number 123Number('123') === 123 // trueNumber("unicorn") // NaNNumber(undefined) // NaN
看下BitInt的wrapper型別:
const theBiggestInt = 9007199254740991nconst alsoHuge = BigInt(9007199254740991)// ↪ 9007199254740991nconst hugeString = BigInt("9007199254740991")// ↪ 9007199254740991nconst hugeHex = BigInt("0x1fffffffffffff")// ↪ 9007199254740991nconst hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111")// ↪ 9007199254740991n
Stringjs中的String是不可變的,同樣的String基礎型別也有和它對應的String wrapper物件。
String基礎型別和不使用new的String函式是一致的:
const string1 = "A string primitive";const string2 = String('A string primitive');
上面兩個String是一致的。但是如果使用new來構造String物件,那麼兩者是不一樣的:
let s_prim = 'foo'let s_obj = new String(s_prim)console.log(typeof s_prim) // Logs "string"console.log(typeof s_obj) // Logs "object"let s1 = '2 + 2' // creates a string primitivelet s2 = new String('2 + 2') // creates a String objectconsole.log(eval(s1)) // returns the number 4console.log(eval(s2)) // returns the string "2 + 2"
我們可以透過String物件的valueOf()方法,獲得其String基礎型別。
SymbolSymbol是一個唯一的不可變的基礎型別,一般用在物件的key中。
// Here are two symbols with the same description:let Sym1 = Symbol("Sym")let Sym2 = Symbol("Sym")console.log(Sym1 === Sym2) // returns "false"
Symbol是不支援new操作的:
let sym = new Symbol() // TypeError
如果你真的想建立Symbol物件,則可以使用Object():
let sym = Symbol('foo')typeof sym // "symbol" let symObj = Object(sym)typeof symObj // "object"
null
null表示引用的是無效的Object物件或者地址。
雖然null可以看做是primitive,但是null其實是一個Object,所有的物件都來自null:
typeof null === 'object' // true
ObjectObject是js中的一種資料型別,幾乎所有的物件都繼承自Object,它儲存的是key-value形式的資料,我們可以透過使用Ojbect()方法或者new Object()或者Object字面量的方式來建立Object。
let o = {}let o = {a: 'foo', b: 42, c: {}}let a = 'foo', b = 42, c = {}let o = {a: a, b: b, c: c}
注意使用Object()或者new Object()是一樣的效果,都會得到一個Object物件。
在ES2015之後,我們還可以使用動態的物件屬性:
let param = 'size'let config = { [param]: 12, ['mobile' + param.charAt(0).toUpperCase() + param.slice(1)]: 4}console.log(config) // {size: 12, mobileSize: 4}
Function
Function也是一個Object,JS中的所有函式都是Function物件。
(function(){}).constructor === Function
那麼透過Function建構函式和function函式定義創建出來的函式有什麼區別呢?
使用new Function建立的函式,其作用域範圍是global,我們看一下具體的例子:
var x = 10;function createFunction1() { var x = 20; return new Function('return x;'); // this |x| refers global |x|}function createFunction2() { var x = 20; function f() { return x; // this |x| refers local |x| above } return f;}var f1 = createFunction1();console.log(f1()); // 10var f2 = createFunction2();console.log(f2()); // 20
Date
Date是js中用來操作時間的Object。我們看下Date的常用例子:
let today = new Date()let birthday = new Date('December 17, 1995 03:24:00')let birthday = new Date('1995-12-17T03:24:00')let birthday = new Date(1995, 11, 17) // the month is 0-indexedlet birthday = new Date(1995, 11, 17, 3, 24, 0)let birthday = new Date(628021800000) // passing epoch timestamplet [month, date, year] = ( new Date() ).toLocaleDateString().split("/")let [hour, minute, second] = ( new Date() ).toLocaleTimeString().slice(0,7).split(":")
ArrayJS內建了很多種不同型別的Array,最常用的就是Array字面量和Array Object。
我們看下怎麼建立一個Array:
let fruits = ['Apple', 'Banana'];console.log(fruits.length); // 2console.log(fruits[0]); // "Apple"let fruits = new Array('Apple', 'Banana');console.log(fruits.length); // 2console.log(fruits[0]); // "Apple"
遍歷Array:
let fruits = ['Apple', 'Banana']fruits.forEach(function(item, index, array) { console.log(item, index)})// Apple 0// Banana 1
新增Item到Array:
let first = fruits.shift() // remove Apple from the front// ["Banana"]
從前面新增item:
let newLength = fruits.unshift('Strawberry') // add to the front// ["Strawberry", "Banana"]
刪除某個index的item:
let vegetables = ['Cabbage', 'Turnip', 'Radish', 'Carrot']console.log(vegetables)// ["Cabbage", "Turnip", "Radish", "Carrot"]let pos = 1let n = 2let removedItems = vegetables.splice(pos, n)// this is how to remove items, n defines the number of items to be removed,// starting at the index position specified by pos and progressing toward the end of array.console.log(vegetables)// ["Cabbage", "Carrot"] (the original array is changed)console.log(removedItems)// ["Turnip", "Radish"]
複製array:
let shallowCopy = fruits.slice() // this is how to make a copy// ["Strawberry", "Mango"]
除了Array之外,JS還內建了特定型別的Array:
Int8ArrayUint8ArrayUint8ClampedArrayInt16ArrayUint16ArrayInt32ArrayUint32ArrayFloat32ArrayFloat64ArrayBigInt64ArrayBigUint64Array這些特定型別的Array中只能儲存特定型別的值。
Keyed collections除了陣列之外,JS中還有key-value的集合,比如:Map,Set,WeakMap和WeakSet。
對Map來說,我們可以透過使用set,get,has,delete等犯法來對Map進行操作:
let contacts = new Map()contacts.set('Jessie', {phone: "213-555-1234", address: "123 N 1st Ave"})contacts.has('Jessie') // truecontacts.get('Hilary') // undefinedcontacts.set('Hilary', {phone: "617-555-4321", address: "321 S 2nd St"})contacts.get('Jessie') // {phone: "213-555-1234", address: "123 N 1st Ave"}contacts.delete('Raymond') // falsecontacts.delete('Jessie') // trueconsole.log(contacts.size) // 1
遍歷Map:
let myMap = new Map()myMap.set(0, 'zero')myMap.set(1, 'one')for (let [key, value] of myMap) { console.log(key + ' = ' + value)}// 0 = zero// 1 = onefor (let key of myMap.keys()) { console.log(key)}// 0// 1for (let value of myMap.values()) { console.log(value)}// zero// onefor (let [key, value] of myMap.entries()) { console.log(key + ' = ' + value)}// 0 = zero// 1 = one
使用forEach來遍歷map:
myMap.forEach(function(value, key) { console.log(key + ' = ' + value)})// 0 = zero// 1 = one
Set中儲存的是唯一的物件。
我們看下Set的操作:
let mySet = new Set()mySet.add(1) // Set [ 1 ]mySet.add(5) // Set [ 1, 5 ]mySet.has(1) // truemySet.delete(1) // removes 1 from the set
set的遍歷:
// logs the items in the order: 1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2} for (let item of mySet) console.log(item)
WeakMap,WeakSet和Map於Set的區別在於,WeakMap的key只能是Object物件,不能是基本型別。
為什麼會有WeakMap呢?
這種實現有兩個缺點,第一個缺點是每次查詢的時候都需要遍歷key的陣列,然後找到對應的index,再透過index來從第二個陣列中查詢value。
第二個缺點就是key和value是強繫結的,即使key不再被使用了,也不會被垃圾回收。
所以引入了WeakMap的概念,在WeakMap中,key和value沒有這樣的強繫結關係,key如果不再被使用的話,可以被垃圾回收器回收。
因為引用關係是weak的,所以weakMap不支援key的遍歷,如果你想遍歷key的話,請使用Map。
本文連結:http://www.flydean.com/js-built-in-objects-structures/