常用Javascript的人都知道,[this這個關鍵字在一個函式內究竟指向誰]的這個問題很令人頭大,本人在這裡整理了一下Javascript中this的指向的五種不同情況,其中前三種屬於基本的情況,而後兩種情況可基於前三種情況的方式來進行思考。

1.this指向於調用該函式之物件

如果你有學過C/C++,你可能會記得一個物件內的成員函式裡的this指的即是該成員函式所在之物件,但在Javascript裡則有那麼些許不同,Javascript裡的this看的是究竟是誰調用該函式,而不是看該函式被定義在哪個物件內,這個大原則抓到了,基本上就已經可以探知this的奧秘了。底下寫一下這種情況的公式與範例:

公式

  1. 1
  1. 物件.函式(); //函式內的this指向該物件

範例

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  1. var obj = {
  2. x: 20,
  3. f: function(){ console.log(this.x); }
  4. };
  5.  
  6. obj.f(); //由於調用f函式時,點前面物件為obj,故f內的this指向obj,則輸出為20。
  7.  
  8. obj.innerobj = {
  9. x: 30,
  10. f: function(){ console.log(this.x); }
  11. }
  12.  
  13. obj.innerobj.f(); //由於調用f函式時,點前面物件為obj.innerobj,故f內的this指向obj.innerobj,則輸出為30。

2.this指向全域物件(瀏覽器:window物件、node.js:GLOBAL物件)

如果調用函式的前方並未有物件,則函式內this就指向全域物件。在瀏覽器內全域物件為window物件,而在node.js中全域物件則為GLOBAL物件。底下一樣寫一下這種情況的公式與範例:

公式

  1. 1
  1. 函式(); //函式內的this指向全域物件

範例

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  1. var x = 10;
  2. var f = function(){
  3. console.log(this.x);
  4. };
  5.  
  6. f(); //由於調用f函式時,前方並未有[物件.]的形式,故f內的this指向全域物件,則輸出全域變數的x(10)。

例外:在使用node.js時,若使用node file.js這樣的方式執行js檔,並不會讓宣告的全域變數掛在全域物件上(意指會利用function將code整個包起來執行),故輸出應為undefined。

前兩種情況常見誤導範例

範例一、物件之成員函式內有函式(感謝NSLin在實務讀書會上的範例Code)

example1.js

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  1. var x = 10;
  2. var obj = {
  3. x: 20,
  4. f: function(){
  5. console.log(this.x);
  6. var foo = function(){ console.log(this.x); }
  7. foo(); // (2)
  8. }
  9. };
  10.  
  11. obj.f(); // (1)

這個範例會輸出多少呢?別忘記大原則,在Javascript裡的this看的是究竟是誰調用該函式,故並不會輸出20 20,而是輸出20 10,為什麼呢?因為(1)obj.f()調用時,f前面物件為obj,故f內的this指向obj。但因為調用f內的(2)foo函式時是用foo(),調用的前方並未有物件,故foo內的this指向全域物件,所以輸出會是全域變數的x的值。

若要讓foo內使用obj.x的值,解法如下:

example1.js

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  1. var x = 10;
  2. var obj = {
  3. x: 20,
  4. f: function(){
  5. console.log(this.x);
  6. var that = this; //使用that保留在這個函式內的this
  7. var foo = function(){ console.log(that.x); } //使用that取得obj
  8. foo();
  9. }
  10. };
  11.  
  12. obj.f();

範例二、借用函式

example2.js

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  1. var x = 10;
  2. var obj = {
  3. x: 20,
  4. f: function(){ console.log(this.x); }
  5. };
  6.  
  7. obj.f(); // (1)
  8.  
  9. var fOut = obj.f;
  10. fOut(); //(2)
  11.  
  12. var obj2 = {
  13. x: 30,
  14. f: obj.f
  15. }
  16.  
  17. obj2.f(); // (3)

範例中三次調用之函式的this所指向的物件為何,不知道各位能不能看得出來。雖然用的是同一個函式,但是因為調用的不同,故this所指向的物件就不同。(1)obj.f()的f所指向的是obj,這比較沒有問題,輸出的會是20;而(2)fOut()裡的this,則是因為調用時前方無物件,則this所指的是全域物件,輸出的會是10;最後(3)obj2.f()則是obj2去呼叫f,故f內的this指向的是obj2,輸出的會是30。

