看板 KnucklesNote
作者 標題 [JS] 克服JS的奇怪部分 ch4 物件與函數(上)
時間 2016-11-23 Wed. 10:19:58
Udemy課程: JavaScript全攻略:克服JS的奇怪部分
https://www.udemy.com/javascriptjs/learn/v4/overview
上完第四章的心得筆記
章節4 物件與函數(上)
30. 物件與「點」
在其他的程式中,物件與函數是不一樣的東西
但在JS中,物件與函數非常的相關,很多情況下幾乎是一樣的
物件是多個名稱/值的組合,物件中也可以包含物件與函數
物件裡的成員變數又稱為物件的屬性(Property)
物件裡的成員函數又稱為物件的方法(Method)
例如建立一個物件 person
加入兩個屬性 firstname、lastname
var person = new Object();
person["firstname"] = "Knuckles";
person["lastname"] = "Huang";
console.log(person);
console.log(person["firstname"] + person["lastname"]);
執行結果:person["firstname"] = "Knuckles";
person["lastname"] = "Huang";
console.log(person);
console.log(person["firstname"] + person["lastname"]);
![[圖]](http://i.imgur.com/w74Q4fb.png)
屬性除了使用中括弧[]來存取外,也可以使用點.來存取,例如
person.firstname = "Knuckles";
person.lastname = "Huang";
console.log(person.firstname + person.lastname); // 顯示 KnucklesHuang
person.lastname = "Huang";
console.log(person.firstname + person.lastname); // 顯示 KnucklesHuang
除非要使用變數來當作屬性名稱,否則的話使用點運算子來存取屬性就好了
物件中再加上物件
person.address = new Object();
person.address.street = "111 Main St.";// 可以用兩個點運算子存取第二層物件的屬性
person["address"]["city"] = "Taipei"; //也可用兩個[]來存取
person.address.street = "111 Main St.";// 可以用兩個點運算子存取第二層物件的屬性
person["address"]["city"] = "Taipei"; //也可用兩個[]來存取
![[圖]](http://i.imgur.com/1kzCQUI.png)
31. 物件與物件實體(object literal)
上一節使用 new Object() 建立物件的方法其實不是很好
另一種使用物件實體的語法為
var person = {};
使用大括弧{}的結果與使用 new Object() 的結果相同使用大括弧的方法還可以直接將屬性的名稱/值配對寫進去
var person = { firstname:"Knuckles", lastname:"Huang" };
這樣結果與上一節使用點運算子或中括弧[]運算子加入屬性的結果是一樣的也可以在物件中加入物件,例如
var person = {
};
firstname:"Knuckles",
lastname:"Huang",
address: {
street: "111 Main St.",
city: "Taipei"
}
};
當有一個函數需要輸入一個物件時,也可以直接傳入物件實體,例如
var knuckles = { // <- 物件名稱改成人名
};
//建立一個函數 greet,輸入值為一個物件,用來顯示輸入物件的屬性 firstname
function greet(person){
}
//執行函數,將建立好的物件實體傳入
greet(knuckles); //顯示 Hi Knuckles
//執行函數時,直接將物件實體寫在輸入值
greet({ firstname: 'Mary', lastname: 'Doe' }); //顯示 Hi Mary
firstname:"Knuckles",
lastname:"Huang",
address: {
street: "111 Main St.",
city: "Taipei"
}
};
//建立一個函數 greet,輸入值為一個物件,用來顯示輸入物件的屬性 firstname
function greet(person){
console.log('Hi ' + person.firstname);
}
//執行函數,將建立好的物件實體傳入
greet(knuckles); //顯示 Hi Knuckles
//執行函數時,直接將物件實體寫在輸入值
greet({ firstname: 'Mary', lastname: 'Doe' }); //顯示 Hi Mary
32. 框架小叮嚀:偽裝命名空間
命名空間(Namespace): 一群變數與函數的容器,用來將相同名稱的變數或函數分開
JS沒有命名空間的功能,但可以用物件的特性假裝出來
例如有兩種語言的Hello!,都想要使用greet做變數名稱
var greet = 'Hello!';
var greet = '哈囉!';
console.log(greet); // 顯示 哈囉!
這樣第一個英文的Hello!會被蓋掉var greet = '哈囉!';
console.log(greet); // 顯示 哈囉!
為了區分兩種語言的 greet,可以改為
var english = { greet: 'Hello!' };
var chinese = { greet: '哈囉!' };
console.log(english.greet); // 顯示 Hello!
建立兩個以語言為名稱的物件,將各自的 greet 變數放進去var chinese = { greet: '哈囉!' };
console.log(english.greet); // 顯示 Hello!
就可以達到命名空間的效果了
33. JSON 與物件實體
JSON(JavaScript Object Notation): 受JS的物件實體語法啟發而產生的一種資料格式
雖然看起來很像,但有一點地方不一樣
例如一個物件實體為
var objectLiteral = {
}
他的JSON格式為firstname: "Knuckles",
isMember: true
}
{
}
也就是在JSON中,成員的名稱必需加上引號變成字串的型式"firstname": "Knuckles",
"isMember": true
}
而在物件實體中,成員的名稱可以加上引號,也可以不用加
JS有內建函式可以很容易的轉換兩種資料
var objectLiteral = {
}
// 將JS物件轉為JSON字串
var jsonString = JSON.stringify(objectLiteral);
console.log(jsonString);
//將JSON字串轉為JS物件
var jsObject = JSON.parse(jsonString);
console.log(jsObject);
執行結果firstname: "Knuckles",
isMember: true
}
// 將JS物件轉為JSON字串
var jsonString = JSON.stringify(objectLiteral);
console.log(jsonString);
//將JSON字串轉為JS物件
var jsObject = JSON.parse(jsonString);
console.log(jsObject);
![[圖]](http://i.imgur.com/Ut0XkEk.png)
34. 函數就是物件
JS的函數為一級函數
一級函數(First Class Functions): 可以對一般變數做的事,也可以對一級函數做
也就是說,可以指派一個變數為函數,可以把函數當輸入值傳入另一個函數
可以使用實體語法立刻建立函數
JS的函數就是物件
函數物件是一個特殊的物件,有自己的屬性和方法
他的成員可以包含各種變數、其他物件、其他函數
函數物件有一些特殊的屬性
函數名稱: 可以沒有名稱,沒名稱的函數稱為匿名函數
程式內容: 在函數裡寫的程式也會成為函數物件的一個屬性
使用小括弧()可呼叫此屬性(Invocable)
舉個例子
function greet(){
}
//可以為函數加上屬性,因為函數就是物件
greet.language = 'english';
console.log(greet.language); //顯示 english
在這個函數物件中,特殊的屬性有console.log('hi');
}
//可以為函數加上屬性,因為函數就是物件
greet.language = 'english';
console.log(greet.language); //顯示 english
函數名稱: greet
程式內容: console.log('hi');
35. 函數陳述句與函數表示式
陳述句(Statement): 程式碼的單位,不會形成一個值
表示式(Expression): 程式碼的單位,會形成一個值,可以用指派給變數存起來
舉例來說
a = 3; // 回傳 3,是一個表示式
1 + 2; // 回傳 3,是一個表示式
a = { greeting: 'hi' } // 回傳一個物件,是一個表示式
if(a === 3){ } // a===3 會回傳布林值,但用if()包起來後沒回傳值了,是一個陳述句
1 + 2; // 回傳 3,是一個表示式
a = { greeting: 'hi' } // 回傳一個物件,是一個表示式
if(a === 3){ } // a===3 會回傳布林值,但用if()包起來後沒回傳值了,是一個陳述句
函數的定義有分為陳述句與表示式兩種方法
// 函數陳述句,沒有回傳值
function greet(){
}
greet(); // 執行這個函數,顯示 hi
// 函數表示式,有回傳值
var anonymousGreet = function(){
}
anonymousGreet(); // 執行這個函數,顯示 hi
function greet(){
console.log('hi');
}
greet(); // 執行這個函數,顯示 hi
// 函數表示式,有回傳值
var anonymousGreet = function(){
console.log('hi');
}
anonymousGreet(); // 執行這個函數,顯示 hi
函數表示式,就是使用匿名函數的方法 function(){ ... }
建立並回傳一個函數物件,然後存到變數 anonymousGreet
函數表示式的提升(Hoistiog)
若是將函數表示式的執行移到建立函數前面
anonymousGreet(); // 顯示錯誤: undefined is not a function
var anonymousGreet = function(){
}
使用函數表示式時,因為是用 = 將函數物件指派給一個變數var anonymousGreet = function(){
console.log('hi');
}
程式內容不會一開始就載入記憶體
只有變數 anonymousGreet 會先載入記憶體,但沒有賦值,所以是 undefined
此時直接使用 anonymousGreet(); 就會出現 undefined is not a function
函數表示式可以用來當其他函式的輸入值
function log(a){
}
log(function(){
});
建立一個一般的函數 log(),用來將輸入值顯示出來console.log(a);
}
log(function(){
console.log('hi');
});
執行log(),將匿名函數 function(){ console.log('hi'); }
當做輸入值傳入log()
log() 使用變數 a 儲存這個輸入的函數
並使用 console.log(a); 顯示出來
執行結果,就是顯示輸入的匿名函數
![[圖]](http://i.imgur.com/eDXykxB.png)
也可以在函數 log() 中執行輸入的函數
function log(a){
}
log(function(){
});
將匿名函數傳給 log() 後,log()使用變數 a 儲存a();
}
log(function(){
console.log('hi');
});
變數 a 即變為一個函數物件,加上小括號()即可執行這個函數物件
執行結果為顯示 hi
36. 傳值和傳參考
傳值(by value): 若變數是一個純值,使用 = 指派給另一變數時,
會將該值複製一份存到另一個變數的的記憶體位置
傳參考(by reference): 若變數是一個物件,使用 = 指派給另一個變數時,
不會複製該物件,而是讓兩個變數共用相同的記憶體位置,
使得同一個物件,有兩個名稱,像是幫物件取了一個別名
// by value (純值)
var a = 3;
var b = a; // 將 a 的值 3 指派給 b
a = 2; // 改變 a 的值為 2
console.log(a); // a 顯示為 2
console.log(b); // b 顯示為 3 ,b的值不會因為改變a而跟著變
// by reference (所有的物件(包含函數物件))
var c = { greeting: 'hi' };
var d = c; // 將 c 的物件指派給 d
c.greeting = 'hello'; // 改變 c 物件的屬性值
console.log(c); // c 顯示為 Object {greeting: "hello"}
console.log(d); // d 顯示為 Object {greeting: "hello"}
// d 物件隨著 c 物件一起變了,因為就是同一個物件
// by reference (物件傳給函數的輸入值時也一樣)
function changeGreeting(obj){ //建立一個函數
}
changeGreeting(d); // 傳入 d 物件給這個函數
console.log(c); // c 顯示為 Object {greeting: "Hola"}
console.log(d); // d 顯示為 Object {greeting: "Hola"}
// 在函數裡將物件obj的屬性改變後,c跟d也變了
// 使用 = 指派物件產生了新的記憶體位置
c = { greeting: 'howdy' };
console.log(c); // c 顯示為 Object {greeting: "howdy"}
console.log(d); // d 顯示為 Object {greeting: "Hola"}
// 使用物件實體語法建立新的物件給c後,
// c和d不再是同一個物件了
var a = 3;
var b = a; // 將 a 的值 3 指派給 b
a = 2; // 改變 a 的值為 2
console.log(a); // a 顯示為 2
console.log(b); // b 顯示為 3 ,b的值不會因為改變a而跟著變
// by reference (所有的物件(包含函數物件))
var c = { greeting: 'hi' };
var d = c; // 將 c 的物件指派給 d
c.greeting = 'hello'; // 改變 c 物件的屬性值
console.log(c); // c 顯示為 Object {greeting: "hello"}
console.log(d); // d 顯示為 Object {greeting: "hello"}
// d 物件隨著 c 物件一起變了,因為就是同一個物件
// by reference (物件傳給函數的輸入值時也一樣)
function changeGreeting(obj){ //建立一個函數
obj.greeting = 'Hola'; //將傳入物件的屬性值改為'Hola'
}
changeGreeting(d); // 傳入 d 物件給這個函數
console.log(c); // c 顯示為 Object {greeting: "Hola"}
console.log(d); // d 顯示為 Object {greeting: "Hola"}
// 在函數裡將物件obj的屬性改變後,c跟d也變了
// 使用 = 指派物件產生了新的記憶體位置
c = { greeting: 'howdy' };
console.log(c); // c 顯示為 Object {greeting: "howdy"}
console.log(d); // d 顯示為 Object {greeting: "Hola"}
// 使用物件實體語法建立新的物件給c後,
// c和d不再是同一個物件了
在其他程式語言有語法可以決定要傳值還是傳參考
但在JS中沒有選擇,純值就是使用傳值,物件就是使用傳參考
37. 物件、函數與「this」
JS在建立執行環境時,會自動產生一個變數 this
用來指向目前所在的物件
在全域環境時,this 就是指向預設的全域物件 window
若是在函數建立的執行環境下,this是指向什麼呢
function a(){
}
var b = function(){
}
a(); // 顯示 window
b(); // 顯示 window
可知不管是用函數陳述句或函數表示式,console.log(this);
}
var b = function(){
console.log(this);
}
a(); // 顯示 window
b(); // 顯示 window
裡面的 this 還是指向全域環境的 window 物件
若是在物件的方法(Method)中呢?
var c = {
}
c.log(); // 顯示 Object {name: "The c object"}
建立一個物件 c,使用函數表示式在物件裡新增一個函數 logname: 'The c object',
log: function(){
console.log(this); // 這個 this 是什麼呢?
}
}
c.log(); // 顯示 Object {name: "The c object"}
物件中的函數就是物件的方法(Method),又叫成員函數
使用 c.log(); 執行這個成員函數,
可知在物件 c 的成員函數中的 this,是指向物件 c
在JS有一個常令人感到困惑,有些人認為是bug的地方
若是在物件的成員函數中,建立一個函數
那麼在這個成員函數中的函數裡,this指向什麼呢?
var c = {
}
c.log(); // 顯示 window
在物件 c 的成員函數 log 中,建立一個函數 log2 並執行name: 'The c object',
log: function(){
var log2 = function(){
console.log(this); // 這個this是什麼呢?
}
log2();
}
}
c.log(); // 顯示 window
可發現在 log2 中的 this,預期應該要指向物件 c
結果竟然是指向全域的 window
這是JS一個奇怪的地方,有個常用的模式用來解決這個問題
var c = {
}
c.log(); // 顯示 Object {name: "The c object"}
在成員函數 log() 中,一開始就先用個變數 self 把 this 存起來name: 'The c object',
log: function(){
var self = this; //建立一個變數 self 把這邊的 this 存起來
var log2 = function(){
console.log(self); // 這個self是什麼呢?
}
log2();
}
}
c.log(); // 顯示 Object {name: "The c object"}
因為這邊的 this 可確定是指向物件 c
使用 var self = this; 後 self 即代表物件 c
接著在函數 log2 中顯示 self,因為 log2 的執行環境沒有變數 self
他會到外部環境尋找 self,依函數 log2 定義的地方,外部環境為 log
找到 log 裡的變數 self,而 log 裡的 self 代表物件 c
所以顯示 self 為物件 c
--
※ 作者: Knuckles 時間: 2016-11-23 10:19:58
※ 編輯: Knuckles 時間: 2016-11-25 03:45:41
※ 看板: KnucklesNote 文章推薦值: 1 目前人氣: 0 累積人氣: 1791
回列表(←)
分享