0%

物件 object

物件是一批相關的數據以及/或者功能(通常包含了幾個變數及函式 — 當它們包含在物件中時被稱做「屬性」(properties)或「函式」(methods))。

物件結構

物件宣告方式有兩種

  1. 物件實字(Object Literals)
    宣告一個物件名稱,並於賦值運算子後方填入{}作為區塊宣告。
var 物件名稱 = {}
  • 物件本身的組成是由一個 {屬性(property) : 值(value)} 組成的,屬性的值可以是任何合法的值,可以包含陣列、函式或其他物件。
  • 如果是一般的值的情況,稱為"屬性(property, prop)",如果是一個函式,稱之為"方法(method)"。屬性與方法我們通常合稱為物件中的成員(member)。
  • 物件中的每一個成員(member)須以,相隔。
  • 屬性定義上一律為字串,固可使用特殊字元。
var object = {
properties: value, //屬性: 值(可為number or string)
array: [],
object: {},
holloworld: function() { //方法: 值
console.log('Hello World')
}
}

屬性名稱(鍵)中也不要使用保留字,請使用合法的變數名稱

2. 建構式方式 通常用於建構新物件中的屬性。
var 物件名稱 = new Object();

多數情況請使用物件實字(Object Literals)宣告物件

參考:
從ES6開始的JavaScript學習生活-物件

物件取值、新增、刪除

物件取值取值、新增、刪除有以下兩種,分別為

  1. 點記法 (Dot notation)
    可透過點記法 (Dot notation) 存取物件的屬性與函式。
    物件名稱作為命名空間 (Namespace),接著寫一個"."以及想取值、新增、刪除的屬性 or 方法名稱。
    點記法只可接受字串表示的成員名稱,不能是指向變數名稱。
  • 取值
    物件名稱.屬性名稱
    物件名稱.子物件名稱.屬性名稱 //巢狀物件
    物件名稱.方法名稱() //函式取值
  • 更新(新增)
    透過賦值運算子將值賦予現有屬性與函式的數值,也可以建立全新的成員。
    物件名稱.屬性名稱 = 值
    物件名稱.方法名稱 = function() {console.log('hello world')} //新增函式
  • 刪除
    使用一元運算子delete刪除現有屬性與函式的數值。
delete 物件名稱.屬性名稱
delete 物件名稱.方法名稱 //刪除函式
  1. 括弧記法 (Bracket notation)
    括弧記法 (Bracket notation) 是另一種存取物件屬性的方法。
    將要存取屬性名稱使用"[]"包覆起來。
    如屬性名稱為變數值可使用括弧記法。
  • 取值
    物件名稱['屬性名稱']
    物件名稱[變數名稱]
    物件名稱[方法名稱]() //函式取值
  • 新增
    透過賦值運算子將值賦予現有屬性與函式的數值,也可以建立全新的成員,與點記法相同。
    物件名稱['屬性名稱'] = 值
    物件名稱[變數名稱] = 變數值
    物件名稱['方法名稱']() //函式
  • 刪除
    使用一元運算子delete刪除現有屬性與函式的數值,與點記法相同。
delete 物件名稱['屬性名稱']
delete 物件名稱['方法名稱'] //函式

此方式與陣列取值方式相似,陣列取值使用指數 (index number) 選擇項目;而括弧記法 (Bracket notation)取值是透過各成員數值相關的名稱來選擇項目。

補充:
變數無法被刪除,屬性才能被刪除。
使用 var 宣告變數,此變數無法被刪除。
如直接使用賦值運算子賦予一個值,類似於全域變數下新增一個屬性window.property = value

var a = 1        //宣告變數  a = 1
aa = 1 //類似windows.a = 1
delete a //false
console.log(a) //1
delete aa //true
console.log(aa) //aa is not defined

物件與純值

  • 純值(Primitive Types)又稱為原始型別,可參考:。
    • 使用建立的純值無法新增屬性。
    var name = '小明'
    console.log(name) //小明
    name.age = 30
    console.log(name) //小明
    • 使用建構式建立則變為物件型別,便可以新增屬性。
    var newName = new String('小明')
    console.log(newName)); //String {"小明"}
    newName.age = 30;
    console.log(newName); //String {"小明", age: 30}
  • 未定義的物件屬性預設值
    物件下未定義屬性的值為undefined,並於undefined下無法新增任何屬性。

物件的參考特性

  • 傳值(Call By Value):
    傳到 另一個記憶體位置 上。

    當宣告一個 primitive type 的變數時(如:數字、字串、布林),假設把 preson 指定成一個 primitive type 的時候,preson 會在記憶體中存在一個自己的位置(0x01)。當指定另一個變數 preson2 = preson 的時候,preson2 實際上會建立另一個獨立的記憶體位置(假設叫做0x02),接著再把 preson 的值存在這個獨立的記憶體位置。
    preson 和 preson2 其實是存在於兩個不同的記憶體位置,因此彼此並不會乎相干擾影響,當修改preson2的值時,preson仍不會變。

