原文:Scope and this in JavaScript

今天我想简单讨论下关于JavaScript的作用域和this变量。“作用域”的概念就是说。我们的代码能够从哪里去訪问某些函数或者变量。也就是它们所存在的上下文。或者说就是它们被运行的地方。

你可能已经见过有的人写相似这样的代码:

function someFunc() {
var _this = this;
something.on("click", function() {
console.log(_this);
});
};

但是你却搞不懂当中“var _this=this;”这一句究竟是要干嘛。希望本篇文章能澄清这样的疑惑。

第一种作用域是全局作用域(Global  Scope),它的定义非常easy。假设一个函数或者变量是全局的,那么在不论什么地方都能够訪问它们。在浏览器里,全局作用域事实上就是window对象。所以假设你在代码里这样定义一个变量:

var x = 9;

那么实际上你是在window对象上创建一个叫x的属性,并给它赋值为9。当然假设你愿意也能够这样写:

window.x = 9;

只是既然它是全局变量,一般人都不会这样做。

window对象是全局的。它上面的全部属性都能够在代码里的不论什么地方直接訪问到。

第二种。也是最后一种作用域。就是本地作用域(Local Scope)。JavaScript在函数水平上创建本地作用域,比方:

function myFunc() {
var x = 5;
};
console.log(x); //undefined

由于x是在myFunc()之内声明的。所以它仅仅在myFunc()内是能够訪问到的。

一点小提醒:

假设你没有使用varkeyword来声明一个变量,那么它会自己主动变成全局的。

所以这样写也是可行的:

function myFunc() {
x = 5;
});
console.log(x); //5

但这并非一个好主意。这样做会把全局作用域搞乱,所以这样的做法非常不推荐。你应该尽量少地在全局作用域里声明变量。之所以像jQuery一类的函数库会这样做,也是出于这个原因:

(function() {
var jQuery = { /* all my methods go here */ };
window.jQuery = jQuery.
})();

先把全部的东西都放在一个匿名函数的函数体里。然后再立马运行这个函数,这样一来,在这个函数里声明的全部变量就都仅仅存在于本地作用域了。而在最后,你再把jQuery对象绑定到window对象上,这样jQuery就是全局的了,而你也就进而把全部的东西变成是全局可訪问到的了(译者注:这些变量函数什么的仍然存在于本地作用域,所以不能从外面直接訪问。当然,要訪问的话。仅仅能通过这个唯一的全局的接口:jQuery,这就是所谓的闭包的最大功德)。虽然在这段演示样例代码里我把jQuery简化到了不能再简化的地步,但是本质上说。它的源代码的工作原理就是这样的。

假设你想知道很多其它细节,强烈推荐你读一下Paul
Irish的文章:10 Things I learned from the jQuery Source

由于本地作用域以函数为单位。所以在一个函数内定义的函数,是能够訪问外面这个包括它的函数的本地变量的:

function outer() {
var x = 5;
function inner() {
console.log(x); //5
}
inner();
}

只是反过来却不行,outer()函数并不能訪问inner()里面的不论什么变量:

function outer() {
var x = 5;
function inner() {
console.log(x); //5
var y = 10;
}
inner();
console.log(y); //undefined
}

眼下来看,这都是些非常easy非常基本的东西。

只是,假设我们要来审视一番thiskeyword。情况就变复杂非常多了,我想咱们都遇见过这样的情况(译者注:关于这里有争议,见后面注解【1】。):

