在其他的程式語言中 物件與函數是兩個不同的東西,
但是在 JavaScript 裡 它們是非常非常相關的
它們在很多情況下幾乎是一樣的
所以讓我們來談談物件和函數
我們先來看物件和點
記得之前說過物件的值也可以是另一個「名稱/值」的組合
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 4 節講座 30 影片截圖
物件的值可以是純值、另外一個物件(名稱/值配對)、函數(在物件裡稱作方法),
屬性或方法與物件本身有所連結,物件可以參考到屬性和方法
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 4 節講座 30 影片截圖
在記憶體中
核心物件會有一個記憶體的位址
然後可以參考到這些電腦記憶體中的屬性和方法的位址
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 4 節講座 30 影片截圖
屬性和方法的位址可能有關,也可能無關
但無論如何,物件能夠參考到這些位址空間
找到這些屬性、方法的所在
來看看 JavaScript
如何找出那些物件的屬性和方法的記憶體位置
我要建立一個新的物件 var person
用「等於」這個指派運算子 然後再使用 new 這個特殊的運算子來新增物件,
程式碼如下:
var person = new Object();
有其他方式可以更快的新增物件
但現在我先用 new object 的語法建立一個新的物件
因為這比較清楚表示我們在幹嘛
現在已經建立好一個新物件
然後它會在記憶體裡
現在我新增一些屬性和方法
例如,person
然後用一個稱為「Computed Member Access」的運算子
在中括號裡我放進值的名稱
讓我存在記憶體中的東西
比如,[firstname]
它還不存在,所以令他等於 Jimmy
這會在記憶體中創造這個東西,然後給他那個名字
然後這個物件就能夠參考到它在記憶體中的位子
所以,它會知道 [firstname] 在記憶體中的位子
並且知道是純值的字串
這就是屬性 一個稱為 firstname 的屬性
這是其中一種取用屬性的方法
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
回到運算子優先性和相依性表格
運算子優先性,你會發現我們有「Computed Member Access」
在很上面
它是左到右相依的 這是中括號
所以這是一個運算子
這個運算子能夠找出物件的屬性和方法
用這個字串去取用是其中一種方法
然後設定「lastname
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
person['lastname'] = "Huang";
這個運算子好用的地方是我可以將字串指派給一個變數
在中括號中使用變數,如果值是會改變的,就很適合用這種方式來取用屬性
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
person['lastname'] = "Huang";
var firstNameProperty = "firstname";
console.log(person);
console.log(person[firstNameProperty]);
我們來看 Console 中的結果:
這個中括號是「Computed Member Access 運算子」
它把物件當做一個參數,然後字串當做另一個參數
然後找到屬性,並回傳它的值
我也將物件本身輸出
我可以看到這兩個屬性:firstname 和 lastname
還有另一個屬性 proto
現在不用管他
但之後會講到這個
現在只要專注在我建立的東西就好 firstname 和 lastname
我用中括號這個特殊運算子找到 firstname
講講另一個運算子
更常見、清楚
很簡單就能打出來的運算子 可以取用屬性和方法
你可以用字串
但這個比較清楚,也很容易打出來
接下來我要 console.log 出 firstname
但我要用一個不同的運算子 「點」運算子
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
person['lastname'] = "Huang";
var firstNameProperty = "firstname";
console.log(person.firstname);
這個點運算子是個函數
當它接在物件之後時
回到運算子優先性和相依性表格
你可以看到這是優先性第二高的
它是從左到右的 需要兩個參數
物件名稱和屬性名稱
我沒有放在引號內
console.log(person.'firstname'); // 錯誤的寫法
雖然本質上它是這樣運作的
它會將字串傳入物件
這是你想要的值的名稱
不過不需要這樣做 這樣不正確
別這樣做
如果你放在點後面,語法解析器會自動瞭解
console.log(person.firstname); // 正確的寫法
你打出來的東西就是想要傳入的字串
這樣我們打字可以更快
我們到 Console 看結果:
所以這個點就和中括號一樣
這是取用成員的方式
這叫做成員取用
成員取用運算子 成員就是物件的成員
手指和腳指就是你身體的成員
所以它會幫你找到物件的成員,像是方法和屬性
所以它會找到這個屬性,因為只是個字串
他不會呼叫出方法
而是呼叫屬性
所以我可以console.log 出 person.lastname
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
person['lastname'] = "Huang";
var firstNameProperty = "firstname";
console.log(person.firstname);
console.log(person.lastname);
Console 中的結果:
很易讀、打字也很簡單
就是這樣
它會找到記憶體的位址
然後這個名稱從物件去參照
我也可以用這個方法去設值
其實,屬性的種類有很多種
除了純值外,還可以是另外一個物件
我可以新增一個名稱/值的配對
設定 person.address
我不設定字串、數值或布林
而是另一個用 new 運算子創造出來的物件,
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
person['lastname'] = "Huang";
person.address = new Object();
所以這是物件中的物件
然後我可以用「點運算子」增加屬性
給我的子物件
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
person['lastname'] = "Huang";
person.address = new Object();
person.address.street = '123 Main St.';
這些都是運算子 我們要怎麼知道誰先運算呢
就要回到相依性來判斷
成員取用運算子是左到右相依
是左相依性的, 所以在點左邊會先被執行
然後才是右邊
所以在 person 和 address 中間的這個點會先執行
然後看 person 這個物件
然後找屬性和方法的名字
最後在記憶體中找到它們
接下來是第二個點,它會找到這個物件
在記憶體中找到,進而找尋它的屬性或方法 叫 street
在這個情況下 他無法找到這個屬性,所以我設一個值給他
然後它就會自動建立這個屬性,並且賦予他值
我可以在替 person.address 增加其他屬性
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
person['lastname'] = "Huang";
person.address = new Object();
person.address.street = '123 Main St.';
person.address.city = 'New York';
person.address.state = 'NY';
我可以不斷建立子物件
我可以不斷這樣建立 因為它是左相依性的
物件可以不斷包含其他物件
這很強大
物件本身和屬性方法都待在記憶體內
這些點、中括號都只是函數
它們是運算子 一個取出資訊的方式
我們分別用這兩種運算子來取值,
程式碼如下:
var person = new Object();
person['firstname'] = "Jimmy";
person['lastname'] = "Huang";
person.address = new Object();
person.address.street = '123 Main St.';
person.address.city = 'New York';
person.address.state = 'NY';
console.log(person.address.street);
console.log(person.address.city);
console.log(person["address"]["state"]);
Console 中的結果:
他們本質上是一樣的
也請記得, 雖然我們可以用點也可以用中括號去取用屬性和方法
最好還是只要用點運算子就好
他很簡潔、很清楚 也很容易除錯
建議用點運算子存取物件的屬性
除非你真的需要用動態字串取用屬性
一些可能會改變的字串
但剛開始的時候, 只要用點運算子就好
這就是物件和點
可以使用物件實體語法(object literal syntax)來建立物件,JavaScript可以使用{}來建立物件,{}不是運算子,當JavaScript解析語法時看到大括號,而你不是當作if條件式或迴圈時,它就是假設你在創造物件。對JavaScript來說它就是建立物件到記憶體中,還有屬性和方法到記憶體,
程式碼如下:
var Jimmy = {
firstname: 'Jimmy',
lastname: 'Huang',
address: {
street: '123 Main St.',
city: 'New York',
state: 'NY'
}
};
可在函數呼叫時把物件當作參數傳入
程式碼如下:
var Jimmy = {
firstname: 'Jimmy',
lastname: 'Huang',
address: {
street: '123 Main St.',
city: 'New York',
state: 'NY'
}
};
function greet(person) {
console.log("Hi " + person.firstname);
}
greet(Jimmy);
Console 中的結果:
可在呼叫函數時同時創造物件,
程式碼如下:
function greet(person) {
console.log("Hi " + person.firstname);
}
//這個物件在被呼叫時同時建立(到記憶體中)
greet({
firstname: 'John',
lastname: 'Doe'
});
Console 中的結果:
也可使用點(成員取用)運算子來新增物件的屬性
程式碼如下:
var Jimmy = {
firstname: 'Jimmy',
lastname: 'Huang',
address: {
street: '123 Main St.',
city: 'New York',
state: 'NY'
}
};
//可以結合.運算子與物件實體語法
Jimmy.address2 = {
street: '333 Seconed St.'
}
console.log(Jimmy);
Console 中的結果:
所以不論物件實體語法
或是用點運算子建立物件
都是一樣的
對於 JavaScript 引擎來說
就是在建立物件到記憶體中 還有屬性和方法到記憶體
所以我用的語法對 JavaScript 引擎沒差
都是建立同樣的東西
JavaScript 能夠用以上兩種語法建立物件
重要的是你要用哪種語法?
物件實體語法真的很強大
他可以寫出很簡潔的程式碼 也很易懂
這就是物件實體語法(object literal syntax)
用大括號去定義 用冒號區隔名稱和值