zoukankan      html  css  js  c++  java
  • 【JavaScript】访问外部作用域的同名变量或 this 的几种方法

    我们之前讨论过 python 和 js 在实现闭包时,使用变量的作用域差异

    https://blog.csdn.net/qq_16181837/article/details/104805151

    今天又遇到了相关的问题

    js 如何访问外部作用域的同名变量?
    在 python 中,我们有 nolocal 、 global 关键字,可以方便地声明某变量的作用域:

    def outer():
    	outer_var = 'outer'
    	def inner():
    		# 闭包
    		nolocal outer_var
    		outer_var = 'inner'
    		return outer_var
    	print(inner())
    	
    outer()
    

    输出:

    inner


    但是据我所知,js 是没有这样的关键字的。
    查了很多资料

    我这里提供几个方法:

    法一、访问外部包装的对象

    function outer() {
        var variable = 'outer';
        var obj = {
            outer_var: variable
        };
        function inner() {
            var variable = 'inner';
            console.log(obj.outer_var);
        }
        inner();
    }
    outer()
    

    输出:

    inner

    法二、通过函数访问外部变量

    function outer() {
        var variable = 'outer';
    
        function get_outer() {
            return variable;
        }
        function inner() {
            var variable = 'inner';
            console.log(get_outer());
        }
        inner();
    }
    outer()
    

    输出:

    inner


    可能你会问了,我们为什么非要让内部的变量与外部同名呢?
    的确可以,一些情况下我们可以在内部声明一个例如 local_variable 之类的变量以示区分,但在某些情况下是不可以的

    看这个例子:

    class test {
        constructor(btn) {
            // 给传进来的 btn 绑定一个回调事件
            // 调用对象的 method 方法
    
            $(btn).on('click', function(outer_this) {
                // 但是这里的 this 是调用回调函数的对象,即 btn 而不是指向对象的 this
                // 如何访问外部作用域的 this?
                return function() {
                    this.method();
                }
            }(this));
        }
    
        method() {
            console.log('Calling method.');
        }
    }
    
    var obj = new test($('.btn-1')[0])
    

    报错:
    在这里插入图片描述
    我们看一下此时的 this 指向谁:
    在这里插入图片描述
    注册事件时,函数被作为一个属性绑定在 .btn-1 这个对象上。
    那么自然地,回调时 this 就会指向这个对象。

    我们都说 js 万物皆对象,所以我为什么说

    一些情况下我们可以在内部声明一个例如 local_variable 之类的变量以示区分,但在某些情况下是不可以的

    怎么办呢?
    上面的法一是可以用的,即访问外部包装的对象
    但是法二是不行的,因为 get_outer 函数中的 this 指向的是其调用者,即全局对象或者是 undefined。
    对于这种情况,我要介绍三种方法:

    法一、改变函数 this 指向

    这里我们用到函数的一个方法 bind,为 bind 方法提供一个对象实参,其返回一个绑定到该对象上的函数拷贝。
    即 fun.bind(obj) 会返回一个 obj.fun 供以后调用。

    class test {
        constructor(btn) {
            btn.addEventListener('click', function() {
                this.method();
            }.bind(this));
        }
        method() {
            console.log('Calling method.');
        }
    }
    var obj = new test($('.btn-1')[0]);
    

    输出:

    Calling method.

    这里拓展两个方法:

    fun.call(obj[, arg1[, arg2[, arg3…]]])
    fun.apply(obj, arg1, arg2, arg3…)

    这两个方法将会立即执行函数,并改变其运行时的 this 指向为第一个参数 obj,并传入指定参数。
    不同点在于 apply 方法的第二个参数为一个数组,而 call 为一个列表。

    法二、传参

    说来惭愧,一直在想如何能够像 python 一样直接访问到外部的变量,忽略了最简单的方法
    不过这里面存在一个问题,原生 webapi 的 addEventListener 方法并没有提供传入回调函数的参数的接口,所以这里需要用闭包来实现:

    原生 webapi:

     class test {
         constructor(btn) {
             btn.addEventListener('click', function(outer_this) {
                 return function() {
                     outer_this.method();
                 }
             }(this));
         }
         method() {
             console.log('Calling method.');
         }
     }
    
     var obj = new test($('.btn-1')[0]);
    

    jQuery:

    相比来说,jquery 就方便多了,因为他提供了传入参数的接口:

     class test {
         constructor(btn) {
             $(btn).on('click', {
                 outer_this: this
             }, function(jq_event) {
                 jq_event.data.outer_this.method();
             });
         }
         method() {
             console.log('Calling method.');
         }
     }
    
     var obj = new test($('.btn-1')[0]);
    

    法三、

    还有更简单的,这本来应该是最自然的思路,然而我给想复杂了

     class test {
         constructor(btn) {
         	var that = this;
             $(btn).on('click', function(jq_event) {
                 that.method();
             });
         }
         method() {
             console.log('Calling method.');
         }
     }
    
     var obj = new test($('.btn-1')[0]);
    

    jq 的 on 允许你传入一个对象,当函数被回调时,jq 会调用该函数,并传入一个 jq 的事件对象,其内的 data 属性即为你注册时传入的对象。

    至此为止,问题基本解决。但实际上这都是补救的方法,js 缺少像 python 那样直接声明变量作用域的优雅的关键字,很遗憾啊。

  • 相关阅读:
    redhat 关机注销命令详解
    CentOS网络配置详解
    Red Hat 6网络配置笔记
    H3C三层交换机S5500初始配置+网络访问策略
    python 发邮件 ,转载:https://mp.weixin.qq.com/s/kmNZ04MlDve4AmCCOoT2HA
    解决不能右键查看元素的问题, 转载:https://mp.weixin.qq.com/s/V_fpPN62Kdf0bz6zgFpVCg
    这几点鲜有人知的爬虫技巧,让你爽歪歪 转载:https://mp.weixin.qq.com/s/52luElhn4nRBZCdQMGEhnw
    一个反爬 JS 逆向分析的例子 转载:https://mp.weixin.qq.com/s/2luhB-AhMIzxVh6rPERzCA
    ssh 端口转发 转载:https://mp.weixin.qq.com/s/uesOCt9gmdST-HpwYTKsIw
    爬虫视频
  • 原文地址:https://www.cnblogs.com/gaolihai/p/13149756.html
Copyright © 2011-2022 走看看