个人博客仓库html
this, call, apply 和 bind 以及箭头函数git
用来放一些我看的书籍和学习的记录,欢迎点赞和讨论。github
在了解闭包以前,有一些概念必须了解,这些是造成闭包的先决条件。promise
var age = 10;
function Tom(){
var name = 'Tom';
function say(){
console.log('My name is '+name);
console.log('My age is '+age);
console.log(person);
}
say();
}
//Tom();
function Person(){
var person = 'person';
Tom();
}
Person();
// My name is Tom
// My age is 10
// error: person is not defined
复制代码
var age = 10;
function Person(){
var person = 'person';
function Tom(){
var name = 'Tom';
function say(){
console.log('My name is '+name);
console.log('My age is '+age);
console.log(person);
}
say();
}
Tom();
}
Person();
// My name is Tom
// My age is 10
// person
复制代码
上面 Tom() 建立了一个局部变量 name 和一个名为 say() 的函数, say() 是定义在 Tom() 里的内部函数,仅在该函数体内可用。say() 内没有本身的局部变量,然而它能够访问到外部函数的变量,因此 say() 可使用父函数 Tom() 中声明的变量 name 。可是,若是有同名变量 name 在 say() 中被定义,则会使用 say() 中定义的 name 。bash
看到这里你可能就有疑问了,词法做用域究竟是什么呢?闭包
它就是指在函数嵌套中,变量解析的一种规则,根据变量在代码中声明的位置,嵌套的函数在查找变量时,若是本身没有声明,那么能够访问外部的变量。app
上面两个例子还涉及到 this 的问题。异步
定义:函数 A 有一个函数 B ,函数 B 引用了函数 A 的变量, 函数 B 就叫作闭包。函数
下面是偏函数的示例,柯里化和偏函数稍微有些区别,放到单独一个模块来讲。性能
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
复制代码
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
复制代码
这是在循环中建立闭包的常见错误,这段代码会输出五个6,缘由是给 setTimeout 的函数是闭包,循环六次造成六个闭包,这六个闭包在循环环境中被建立,共享了一个词法做用域,这个做用域存在一个变量 i ,异步执行的时候循环早已经结束,此时 i = 6 因此输出的都是 6 。
方法1: let ,并且这样不会产生闭包
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
复制代码
方法2:匿名函数
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
复制代码
方法三: 使用闭包
function callback(j){
return function () {
console.log(j)
}
}
for (var i = 1; i <= 5; i++) {
setTimeout(callback(i), i * 1000)
}
复制代码
方法四:setTimeout 的第三个参数
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
},i * 1000,i)
}
复制代码
闭包在处理速度和内存消耗方面都有比较大的开销。
例如:在建立新的对象或者类时,方法一般应该关联于对象的原型,而不是定义到对象的构造器中。缘由是这将致使每次构造器被调用时,方法都会被从新赋值一次(也就是,每一个对象的建立)。
function Persion(name,age){
this.name = name.toString();
this.age = Number(age) || 0;
this.getName = function(){
console.log(this.name);
};
this.getAge = function(){
console.log(this.age);
}
}
复制代码
应该这样作
function Persion(name,age){
this.name = name.toString();
this.age = Number(age) || 0;
}
Persion.prototype.getName = function(){
console.log(this.name);
};
Persion.prototype.getAge = function(){
console.log(this.age);
};
复制代码
如今统一将 callback 函数换成了 promise 。