3.this指向利用call或apply所指派給this的物件

有個方法可以更動前兩種敘述所讓this指派的值,就是利用call與apply。call與apply都是呼叫該函式並讓該函式的this指向給予call或apply的第一個參數。至於call和apply的差別則是在於其後面給予被調用之函式的參數放入的方法不同,一個是直接攤平放在第二個以後的參數;一個是直接放入一個裡面放要給予之參數的陣列。底下一樣看一下公式和範例:

公式

  1. 1
  2. 2
  1. (A物件.)函式.call(B物件,參數1,參數2,參數3, ......); //函式的this指向B物件(若B物件為null,則指向全域物件)
  2. (A物件.)函式.apply(B物件,[參數1,參數2,參數3, ......]); //函式的this指向B物件(若B物件為null,則指向全域物件)

範例

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  1. var obj = {
  2. x: 20;
  3. f: function(){ console.log(this.x); }
  4. };
  5.  
  6. var obj2 = {
  7. x: 30;
  8. };
  9.  
  10. obj1.f.call(obj2); //利用call指派f的this為指向obj2,故輸出為30

4.this指向new所產生之新物件

若將函式當作建構式(constructor)來用,則內部的this則指向於new所產生之新物件。

公式

  1. 1
  1. new 建構式(); //建構式內之this指向new所產生之新物件

範例

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  1. function Monster(){
  2. this.hp = 100;
  3. };
  4.  
  5. var monster = new Monster(); //Monster的this指向new出來之新物件並回傳回來,new的寫法就類似於下面的寫法。
  6. var monster = (function(){
  7. var _new = { constructor: Monster, __proto__: Monster.prototype }; //在IE內可能不相似
  8. _new.constructor(); //這也是為何說可以利用前三種情況來變化的原因,constructor呼叫時,this指向的即是_new這個物件。
  9. return _new;
  10. })();

5.callback函式內的this會指向於調用放入該callback的函式之this所指向之物件

先想想在jQuery中,我們若要讓#button這個元素被click的時候,內容改為“Clicked”這樣的字串,該如何寫呢?

clicked.js

  1. 1
  2. 2
  3. 3
  1. $('#button').click(function(){
  2. this.html("Clicked");
  3. })

