闭包函数

  • 作用域链
    • 由于作用域是相对于变量而言的,而如果存在多级作用域,这个变量又来自于哪里?这个问题就需要好好地探究一下了,我们把这个变量的查找过程称之为变量的作用域链
    • 作用域链的意义:查找变量(确定变量来自于哪里,变量是否可以访问)
    • 简单来说,作用域链可以用以下几句话来概括:(或者说:确定一个变量来自于哪个作用域)
      • 查看当前作用域,如果当前作用域声明了这个变量,就确定结果
      • 查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明
      • 再查找上级函数的上级函数,直到全局作用域为止
      • 如果全局作用域中也没有,我们就认为这个变量未声明(xxx is not defined)
    var name = 'sunny';
    (()=>{
        var name = 'storm';
        console.log(name);
    })();

    var name = 'sunny';
    (()=>{
        console.log(name);
        var name = 'storm';
    })();

    var name = "sunny";
    var f1 = ()=> {
        return ()=>{
            console.log(name);
        }
        var name="storm";
    }
    var nameNew = f1();
    nameNew();

    var name = "sunny";
    var f1 = ()=>{
        return {
            say:()=>{
                console.log(name);
                var name = "storm";
            }
        }
    }
    var nameNew = f1();
    console.log(nameNew.say());
  • 闭包函数的实现
    • 函数执行完毕后,作用域中保留了最新的timer变量的值
    • 闭包函数常应用于模块化开发,防止变量被破坏
    var count = ()=>{
        var timer = 0;
        var c = ()=>{
            return timer++;
        }
        return c;
    }
    var counter = count();
    console.log(counter());
    console.log(counter());
    console.log(counter());
  • 闭包的创建

    • 先用外层函数封装一个受保护的局部变量
    • 再在内层函数中操作外层函数的变量
    • 外层函数将内层函数返回到外部,在外部反复调用
  • 闭包的判断

    • 函数嵌套!
    • 内层函数使用外层函数的局部变量
    • 内层函数被返回到外部,在外部调用
  • 判断闭包输出

    • 同一次外层函数调用返回的内层函数,操作同一个变量
    • 外层函数调用了几次,就有几个受保护的变量副本
  • 闭包的优缺点

    • 优点:即要重用变量,又要保护变量不被污染
    • 缺点:占用更多内存空间——因为outer的活动对象无法释放
  • 函数的四种调用方式

    • 在ES6的箭头函数之前的时代,想要判断一个函数内部的this指向谁,就是根据这四种方式来决定的,函数内部的this跟大小写、书写位置无关
    • 函数调用
    • 方法调用
    • 构造函数调用(new)
    • 上下文方式调用(call、apply、bind)
    // 函数调用,this指向window
    var age = 18;
    var person = {
        age:15,
        say:function(){
            console.log('我今年' + this.age + '岁!');
        }
    }
    var sunny = person.say;
    sunny(); 
    // 方法调用,this指向对象person
    var age = 18;
    var person={
        age:15,
        say:function (){
            console.log('我今年' + this.age + '岁!');
        }
    }
    person.say();
    // 构造函数调用,this将指向构造函数实例本身
    var age=18;
    var person={
        age:15,
        say:function(){
            console.log('我今年' + this.age + '岁!');
        }
    }
    new person.say();
    //上下文调用方式,有3种,call、apply、bind
    function f1(){
        console.log(this);
    }
    //call方法的第一个参数决定了函数内部的this的值
    f1.call([1,3,5])
    f1.call({age:20,height:1000})
    f1.call(1)      
    f1.call("abc")
    f1.call(true);
    f1.call(null)
    f1.call(undefined);

    //上述代码可以用apply完全替换

    //总结:
    //call方法的第一个参数:
    //1、如果是一个对象类型,那么函数内部的this指向该对象
    //2、如果是undefined、null,那么函数内部的this指向window
    //3、如果是数字-->this:对应的Number构造函数的实例
    //      -->   1   --> new Number(1)
    //  如果是字符串-->this:String构造函数的实例
    //      --> "abc"   --> new String("abc")
    //  如果是布尔值-->this:Boolean构造函数的实例
    //      --> false   --> new Boolean(false)
  • bind()函数
    // 普通方法调用
    var person = {
        age:18,
        run : function(){
            console.log(this);  //this指向person
            var _that=this;
            setTimeout(function(){
                console.log(this.age); //this指向window
                console.log(_that.age); 
            },50);
        }
    }
    person.run();
// 通过执行了bind方法,匿名函数本身并没有执行,只是改变了该函数内部的this的值,指向person
var person = {
    age:18,
    run : function(){
        console.log(this);  // this指向person
        setTimeout((function(){
            console.log(this.age); 
        }).bind(this),50);  // 绑定this指向person
    }
}
person.run();
    // bind函数基本用法
    function speed(){
        console.log(this.seconds);
    }
    speed({ seconds:100 });
    // 执行了bind方法之后,产生了一个新函数,
    // 这个新函数里面的逻辑和原来还是一样的,唯一的不同是this指向{ seconds:100 }
    var speedBind = speed.bind({ seconds:100 });
    speedBind();    //100
    // bind函数常规写法
    (function eat(){
        console.log(this.seconds);
    }).bind({ seconds:360 })()  
    // bind函数案例
    var obj={
        name:"西瓜",
        drink:(function(){
            //this指向了:{ name:"橙汁" }
            console.log(this.name);
        }).bind({ name:"橙汁" })
    }
    obj.drink();    //"橙汁"

    var p10={
        height:88,
        run:function(){
            //this
            setInterval((function(){
                console.log(this.height);   //88
            }).bind(this),100)  
        }
    }
    p10.run();
    // 手写bind函数
    Function.prototype._bind = target => {
        // 这里的this指向函数实例
        // target表示新函数的内部的this的值
        // 利用闭包创建一个内部函数,返回那个所谓的新函数
        return () => {
            this.call(target);
        }
    }
    function person(){
        console.log(this);
    }
    person();
    var sunny = person.bind({age:18});
    sunny();
原文地址:https://www.cnblogs.com/SharkJiao/p/13548207.html