在 JavaScript 中 primitive type(Boolean, String, Number, null, undefined)都屬於 Call By Value。

  • 傳參考
    是把記憶體位置 傳到 另一個記憶體位置 上。

    當建立一個 preson 為 Object(或function) 時,preson 會在記憶體中建立一個自己的位置(0x01)。當指定另一個變數 preson2 = preson 的時候,這時候並不會給 preson2 一個新的位置,而是一樣指定到物件 preson 的位置(即0x01)。
    物件 preson 和 preson2 都會被指稱到相同的位置(即0x01),因此,當 preson 的值改變的時候 preson 的值也會改變。

物件參考觀念-延伸範例

Call by Reference 還是 Call by Sharing

  • 傳參照呼叫(Call by reference)

傳遞給函式的是它的實際參數的隱式參照而不是實參的拷貝。

  • 傳共享物件呼叫(Call by sharing)

與傳參照呼叫不同,對於呼叫者而言在被呼叫函數裡修改參數是沒有影響的。

參考:求值策略- 维基百科

淺層複製及深層複製

  • 淺層複製(Shallow Copy)

只能達到淺層的複製(第一層),若有第二層以上的資料的話,
就無法達到實際的複製,而是會與舊物件一起共用同一塊記憶體。

有下列3種方式

  • for () { ... in ... }
var family = {
name: '小明家',
members: {
father: '老爸',
mon: '老媽',
ming: '小明'
}
};
var newFamily = {} //建立一個新物件
for (var key in family) {
newFamily[key] = family[key]
}
console.log(family === newFamily) //false (物件第一層已指向不同位址)
console.log(newFamily) //{name: "小明家", merbers: {...}}
newFamily.name = "大明家"
console.log(newFamily) //{name: "大明家", merbers: {...}}

console.log(family.members === newFamily.members) //true (物件第二層仍指向相同位址)
  • jQuery
var newFamily2 = jQuery.extend({}, family)
  • ES6
var newFamily3 = Object.assign({}, family)
  • 展開運算子(Spread Operator)
var newFamily4 = {...family}
  • 深層複製(Deep Copy)

會另外創造一個一模一樣的物件,新物件跟原物件共用記憶體,修改新物件不會改到原物件。

  • 轉成 JSON 再轉回來
    JSON.stringify把物件轉成字串,再用JSON.parse把字串轉成新的物件。
    缺點:繼承的屬性會遺失。
var newFamily5 = JSON.parse(JSON.stringify(family))
console.log( family === newFamily5 ) //false
console.log( family.members === newFamily5.members ) //false
  • Lodash
    另外一個很熱門的JS函式庫 lodash,提供_.cloneDeep用來做 Deep Copy。
    lodash
    API
var newFamily6 = _.cloneDeep(family);
console.log( family === newFamily6 ) //false
console.log( family.members === newFamily6.members ) //false

陣列(Array)

陣列是一種有順序的複合式的資料結構,用於定義、存放複數的資料類型,陣列是用來把複數的值存進一個變數裡,可以是原始的資料類型、其他陣列、函式等等。

註: 雖然陣列資料類型是屬於物件,但Array這個包裝物件的 typeof Array 也是回傳’function’。
定義方式

  • 一:
    將複數的值以 , 隔開並使用方括號 [] 包覆存進一個變數內。
var newArray = [1, 'string', function(){console.log('hello world')}, true, {}]
  • 建構式:
    這種方式並不建議使用,使用此語法在分配後,會把長度值(成員個數)固定住,除非你百分之百確定陣列裡面的成員個數,不然千萬不要用。
//警告: 不要使用這種定義方式
var aArray = new Array(10) //預先分配的定義
var bArray = new Array(1,2,3) //定義三個陣列值

註: JavaScript的內建物件都不建議new作初始定義的。不過有一些特例是一定要的,例如Date、Error等等。
每一個陣列中的元素,都附有一個能讀相對應元素的數字,稱做數字索引(numeric index)。
是從0開始的順序整數值,可以用方括號 [] 來取得成員的指定值,也可以用這個方式來改變成員包含的值。

console.log(newArray[0])    //1
console.log(newArray[1]) //string
console.log(newArray[2]()) //hello world
console.log(newArray[3]) //true

注意事項

  • 多維陣列
    多維陣列指的是"陣列中的陣列"結構,只是在陣列中保存其他的陣列值。
    維數過多時在處理上會愈複雜,一般常見的只有二維。
var newArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
  • 儲存多種資料類型
  • 多洞的陣列(Holey Arrays)或稀疏陣列(Sparse Arrays)
    在定義陣列時,它的陣列值和索引(index)並沒有塞滿,或是從一個完整的陣列刪除其中一個(留了個空位)。
var newArray = []