此時這個this居然會指向$(‘#button’)這個物件,感覺很自然,但實際想想會覺得很神奇。假設你寫一個function,它可以吃一個function,並在裡面呼叫傳入的function,你該怎麼寫呢?

function-to-function.js

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  1. var f = function(innerf){
  2. //前面的處理
  3. innerf(arg1, arg2, arg3, ......);
  4. //後面的處理
  5. }

但如果這樣寫的話,innerf裡的this根據前述規則就應該是全域物件了!那為什麼常常別人實作的callback函式可讓this指向於調用放入該callback的函式之this所指向之物件呢?這表示大家實作上會遵守一個規則,會將自己的this傳給callback當作它的this來用!這也是為什麼我說這個情況其實也是前三種情況的變化而已了!所以上面的code應該改成如下的形式會比較好:

function-to-function-improved.js

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  1. var f = function(innerf){
  2. //前面的處理
  3. innerf.call(this, arg1, arg2, arg3, ......);
  4. //或是innerf.apply(this, [arg1, arg2, arg3, ......])
  5. //後面的處理
  6. }

參考資料

  1. Javascript的this用法:http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html
  2. [图解] 你不知道的 JavaScript - “this”:http://www.cnblogs.com/ruxpinsp1/archive/2008/04/20/1162463.html

#Javascript:this用法整理的更多相关文章

  1. JavaScript this用法总结(**************************************)

    JavaScript this用法总结 在JavaScript中,this关键字可以说是最复杂的机制之一.对this的作用机制缺乏比较深入的理解很容易在实际开发中出现问题. 1.this的作用 为什么 ...

  2. Spring JdbcTemplate用法整理

    Spring JdbcTemplate用法整理: xml: <?xml version="1.0" encoding="UTF-8"?> <b ...

  3. linq用法整理

    linq用法整理 普通查询 var highScores = from student in students where student.ExamScores[exam] > score se ...

  4. linux学习:特殊符号,数学运算,图像与数组与部分终端命令用法整理

    指令:let.expr.array.convert.tput.date.read.md5.ln.apt.系统信息 一:特殊符号用法整理 系统变量 $# 是传给脚本的参数个数 $0 是脚本本身的名字 $ ...

  5. #ifndef#define#endif的用法(整理)

    [转] #ifndef#define#endif的用法(整理)    原作者:icwk  文件中的#ifndef 头件的中的#ifndef,这是一个很关键的东西.比如你有两个C文件,这两个C文件都in ...

  6. Javascript Array 方法整理

    Javascript Array 方法整理 Javascript 数组相关方法 说明 大多数其它编程语言不允许改变数组大小,越界访问索引会报错,但是 javascript不会报错,不过不建议直接修改a ...

  7. Google Guava 库用法整理<转>

    参考: http://codemunchies.com/2009/10/beautiful-code-with-google-collections-guava-and-static-imports- ...

  8. MySQL中使用SHOW PROFILE命令分析性能的用法整理(配合explain效果更好,可以作为优化周期性检查)

    这篇文章主要介绍了MySQL中使用show profile命令分析性能的用法整理,show profiles是数据库性能优化的常用命令,需要的朋友可以参考下   show profile是由Jerem ...

  9. 《JavaScript实用效果整理》系列分享专栏

    整理一些使用的JavaScript效果,在Web开发中遇到的比较好的动态效果,都收藏在这里,对以后的网站开发增加不少的色彩 <JavaScript实用效果整理>已整理成PDF文档,点击可直 ...

  10. Android spannableStringBuilder用法整理

    Android spannableStringBuilder用法整理 分类: Android开发2013-11-29 10:58 5009人阅读 评论(0) 收藏 举报 Androidspannabl ...

随机推荐

  1. VMware ESXI5.0的安装配置 zz

    http://www.hotxf.com/thread-297-1-1.html 1,    Vmware ESXI 光盘一张文件大小290M,本教程是以 5.0为案例. 2,    所需要安装的操作 ...

  2. SQLServer使用表值参数,高性能批量插入数据

    记得前段时间帮同事写了个解析账号并入库的小工具,来批量导入账号信息,账号量相当大,程序每读取一条记录便执行一次insert来插入数据,整整跑了一下午才把账号全部入库. 今天又接到同事类似的需求,不过这 ...

  3. HTTP消息中header头部信息的讲解

    HTTP Request的Header信息 1.HTTP请求方式 如下表: GET 向Web服务器请求一个文件 POST 向Web服务器发送数据让Web服务器进行处理 PUT 向Web服务器发送数据并 ...

  4. POJ 1142 Smith Numbers(史密斯数)

    Description 题目描述 While skimming his phone directory in 1982, Albert Wilansky, a mathematician of Leh ...

  5. 异步上传图片,光用jquery不行,得用jquery.form.js插件

    异步上传图片,光用jquery不行,得用jquery.form.js插件,百度一下下载这个插件,加jquery,引入就可以了 <form id="postbackground" ...

  6. 领域驱动设计系列文章——浅析VO、DTO、DO、PO的概念、区别和用处

    本篇文章主要讨论一下我们经常会用到的一些对象:VO.DTO.DO和PO. 由于不同的项目和开发人员有不同的命名习惯,这里我首先对上述的概念进行一个简单描述,名字只是个标识,我们重点关注其概念: 概念: ...

  7. BestRW团队项目创意以及NABCD

    一.写在前面 这次的团队项目我们队选到的是自由选题,与其说是选,不如说是分配.毕竟我们组游戏排名倒数第二···其实当我第一次听说我们队排倒数第二的时候我是有点不爽的,毕竟在这后面能够抽到的题目都是剩下 ...

  8. iOS - OC 基本语法

    1.常见文件扩展名 .c C 语言源文件 .cc..cpp C++ 语言源文件 .m Objective-C 源文件 .mm Objective-C++ 源文件 .h 头文件 .pl Perl 源文件 ...

  9. 百度编辑器Ueditor的简单调用

    先去ueditou.baidu.com网站下载百度编辑器,放到项目根目录下的Data目录中,然后引入文件 <!DOCTYPE html> <html lang="en&qu ...

  10. Android应用程序构成

    一个Android应用程序一般是由以下4个组件构成的: 活动(Activity) 意图(Intent) 服务(Service) 内容提供器(Content Provider) 这4个组件是构成andr ...