闭包问题
简单定义
闭包简单来讲就是一个外部函数里面嵌套一个内部函数,内部函数引用了外部函数的变量等内容,外部函数将内部函数作为返回值(其实也未必要以返回值的形式,只要能够把内部函数传递出去就行),这就是一个闭包。
有了闭包,这样就可以在外部函数以外的地方调用内部函数,本来一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包却可以继续保持对该函数作用域的引用,闭包使得函数可以继续访问定义时的词法作用域。
- 优点:使得外部可以访问内部函数等,延长内部函数等的寿命
- 缺点:滥用闭包造成内存泄露
如下为一个简单的闭包:
function outer(){
var a=2;
function inner(){
console.log(a);
}
return inner;
}
let other=outer();
other();//2
本来outer函数执行完之后,它的执行上下文应该是会被销毁的,但是通过inner(outer函数的闭包),在outer函数执行结束后还能打印出a的值
调用栈的变化如下:
其实other函数的执行上下文中并没有变量a,当other函数执行到输入变量a时,其实是从作用域链other函数作用域–>outer函数的闭包–>全局作用域,
使用回调函数也是闭包
function wait(s){
setTimeout(()=>{
console.log(s);
},1000);
}
wait('你好呀');
wait()执行1000毫秒之后,它的内部作用域并不会消失,timer函数依然保持有wait()作用域的闭包
模块化
模块模式需要具备两个条件
1.必须有外部封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)
2.封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。
注意:一个从函数调用所返回的,只有数据属性而没有闭包函数的对象并不是真正的模块。
单例模块化(IIFE立刻执行函数)
var module=(function m(){
var a="cool";
var b=[1,2,3];
function f(){
console.log(a);
}
function f1(){
console.log(b.join('!'));
}
return {
f:f,
f1:f1
}
})();
module.f();//'cool'
module.f1();//1!2!3
三 this指向问题
this是在运行是绑定的,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
默认绑定(this指向全局对象window)
function f(){
console.log(this.a);
}
var a=2;
f();//2
在以上代码中,f()是不带任何修饰的函数引用进行调用的,所以是属于默认绑定
隐式绑定
function f(){
console.log(this.a);
}
var obj={
a:2,
f:f
}
obj.f()//2
这种情况是函数的引用有上下文对象的(obj对象的上下文),所以隐式绑定规则会把函数调用中的this绑定到这个上下文对象
function f(){
console.log(this.a);
}
var obj={
a:22,
f:f
}
var obj1={
a:2,
obj:obj
}
obj1.obj.f();//22
引用的是最接近的变量a
注意,将如上要调用的函数赋值给另外变量后,”隐式“会消失
以下3个例子都属于赋值操作,setTimeout()函数实现也是类似的
例一:
function f(){
console.log(this.a);
}
var obj={
a:22,
f:f
}
var other=obj.f;
var a=123
other();//123
例二:
function f(){
console.log(this.a);
}
function fun(func){
func();
}
var obj={
a:22,
f:f
}
var a=123
fun(f);//123
相当于将f赋值给func
例三:
function f(){
console.log(this.a);
}
var obj={
a:22,
f:f
}
var a=123
setTimeout(obj.f,100);//123
显式绑定
指定某个对象为函数调用时的this,call、apply、bind函数
call
function f(){
console.log(this.a);
}
var obj={
a:22
}
var other=function(){
f.call(obj)
}
other();//22
setTimeout(other,100);//22
apply
function f(b){
console.log(this.a,b);
return this.a+b;
}
var obj={
a:22
}
var other=function(){
return f.apply(obj,argumments)//argumments是固定变量,不能改名
}
var res=other(3);//22 3
console.log(res);//25
bind
function f(b){
console.log(this.a,b);
return this.a+b;
}
function bind(f,obj){
return function(){
return f.apply(obj,arguments)
}
}
var obj={
a:22
}
var other=bind(f,obj)
var res=other(3);//22 3
console.log(res);//25
new绑定
所有函数都可以用new来调用,new调用的过程见下文 使用new操作符创建构造函数的实例整个过程?
function f(a){
this.a=a;
}
var other=new f(2);
console.log(other.a);//2
注意
- this四条规则的优先级就不一一细说,new绑定>显式绑定>隐式绑定>默认绑定(可参考《你不知道的javaScript上》91~95页)
- 当call、apply、bind需要忽略this绑定时,可以传入null(但是会改变this绑定,函数中如果使用this会绑定到全局对象),可以用∅。
- 特殊的箭头函数,箭头函数中的this指的是其外层作用域.
function f(a){
return (a)=>{
//这里面的this是f里的
console.log(this.a);
}
}
var obj1={
a:1
}
var obj2={
a:2
}
var other=f.call(obj1);
other.call(obj2);//1