别催了~ 正在从服务器偷取页面 . . .

javascript闭包


闭包问题

简单定义

闭包简单来讲就是一个外部函数里面嵌套一个内部函数,内部函数引用了外部函数的变量等内容,外部函数将内部函数作为返回值(其实也未必要以返回值的形式,只要能够把内部函数传递出去就行),这就是一个闭包。

有了闭包,这样就可以在外部函数以外的地方调用内部函数,本来一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包却可以继续保持对该函数作用域的引用,闭包使得函数可以继续访问定义时的词法作用域。

  • 优点:使得外部可以访问内部函数等,延长内部函数等的寿命
  • 缺点:滥用闭包造成内存泄露

如下为一个简单的闭包:

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

注意

  1. this四条规则的优先级就不一一细说,new绑定>显式绑定>隐式绑定>默认绑定(可参考《你不知道的javaScript上》91~95页)
  2. 当call、apply、bind需要忽略this绑定时,可以传入null(但是会改变this绑定,函数中如果使用this会绑定到全局对象),可以用∅。
  3. 特殊的箭头函数,箭头函数中的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

文章作者: John Doe
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 John Doe !
评论
  目录