由事件驅動的或者當它被調用時執行的可重複使用的代碼塊。javascript
// function.js
console.log(add1(1, 2)); // 3
console.log(add2(1, 2)); // Uncaught ReferenceError: Cannot access 'add2' before initialization
//函數聲明
function add1(a, b){
return a + b;
}
//函數表達式
const add2 = function add(a, b){
return a + b;
}
複製代碼
內置對象,用於獲取函數參數和參數長度等。java
// factorialArguments.js
const factorialArguments = function(num) {
if(num <= 1){
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
console.log('5的階乘', factorialArguments(5));
// factorial.js
const factorial = function f(num) {
if(num <= 1) {
return 1;
} else {
return num * f(num - 1);
}
}
console.log('5的階乘', factorial(5));
複製代碼
// callAndApply.js
const sum = function sum(a, b) {
return a + b;
}
const sumByCall = function (a, b) {
return sum.call(this, a, b);
}
const sumByApply = function (a, b) {
return sum.apply(this, [a, b]);
}
const sumByApplyArguments = function (a, b) {
return sum.apply(this, arguments);
}
console.log('sum 求和', sum(2, 7));
console.log('sumByCall 求和', sumByCall(2, 7));
console.log('sumByApply 求和', sumByApply(2, 7));
console.log('sumByApplyArguments 求和', sumByApplyArguments(2, 7));
複製代碼
區別: apply
傳遞參數是數組傳遞,call
是一個一個傳遞git
// transmitByValue.js
const transmitByValue = function (str) {
str += ',我是 pr';
return str;
}
const str = '你好';
console.log('str before', str); // str before 你好
console.log('str by transmitByValue', transmitByValue(str)); // str by transmitByValue 你好,我是 pr
console.log('str after', str); // str after 你好
複製代碼
// transmitByAddress.js
const transmitByAddress = function (str) {
str.words += ',我是 pr';
return str;
}
const str = {
words: '你好'
};
console.log('str before', str.words); // str before 你好
console.log('str by transmitByAddress', transmitByAddress(str)); // str by transmitByAddress { words: '你好,我是 pr' }
console.log('str after', str.words); // str after 你好,我是 pr
複製代碼
// closureHasAnonymousFunction.js
const closure = function () {
let num = 10;
return function () {
return ++num;
}
}
const closureAlias = closure();
console.log(closureAlias()); // 11
console.log(closureAlias()); // 12
console.log(closureAlias()); // 13
console.log('-----');
// closure()()外層函數每次都執行,即 num 每次都初始化
console.log(closure()()); // 11
console.log(closure()()); // 11
console.log(closure()()); // 11
複製代碼
解析github
num
,值爲匿名函數;this 對象是在運行時基於函數的執行環境而綁定的,若是全局那就是 window 或 global(Node.js),在對象內部就是這個對象。閉包運行時屬於 window,由於不屬於這個對象的屬性或方法。數組
// closureHasThis.js
var obj = {
name: 'say',
say () {
return function () {
return this;
};
}
}
console.log(obj.say()()); // 指向 Window
複製代碼
1.對象冒充方式解決閉包
// closureHasThis1.js
var obj = {
name: 'say',
say () {
return function () {
return this;
};
}
}
console.log(obj.say().call(obj));
複製代碼
2.賦值方式解決app
// closureHasThis2.js
var obj = {
name: 'say',
say () {
var that = this;
return function () {
return that;
};
}
}
console.log(obj.say()());
複製代碼
這個例子在 ES6 的 let 關鍵字中介紹過。函數
循環中的每一個迭代器在運行時都會給本身捕獲一個 i 的副本,根據做用域的工做原理,循環中的 10 個函數雖然是在各個迭代器中分別定義的,但它們都會被封閉在全局做用域中,實際只有一個 i,結果每次都會輸出 10。oop
// loop.js
for (var i = 0; i <= 9; i++) {
setTimeout(function () {
console.log(i);
});
}
複製代碼
1.使用閉包,在每一個循環迭代中加個閉包做用域post
// loopHasClosure.js
for (var i = 0; i <= 9; i++) {
(function () {
console.log(i);
})(i);
}
複製代碼
2.使用 let
// loopHasLet.js
for (let i = 0; i <= 9; i++) {
console.log(i);
}
複製代碼