函式(function
)是Javascript程式碼區塊,僅定義一次就可以被多次執行或調用。
function getName(name) { // name → 參數
return name; // name → 回傳值
}
getName("yakim"); // "yakim" → 引數
函式執行是在被調用時才開始,非函式定義時,Javascript有四種調用方式
call()
與 apply()
方法間接調用方法調用與函式調用有一個重要差異:調用情境,即函式主體的
this
會視呼叫他的物件而定:
undefined
函式中的return 述句會使函式停止執行,並將其運算後的值回傳(如果有的話),
如果return
後面沒有東西,就回傳undefined
。
但並非所有函式都有return
,像以下範例,
就只是印出物件的特性名稱與值,不需回傳值,該函式的調用值永遠是undefined
。
var o ={1:"a", 2:"b", 3:"c"};
function print(o) {
for(var prop in o) {
console.log("key:"+prop+", name:"+o[prop]);
}
}
print(o);
// "key:1, name:a"
// "key:2, name:b"
// "key:3, name:c"
函式調用的引數比參數少時,額外參數會被設為undefined
,我們可以為可能不存在的參數設定預設值,使函式更有彈性。
設計具有選擇性引數的函式時,非必須的引數要放在引數列的最後,這樣他們才能被忽略,簡單來說,注意引數順序
var obj1 = {a:"1", b:"2", c:"3"};
var obj2 = {d:"1", e:"2", f:"3"};
function getPropertyNames(o,a) { // o → 必須 ; a → 可以忽略
a = a || []; // 如果a不存在就建一個新陣列
for(var property in o) a.push(property); // 把o的特性值丟進a陣列
return a;
}
var newObj = getPropertyNames(obj1); // 把obj1特性丟進新建的newObj
getPropertyNames(obj2, newObj); // 把obj2特性也丟進newObj
console.log(newObj); // ["a", "b", "c", "d", "e", "f"]
看完引數比參數少的情況,那倒過來看,換成參數比比引數少的時候呢,arguments
就上場了
arguments
物件是種「類陣列物件」,有length
屬性,指的就是引數的數量
arguments[0]
→ 第一個引數arguments[1]
→ 第二個引數// 抓出最大值
Max(1,5,100,20); // 100
function Max() {
var max = 0;
var num = arguments; // 把引數陣列丟進num
// num.length → 4
// num[0] → 1 ; num[1] → 5; num[2] → 100...
for (var i=0; i<num.length; i++) {
if (max < num[i]) max = num[i]; // 當前元素大於max時,max更新
}
return max;
}
當一個函式有超過三個以上的參數,要調用時會不好記順序,這時可以把單一物件當成引數傳入,使用「名稱/值」的方式對照,無須理會正確順序。
var a = [1,2,3,4], b = [];
copy({from:a, to:b, length:4});
function copy(obj) {
// obj.from → [1, 2, 3, 4]
// obj.form_start || 30 → 30
// obj.to → []
// obj.to_start: obj.to_start || 20 → 20
// obj.length → 4
}
函式也可以當做「值」,這代表他們可以被指定給變數、物件特性或陣列元素中,或是傳給函式當作引數用。
// 計算式
function add(x,y) { return x+y; }
function subtract(x,y) {return x-y; }
// 調用上面運算式做為第一個引數
function operate(method, num1, num2) {
return method(num1, num2);
}
// a = 5+10
var a = operate(add, 5, 10);
// c = (10-5) + (2-1)
var c = operate(add,
operate(subtract, 10, 5),
operate(subtract, 2, 1));
函式是一種物件,代表也有特性可以存取,假設你要寫一個每次調用都回傳不同值的函式,且都要記錄上次存取的值,我們可以把資訊直接存在函式特性上
addCount.count = 0;
function addCount() {
return addCount.count++;
}
addCount();
addCount();
addCount();
console.log( addCount.count ); // 3
.call()
與 .apply()
可以用來傳遞參數用,但更重要的功能是用來間接調用某函式,當作是某物件的方法,第一個引數是指出要
在哪個物件上調用,在函式內容中成為this
call
:任何多個引數 f.call(object, num1, num2)
apply
:一組陣列當引數 f.call(object, [num1, num2])
//傳遞參數用--------------
function sum(x,y) {
return x+y;
}
// call
function callTest(num1, num2) {
return sum.call(this, num1, num2);
}
// apply
function applyTest(num1, num2) {
return sum.apply(this, [num1, num2]);
}
/* apply 也可以改寫用 arguments
function applyTest() {
return sum.apply(this, arguments);
}
*/
callTest(10,20); //30
applyTest(10,50); //60
//間接調用函式,當做是物件的方法--------------
function sum(x,y) {
return x+y;
}
function Obj(x,y) {
this.x = x;
this.y = y;
}
var o = new Obj();
//方法.apply(作用域, [參數陣列])
sum.apply(o, [10,100]); // 110
閉包這邊卡了很久,網路上的教學大多看不太懂QAQ
我的理解是閉包主要用於兩部分:
以下例子是輸出global
而不是local
,為什麼呢?
var name = "global";
var obj = {
name: "local",
getName: function() {
return function() {
return this.name;
};
}
};
var a = obj.getName();
console.log(a()); // global
先來看一下a
是什麼?
obj.getName()
的函式回傳值,也是一個函式
// a 就是一個函式
// 在此範例中, this 就等於 window
function() {
return this.name;
};
重點就在這個this
,JavaScript中的this
會隨調用者而變
剛剛obj.getName()
函式已經執行完畢了,其作用域返回到最頂層的 window
所以此例的this
是指向誰呢?!其實就是window
所以winow.name
→ global
,那我們要如何輸出local
呢?
可以先設一個變數,將預期的物件作用域存下來,
之後要調用就不會受到this
的特性影響
var name = "global";
var obj = {
name: "local",
getName: function() {
// 把對象存成變數 self (此時this指向obj)
var self = this;
return function() {
// 返回 obj.name
return self.name;
};
}
};
var a = obj.getName();
console.log(a()); // local
function counter(x) {
var total = x;
return function(x) {
total += x;
return total; // 第二次調用後 total 會被存起來,直到不再調用
};
}
var a = counter(20);
a(30);
console.log(a(40)); // 20+30+40 → 90
資料來源:
JavaScript大全(第六版)
【Youtube】尚学堂科技 javascript视频教程 白贺翔 函数【八】闭包