this 指针 / 闭包 / 作用域

67 阅读3分钟

this 与闭包

作用域与上下文

作用域

  • 作用域简单来说就是在特定的场景下,特定的范围内查找变量的一套规则。

    • 一般情况下我们特指:词法作用域、静态作用域
    • 一般是代码层面上的
  • 分类

    • 全局作用域
    • 函数作用域
      • 在函数内声明的所有变量,在函数体内是始终可见的,可以在整个函数范围内复用
    • 块作用域
      • 是一个用来对之前的最小授权原则进行扩展的工具,将代码在函数中隐藏信息扩展为在块中

let 作为块级作用域存在暂时性死区

var 存在变量提升

块级作用域和暂时性死区

  • 哪些会构成块级作用域
    • if
    • for
    • { ... }
  • 暂时性死区
    • let 声明的变量的块的第一行到声明变量之间的这个区域被称为暂时性死区
    • 暂时性死区存在时,会让let 绑定这个区域,在这个区域内,无法执行该变量的其他声明;

函数表达式

  • JS 是如何运行起来的?
    • 代码的预编译阶段
      • 会对变量的内存空间进行分配
      • 对变量声明进行提升,但是值为undefined
      • 对所有的非表达式的声明进行提升

执行上下文

  • 作用域管制的函数声明在何处,而上下文主要关注的是函数从何开始调用

闭包

函数嵌套函数时,内层函数引用了外层函数作用域下的变量,并且内层函数在全局环境下可访问,就形成了闭包 当函数的执行上下文没有在原本的词法作用域下执行,就形成了闭包

闭包的常见场景:

  • 函数式编程
  • window - event handler
  • setTimeout

this

this的使用规则 this的指向:this的指向是根据执行上下文动态决定的。

  • 在简单调用时,this默认指向的是wondow / global /undefined(浏览器/node/严格模式)
  • 对象调用时,绑定在对象上
  • 使用 call / apply / bind 的时候,绑定在指定的参数上
  • 使用new 关键字,通过构造函数调用创建对象上;
  • 以上三条优先级:new > call/apply/bind > 对象调用;
  • 使用箭头函数时,根据外层的规则决定this的指向
    var number = 5;
    var obj={
        number:3,
        fn1:(function(){ //立即执行函数,JS在解析的时候就会执行
            var number;
            this.number *=2;  //立即执行函数this指向全局定义的number,10
            number = 3;
            return function(){
                var num = this.number;
                this.number *=2; //全局定义的number 20
                console.log(num); //10 -> 3
                number *= 3;
                console.log(number); //9  -> 27
            }
        })()
    }
    
    var fn1 = obj.fn1;
    fn1.call(null);//fn1()
    obj.fn1();
    console.log(window.number);
    
    // 10 9 3 27 20

什么情况下需要考虑this的指向

存在函数式编程的时候,出现的闭包的时候

bind / apply / call

    //bind
    //在不考虑new 的优先级的情况下:
    Function.prototype.bind=Function.prototype. bind || function(context){
        const fn = this;
        //get bind's params 
        const args = Array.prototype.slice.call(arguments , 1);
        return function(...innerArgs){
            const allArgs = [...args,...innerArgs];
            return fn.apply(context ,allArgs)
        }
    }
    
    
    function foo(){
        this.baz = 'baz';
        return this.baz;
    }
    
    var func = foo.bind({'baz':'baz'});
    func(); //baz
    new func() //baz:baz
    
    //如果考虑到new的一个优先级
    //bind 返回的函数作为构造函数,搭配new 关键字出现的话,这种绑定就要被忽略,this要绑定在实例上,也就是说,new 操作符要高于bind绑定;
    
    Function.prototype.bind=Function.prototype. bind || function(context){
        const me = this;
        //get bind's params 
        const args = Array.prototype.slice.call(arguments , 1);
        var F =function(){};
        F.prototype = this.prototype;
        
        var bound = function(){
            var innerArgs = Array.prototype.slice.call(arguments);
            const allArgs = [...args,...innerArgs];
           return fn.apply(this instanceof F ? this : context || this, finalArgs);
        }
        bound.prototype = new F();
        return bound;
    }
    

实现一个call

    function called(){
        const args = Array.prototype.slice().call(arguments, 1);
        context.fn = this;
        if(context){
            const result = context.fn(...args);
            delete context.fn();
            return result
        }else{
            return this(...args)
        }
        
    }