跳到主要内容

33、js的作用域和this

无论是javascript, 还是 emscript, 变量的作用域都属于高级知识。 我们想考察一个js程序员的水平如何,可以直接用作用域来提问。

同时,我们在实际的开发中发现,很多js/emscript 的新人,对于作用域和 this 都很含混,所以这里要单独的提一下。

作用域

无论是javascript, 还是 emscript, 对于作用域的使用基本是一样的。 后者更加严密一些。 我们看几个例子。

1、 全局变量,可以直接引用;

//全局变量 a: 
var a = 1; 
function one() { 
  console.info(a) 
} 

打印结果是 1

2、 函数内的普通变量;

function two(a ){ 
  console.info('a is' + a) 
} 

运行:

two(2) 打印结果是 : a is 2

3、 普通函数可以对全局变量做赋值如下图所示:

var a = 1; 
function four(){ 
  console.info(' in four, before a=4: ' + a) 
  if(true) a = 4; 
  console.info(' in four, after a=4: ' + a) 
} 

运行:

four(4) 

结果:

in four, before a=4: 1     ( 这个是符合正常的scope逻辑的。) 
in four, after a=4: 4      ( 这个也是符合) 

再运行: console.info(a), 可以看到输出: 4 说明 全局变量 a 在 four()函数中已经被发生了永久的变化。

4、 通过元编程定义的函数;

var six = ( function(){ 
  var foo = 6; 
  return function(){ 
    return foo; 
  } 
} 
)(); 

在上述代码中, js解析器会先运行(忽略最后的 () ):

var temp = function(){ 
  var foo = 6; 
  return function(){ 
    return foo; 
  } 
} 

然后再运行: var six = (temp)() , 所以, six 就是:

function(){ 
    return foo; 
} 

上面的foo 就是来自于方法最开始定义的 var foo = 6, 而这个变量的定义,是在一个 function() 中的。 所以它不是一个全局变量。

所以,如果我们在console中输入 foo , 会看到报错消息: Uncaught ReferenceError: foo is not defined

5、 通过元编程定义的函数中的变量,不会污染全局变量;

var foo = 1; 

var six = ( function(){ 
  var foo = 6; 
  return function(){ 
    console.info("in six, foo is: " + foo); 
  } 
} 
)(); 

在上面的代码中,我们先定义了一个 全局变量 foo, 再定义了一个方法 six, 里面定义了一个临时方法 foo. 并且进行来了一些操作。

运行:

six()   // 返回:   in six, foo is: 6 
foo     // 返回: 1 

this

对于this 的使用,

在这里 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

指出了对于javascript中this的详细用法。

在emscript中也基本是一样的。

简单的说,大家只要记得, this 指的是当前作用域的对象实例就好了。

var apple = { 
  color: 'red', 
  show_color: function() { 
    return this.color 
  } 
} 

我们输入 apple.show_color 就可以看到输出 red. 这里的 this 指的就是 apple 变量。

实战经验

1.在Vue的方法定义中容易用错。

当我们发现 代码看起来没问题, 但是console 总报错说 xx undefined 时, 十有八九就是 忘记加 this 了。

例如:

<html> 
<head> 
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> 
</head> 
<body> 
    <div id='app'> 
        {{ message }} 
        <br/> 
        <button v-on:click='highlight' style='margin-top: 50px'>真的吗</button> 
    </div> 

    <script> 
        var app = new Vue({ 
            el: '#app', 
            data: { 
                message: 'this 是很重要的。 不要忘记它哟~' 
            }, 
            methods:  { 
                highlight: function() { 
                    // 报错:  message is not defined. 
                    message += '是的, 工资还会涨~!' 

                    // 正确的代码应该是: 
                    // this.message += '是的, 工资还会涨~!' 
                } 
            } 

        }) 
    </script> 
</body> 
</html> 

使用浏览器加载上述代码,我们会发现报错了:

 

上面的代码中, message += ... 那一行中, message 是当前的vue的实例的一个”property”(属性), 而我们如果希望在 methods 中引用这个属性的话,就需要用 this.message 才对。 这里的 this 对应的就是 var app = new Vue() 中定义的 app.

2.在发起http请求时容易用错

我们看下面的例子,是一段代码片段:

new Vue({ 
    data: { 
        cities: [...] 
    }, 
    methods: { 
        my_http_request: function(){ 
            let that = this 
            axios.get('http://mysite.com/my_api.do') 
            .then(function(response){ 
                // 这里不能使用 this.cities 来赋值 
                that.cities = response.data.result 
            }) 
        }    
    } 
}) 

在上面的代码中,我们定义了一个属性: cities, 定义了一个方法: my_http_request. 该方法会向远程发起一个请求,然后把返回的response中的值赋给 cities.

可以在上面代码中看到, 需要先在 axios.get之前,定义一个变量 let that = this. 这个时候, thisthat 都处于 “Vue”的实例中。

但是在axios.get(..).then() 函数中,就不能再使用this 了。 因为在 then(...) 中,这是个function callback, 其中的 this 会代表这个 http request event. 这是个事件。

所以,只能用这样的方式。

(如果使用了 emscript 的 => 的话,就可以避免上述问题)

3.在event handler中容易用错

道理同上。

版权声明:「DDKK.COM 弟弟快看,程序员编程资料站」本站文章,版权归原作者所有