• 【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 那样直接声明变量作用域的优雅的关键字,很遗憾啊。

  • 相关阅读:
    矩阵乘法运算test
    c字符数组转整型【c语言复习1】
    (转载)JavaScript的那些书
    数据结构 排序算法
    (转载)给自己降降级你会发现一片广阔的天空
    Cocos2D简介
    JRE not compatible with workspace .class file compatibility: 1.7
    水晶报表问题,请高手指教。
    控件开发该如何入门?
    cnblogsDottext的FTB不生效,只是显示一个textarea标记,为什么呢?
  • 原文地址:https://www.cnblogs.com/gaolihai/p/13149756.html
Copyright © 2020-2023  润新知