由事件驱动的或者当它被调用时执行的可重复使用的代码块。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);
}
复制代码