zoukankan      html  css  js  c++  java
  • 作用域链和函数内部this指向问题以及bind、call、apply方法

    作用域链和函数内部this指向问题以及bind、call、apply方法

    作用域链

    • 作用域是相对于变量而言的, 其意义就在与查找变量(确定变量的来处, 变量是否可以访问到, 确定变量在当前位置是否可以取到值)

      • JS分函数作用域、全局作用域和块级作用域(es6新增)
      • JS变量又遵循就近使用的原则
        1. 首先在使用该变量的当前作用域查找 , 如果当前作用域声明了这个变量,就可以确定结果;如果没有查找到,进入步骤2
        2. 查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明
        3. 再查找上级函数的上级函数,直到全局作用域为止
        4. 如果全局作用域中也没有,我们就认为这个变量未声明 xxx变量 is not defined
    • 如何确定函数作用域的上一级作用域

      • 函数的上一次作用域取决于函数声明时的位置,而不是函数调用时的位置。

      • 函数就声明在全局作用域内

        <script>
                function fn(callback){
                    var age=18;
                    callback()   
                }
                
                fn(function(){
                    console.log(age);  //age is not defined
                    //分析:age变量:
                    //1、查找当前作用域:并没有
                    //2、查找上一级作用域:全局作用域 (此时函数就声明在全局作用域内)
                    //3、此时age变量就获取不到fn函数内部的age变量
                    //看上一级作用域,不是看函数在哪里调用,而是看函数在哪里编写,函数声明在全局,它的上一级作用域就是全局作用域。
                    //-->因为这种特别,我们通常会把作用域说成是:词法作用域
                })
        </script>
        
      • 函数声明在其它的函数内 (闭包就是个典型案例)

        <script>
            function fn(){
                var a=5;
                return function(){
                    a++;
                    console.log(a);     //a变量肯定是可以访问的
                }
            }
            var f1=fn();        //f1指向匿名函数
            f1();       //6
            f1();       //7
            f1();       //8
        	 //分析:a变量:
                    //1、查找当前作用域:并没有
                    //2、查找上一级作用域:fn函数作用域 (此时函数就声明在fn函数内)
        
            //-->一般认为函数执行完毕,变量就会释放,但是此时由于js引擎发现匿名函数要使用a变量,所以a变量并不能得到释放,而是把a变量放在匿名函数可以访问到的地方去了
            //-->a变量存在于f1函数可以访问到的地方,当然此时a变量只能被f1函数访问
        </script>
        

    函数内部的this指向问题(箭头函数不适用)

    • this的指向,是在函数执行的地方确定的,而非函数定义的地方

    • 函数的4种调用方式

      1. 普通函数调用的方式

        <script>
            var age = 18; //该age声明在全局作用域下,其就会被挂载到window对象上,即window.age=18
            var p = {
                age: 15,
                say: function () {
                    console.log(this.age); //this指向window
                }
            }
            var s1 = p.say //s1 = function () {console.log(this.age);}
            s1(); //函数调用, 结果为 18
        </script>
        
      2. 方法的调用

        <script>
            var age = 18; //该age声明在全局作用域下,其就会被挂载到window对象上,即window.age=18
            var p = {
                age: 15,
                say: function () {
                    console.log(this.age); //this指向p对象
                }
            }
            p.say() //结果为15  //p.say获取的就是p对象内部的say方法,然后方法后+()就是方法的调用。
        </script>
        
      3. new调用(构造函数)

        <script>
            var age = 18;
            var p = {
                age: 15,
                say: function () {
                    console.log(this.age);
                     //此时this指向构造函数new出来的实例,而实例中并没有初始化age属性
                }
            }
            new p.say() //构造函数调用, 结果为undefined
        </script>
        
        <script>
            var age = 18;
            var p = {
                age: 15,
                say: function (age) {
                    this.age = age
                    console.log(this.age); 
                }
            }
            new p.say(22) //构造函数调用, 结果为22
        </script>
        
      4. 上下文方式(call、apply、bind)

        <script>
            var length = 21; //window.length = 21
            function f1() {
                console.log(this.length);
            }
            f1.call([1, 3, 5]) //结果为3,此时this指向[1,3,5]数组
            f1.apply(this) //结果为21,此时this指向window
            f1.call(5) //undefinde , 此时this指向 Number,其没有length属性
            f1.bind(this)() //结果为21 
        //bind方法只改变函数f1内部this指向,而不会让函数执行,想让其执行,需要在后面手动调用+()
        </script>
        

    bind、call、apply共同点和区别

    • 三个方法都可以改变函数内部this指向
    • bind和call、apply的区别是,bind方法不会让函数执行,而call、apply的调用会让函数也跟着执行
    • call和apply的区别就在与给函数传参的方式不同
      • call(this指向对象, 参数1,参数2,参数3) 参数以逗号分隔,依次排下去
      • apply(this指向对象, [参数1,参数2,参数3]) 参数包裹在一个数组中
  • 相关阅读:
    第八章 路由器交换机及其操作系统的介绍
    k-Tree DP计数
    Drop Voicing 最长升序
    高精度
    1196D2
    C
    POJ 3974 马拉车
    2020.8.1第二十六天
    2020.7.31第二十五天
    每日日报
  • 原文地址:https://www.cnblogs.com/guojiabing/p/10417019.html
Copyright © 2011-2022 走看看