Objects

JavaScript 有 7 種資料類型,6 種只能儲存一個值,但物件能儲存多個 key: value,key 為 string 或 symbol,value 可以為任何值。

let user = new Object(); // "object constructor" syntax
let user = {};  // "object literal" syntax

Literals and properties

let user = {     // an object
  name: "John",  // by key "name" store value "John"
  age: 30,        // by key "age" store value 30
  "likes birds": true,  // multiword property name must be quoted 最後一個加上,
};

// get fields of the object:
alert( user.name ); // John
alert( user.age ); // 30

// 刪除物件資料
delete user.age;

Square brackets

let user = {};

// this would give a syntax error
user.likes birds = true

// set
user["likes birds"] = true;

// get
alert(user["likes birds"]); // true

// delete
delete user["likes birds"];

let key = "likes birds";

// same as user["likes birds"] = true;
user[key] = true;

// key obtain property name as a result of expression
let user = {
  name: "John",
  age: 30
};

let key = prompt("What do you want to know about the user?", "name");

// access by variable
alert( user[key] ); // John (if enter "name")

Computed properties

let fruit = prompt("Which fruit to buy?", "apple");

let bag = {
  [fruit]: 5, // the name of the property is taken from the variable fruit
};

alert( bag.apple ); // 5 if fruit="apple"

// same
let fruit = prompt("Which fruit to buy?", "apple");
let bag = {};

// take property name from the fruit variable
bag[fruit] = 5;

// more complex expression
let fruit = 'apple';
let bag = {
  [fruit + 'Computers']: 5 // bag.appleComputers = 5
};

Reserved words are allowed as property names

任意的名稱都可以作為屬性名稱,只有 _proto_ 不行。

let obj = {
  for: 1,
  let: 2,
  return: 3
};

alert( obj.for + obj.let + obj.return );  // 6

let obj = {};
obj.__proto__ = 5;
alert(obj.__proto__); // [object Object], didn't work as intended

Property value shorthand

通常會使用現存變數的名稱作為 key,可以簡寫為一個值,如下範例。

function makeUser(name, age) {
  return {
    name: name,
    age: age
    // ...other properties
  };
}

let user = makeUser("John", 30);
alert(user.name); // John

// shorthand
function makeUser(name, age) {
  return {
    name, // same as name: name
    age   // same as age: age
    // ...
  };
}

// both use normal and shorthands
let user = {
  name,  // same as name:name
  age: 30
};

Existence check

要取得物件不存在的屬性不會出現錯誤,會返回 undefined,可以用來檢測屬性是否存在;也可以用 in 檢查。

let user = {};

alert( user.noSuchProperty === undefined ); // true means "no such property"

// special operator in
let user = { name: "John", age: 30 };

alert( "age" in user ); // true, user.age exists
alert( "blabla" in user ); // false, user.blabla doesn't exist

// omit ""
let user = { age: 30 };

let key = "age";
alert( key in user ); // true, takes the name from key and checks for such property

// special condition if value = undefined
let obj = {
  test: undefined
};

alert( obj.test ); // it's undefined, so - no such property?

alert( "test" in obj ); // true, the property does exist!

The “for…in” loop

物件的迴圈不適用 for { ; ; } 而是 for { in }

for (key in object) {
  // executes the body for each key among object properties
}

let user = {
  name: "John",
  age: 30,
  isAdmin: true
};

for (let key in user) {
  // keys
  alert( key );  // name, age, isAdmin
  // values for the keys
  alert( user[key] ); // John, 30, true
}

Ordered like an object

整數的屬性依照大小排列,其他的屬性依寫得先後順序排列。

let codes = {
  "49": "Germany",
  "41": "Switzerland",
  "44": "Great Britain",
  // ..,
  "1": "USA"
};

for (let code in codes) {
  alert(code); // 1, 41, 44, 49
}

// Math.trunc is a built-in function that removes the decimal part
alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property
alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property
alert( String(Math.trunc(Number("1.2"))) ); // "1", not same "1.2" ⇒ not integer property

let codes = {
  "+49": "Germany",
  "+41": "Switzerland",
  "+44": "Great Britain",
  // ..,
  "+1": "USA"
};

for (let code in codes) {
  alert( +code ); // 49, 41, 44, 1
}

let user = {
  name: "John",
  surname: "Smith"
};
user.age = 25; // add one more

// non-integer properties are listed in the creation order
for (let prop in user) {
  alert( prop ); // name, surname, age
}

Copying by reference

原始值會複製一樣的值進去,物件會指到相同的記憶體,只要有一個物件的值改變,所有指向相同記憶體的物件都會改變

let message = "Hello!";
let phrase = message;

let user = { name: "John" };
let admin = user; // copy the reference
admin.name = 'Pete'; // changed by the "admin" reference
alert(user.name); // 'Pete', changes are seen from the "user" reference

Comparison by reference

== 跟 === 對物件來說是一樣的,只有當 2 個變數都只到相同記憶體時才相同。

let a = {};
let b = a; // copy the reference
alert( a == b ); // true, both variables reference the same object
alert( a === b ); // true

// different reference
let a = {};
let b = {}; // two independent objects
alert( a == b ); // false

Const object

const 在同個記憶體內改變物件不會出現錯誤,除非將變數指向另一個記憶體才會出現錯誤。

const user = {
  name: "John"
};

user.age = 25; // (*)

alert(user.age); // 25

// change reference
const user = {
  name: "John"
};

// Error (can't reassign user)
user = {
  name: "Pete"
};

Cloning and merging, Object.assign

通常會使用變數指到相同的記憶體,但若想要複製獨立一樣的物件,也是可行的。

let user = {
  name: "John",
  age: 30
};

let clone = {}; // the new empty object

// let's copy all user properties into it
for (let key in user) {
  clone[key] = user[key];
}

// now clone is a fully independent clone
clone.name = "Pete"; // changed the data in it

alert( user.name ); // still John in the original object

// 或是使用 Object.assign
Object.assign(dest, [src1, src2, src3...])

let user = { name: "John" };

let permissions1 = { canView: true };
let permissions2 = { canEdit: true };

// copies all properties from permissions1 and permissions2 into user
Object.assign(user, permissions1, permissions2);

// now user = { name: "John", canView: true, canEdit: true }

// use Object.assign
let user = {
  name: "John",
  age: 30
};

let clone = Object.assign({}, user);

// 以上方法是用在物件的屬性為原始值,若屬性包含物件會指向相同記憶體。
let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object

// user and clone share sizes
user.sizes.width++;       // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one

// 為了解決屬性也是物件的問題,可以使用函式庫 lodash 的函式 .cloneDeep(obj)

Last updated