$("myLink").on("click", function() {
console.log(this); //points to myLink (as expected)
$.ajax({
//ajax set up
success: function() {
console.log(this); //points to the global object. Huh? }
});
});

每次在你的函数被运行的时候,this变量都是被自己主动赋值的,至于它的具体值究竟是什么,这取决于该函数被呼叫的方式。

JavaScript里面有几种基本的呼叫函数的方式,我并不打算如今在这里一一叙述,只是经常使用的就那么三种:作为一个对象的方法被呼叫;或者作为函数独自被呼叫;或者作为一个事件的处理器(event handler)被呼叫。不同的呼叫方式将导致this的值是不同的:

function foo() {
console.log(this); //global object 译者注:事实上就是window
}; myapp = {};
myapp.foo = function() {
console.log(this); //points to myapp object
} var link = document.getElementById("myId");
link.addEventListener("click", function() {
console.log(this); //points to link
}, false);

情况一目了然。

MDN对于第三种情况有具体的解释:

通常来说。在一个事件处理器被运行过程中,我们都希望能追踪到触发这个事件的对象。尤其是有时候可能会在若干个相似的对象上绑定同一个事件处理器(译者注:比方说你在一系列链接<a>对象上绑定了同一个处理click事件的处理器)。当我们用addEventListener()来绑定一个函数的时候,this的值会被改变,注意:this的值实际上是由呼叫者传递给函数的。

所以。如今,再回过头来看一開始的关于“var _this = this;”这一句的用意的疑问。我们才猛然发现已经离答案不远了。

$("#myLink").on("click", function() {})这句的意图是当这个DOM元素被点击时。这个函数便被运行。但是由于这个函数是作为一个事件处理器被呼叫的,所以this变量会指向ID为myLink的DOM元素。而你在Ajax请求里指定的success方法仅仅是一个常规的函数,所以当它被运行的时候。this被赋值为全局对象。(译者注:关于这里有争议,见后面注解【1】)

上述原因就是为什么你总会见到有人写:var _this = this或者var that = this,或者相似的东西(见后面注解【2】),这样做的目的是把当前this的值备份留作以后不时之需。关于以下这段代码里第二个console.log()究竟应该输出什么值,非常多人提出了异议,这个问题我以后再讨论(译者注:看来,作者本人没回避这个问题。我也是在后面提出异议者之中的一个)。

$("myLink").on("click", function() {
console.log(this); //points to myLink (as expected)
var _this = this; //store reference
$.ajax({
//ajax set up
success: function() {
console.log(this); //points to the global object. Huh?
console.log(_this); //better!
}
});
});

另外也有一些呼叫函数的方法是能够主动明白地指定this的值的,只是由于这篇文章如今已经够长了,所以不如等改天再具体讨论吧。

如有问题请留言,我会一一回复。

注解【1】:
这个问题已经有人在原文以下的评论中提到,就是
success: function() {
console.log(this); //points to the global object. Huh?
}
输出的并非window,而是另外一个对象。看起来是jQuery用来记录Ajax设定參数的一个对象。

假设说我在一个载入了jQuery的网页里插入了一个id为vince的<a>标签,然后我运行:

$("#vince").on("click", function() {
console.log(this); // options :
var my_city="Washington,USA";
var my_key="xxxxxxxxxxxxxxxxxxxxxx";
var no_of_days=2;
// build URI:
var uri="http://free.worldweatheronline.com/feed/weather.ashx? q="+my_city+"&key="+my_key+"&format=json&no_of_days="+no_of_days+"&includeLocation=yes";
// uri-encode it to prevent errors :
uri=encodeURI(uri); $.ajax({
type: "POST",
url: uri,
complete: function() {
console.log(this);
}
});
});

我本想模拟用Ajax訪问一个公共的而且仍然活跃的web service,用来做这个演示。而不是用我本地的server。但是找了半天也找不到,最后找到一个提供天气情况的,但是须要注冊才有API码。我没有时间去完毕注冊,所以上面的my_key是“xxxxx”。因此。这个Ajax请求将不会成功返回。

所以我没有像原文那样使用success事件处理函数,而是使用了complete事件。这样不论怎样保证它都会被调用。那么看到的结果事实上是:

实际上关于这个被输出的东西究竟是怎么来的,我们能够在Chrome里面用单步调试的方法来追踪,只是前提是,要载入非min版本号的jQuery源代码,不然没有人看的懂,所以这须要时间。我想还是等我比較闲的时候再来搞吧。

注解【2】:

你多数会见到的一种做法应该是用self,"self = this;"这样的写法是眼下比較流行的。

JavaScript中的作用域以及this变量的更多相关文章

  1. JavaScript中的作用域

    很多(JavaScript)开发者都在讨论"作用域",但它是什么?它们在JavaScript中的任何地方!我发现很多年轻的开发者不知道作用域是什么.他们中大多数人可以用jQuery ...

  2. 【翻译】JavaScript中的作用域和声明提前

    原文:http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html ===翻译开始=== 你知道下面的JavaScript脚本执 ...

  3. JavaScript中的作用域和声明提前

    [翻译]JavaScript中的作用域和声明提前 原文:http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html ===翻译 ...

  4. JavaScript中var和this定义变量的区别

    JavaScript中var和this定义变量的区别 在js中声明变量时可以使用var和this,但使用this的有很大一部分参考书是没有的,经过查阅相关资料总结如下: 用var和this声明变量,存 ...

  5. 认识javascript中的作用域和上下文

    javascript中的作用域(scope)和上下文(context)是这门语言的独到之处,这部分归功于他们带来的灵活性.每个函数有不同的变量上下文和作用域.这些概念是javascript中一些强大的 ...

  6. 漫谈JavaScript中的作用域(scope)

    什么是作用域 程序的执行,离不开作用域,也必须在作用域中才能将代码正确的执行. 所以作用域到底是什么,通俗的说,可以这样理解:作用域就是定义变量的位置,是变量和函数的可访问范围,控制着变量和函数的可见 ...

  7. 深入理解JavaScript中的作用域和上下文

    介绍 JavaScript中有一个被称为作用域(Scope)的特性.虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,我会尽我所能用最简单的方式来解释作用域.理解作用域将使你的代码脱颖而出,减 ...

  8. 认识Javascript中的作用域和作用域链

    作用域 只要写过java或者c#等语言的同学来说,相信一定能理解作用域的概念,在作用域的范围中,我们可以使用这个作用域的变量,对这个变量进行各种操作.可是,当使用Javascript的时候,相信很多的 ...

  9. JavaScript中的作用域与函数和变量声明的提升

      var foo = 1; function bar() {     if (!foo) {         var foo = 10;     }     alert(foo); } bar(); ...

随机推荐

  1. 《开源框架那些事儿22》:UI框架设计实战

    UI是User Interface的缩写.通常被觉得是MVC中View的部分,作用是提供跟人机交互的可视化操作界面. MVC中Model提供内容给UI进行渲染,用户通过UI框架产生响应,一般而言会由控 ...

  2. Oracle 11g 的bug?: aix 上,expdp 11.2.0.1 导出,impdp 11.2.0.3 导入,Interval 分区的 【Interval】 分区属性成了【N】

    如题: Oracle 11g 的bug?: aix 上,expdp 11.2.0.1 导出,impdp 11.2.0.3 导入,Interval 分区的 [Interval] 分区属性成了[N] 谨记 ...

  3. Unity对象查找

    1. GameObject.Find  全局摄像机 全局画布 全局灯光 无法查找隐藏对象 ,效率低下,要用完全的路径来提升查找效率 2. transform.Find  UI中全部使用此方法 可以查找 ...

  4. 好的 Web 前端年薪会有多少?

    只是一个范围参考,主要是看能力而定. 1. 切图熟练.能写一些JS效果(HTML+CSS+jQuery):5K~10K+2. 具备第一条,并可以熟练用JS开发各种组件:8K-15K+3. 具备第二条, ...

  5. HTTP 错误 404.3 解决

    问题 由于扩展配置问题而无法提供您请求的页面.如果该页面是脚本,请添加处理程序.如果应下载文件,请添加 MIME 映射. 解决 在开始菜单中找到 Visual Studio 命令提示工具.然后用管理员 ...

  6. 工欲善其事 之 Web 前端调试工具格式化混淆过的 JS 代码

    工欲善其事 之 Web 前端调试工具格式化混淆过的 JS 代码 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致&q ...

  7. mybatis由浅入深day02_7查询缓存_7.2一级缓存_一级缓存应用

    7 查询缓存 7.1 什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存. 一级缓存是SqlSession级别的缓存.在操作数据库时 ...

  8. Int 型数值存储

    1. Int 在计算机中占 4 Byte, 共 32 位, 最大正数为 2147483647, 最小负数为 -2147483648 2. 正数存储在计算机中的形式为原码,最大正数的十六进制形式为 0X ...

  9. Windows 端口占用

    1.netstat -ano | findstr "80"( 80为提示被占用的端口): 2.tasklist | findstr "5584"(5584是从上 ...

  10. 多进程模块:multiprocessing

    多进程: (1) 前面我们学习的多线程,其实算不上真正的多线程,即使你开了很多个线程,在同一时间内只能有一个CPU核数来处理一个线程(2) 在 python 中,多进程算得上是真正的多线程,假设你的C ...