换个思路理解Javascript中的this

在网上很多文章都对 Javascript 中的 this 做了详细的介绍,但大多是介绍各个绑定方式或调用方式下 this 的指向,于是我想有一个统一的思路来更好理解 this 指向,使大家更好判断,以下有部分内容不是原理,而是一种解题思路。

从call方法开始

call 方法允许切换函数执行的上下文环境(context),即 this 绑定的对象。

大多数介绍 this 的文章中都会把 call 方法放到最后介绍,但此文我们要把 call 方法放在第一位介绍,并从 call
方法切入来研究 this ,因为 call 函数是显式绑定 this 的指向,我们来看看它如何模拟实现(不考虑传入 null 、
undefined 和原始值):


  1. Function.prototype.call = function(thisArg) { 
  2.     var context = thisArg; 
  3.     var arr = []; 
  4.     var result; 
  5.  
  6.     context.fn = this; 
  7.  
  8.     for (let i = 1, len = arguments.length; i < len; i++) { 
  9.         arr.push('arguments[' + i + ']'); 
  10.     } 
  11.  
  12.     result = eval("context.fn(" + arr + ")"); 
  13.  
  14.     delete context.fn; 
  15.  
  16.     return result; 
  17. }  

从以上代码我们可以看到,把调用 call 方法的函数作为第一个参数对象的方法,此时相当于把第一个参数对象作为函数执行的上下文环境,而
this 是指向函数执行的上下文环境的,因此 this 就指向了第一个参数对象,实现了 call 方法切换函数执行上下文环境的功能。

对象方法中的this

在模拟 call 方法的时候,我们使用了对象方法来改变 this 的指向。调用对象中的方法时,会把对象作为方法的上下文环境来调用。

既然 this 是指向执行函数的上下文环境的,那我们先来研究一下调用函数时的执行上下文情况。

下面我门来看看调用对象方法时执行上下文是如何的:


  1. var foo = { 
  2.     x : 1, 
  3.     getX: function(){ 
  4.         console.log(this.x); 
  5.     } 
  6. foo.getX(); 

从上图中,我们可以看出getX方法的调用者的上下文是foo,因此getX方法中的 this 指向调用者上下文foo,转换成 call 方法为foo.getX.call(foo)。

下面我们把其他函数的调用方式都按调用对象方法的思路来转换。

构造函数中的this


  1. function Foo(){ 
  2.     this.x = 1; 
  3.     this.getX = function(){ 
  4.         console.log(this.x); 
  5.     } 
  6. var foo = new Foo(); 
  7. foo.getX();  

执行 new 如果不考虑原型链,只考虑上下文的切换,就相当于先创建一个空的对象,然后把这个空的对象作为构造函数的上下文,再去执行构造函数,最后返回这个对象。


  1. var newMethod = function(func){ 
  2.     var context = {}; 
  3.     func.call(context); 
  4.     return context; 
  5. function Foo(){ 
  6.     this.x = 1; 
  7.     this.getX = function(){ 
  8.         console.log(this.x); 
  9.     } 
  10. var foo = newMethod(Foo); 
  11. foo.getX(); 

DOM事件处理函数中的this


  1. DOMElement.addEventListener('click', function(){ 
  2.  
  3.   console.log(this); 
  4.  
  5. });  

把函数绑定到DOM事件时,可以当作在DOM上增加一个函数方法,当触发这个事件时调用DOM上对应的事件方法。


  1. DOMElement.clickHandle = function(){ 
  2.     console.log(this); 
  3. DOMElement.clickHandle(); 

普通函数中的this


  1. var x = 1; 
  2. function getX(){ 
  3.     console.log(this.x); 
  4. getX();  

这种情况下,我们创建一个虚拟上下文对象,然后普通函数作为这个虚拟上下文对象的方法调用,此时普通函数中的this就指向了这个虚拟上下文。

那这个虚拟上下文是什么呢?在非严格模式下是全局上下文,浏览器里是 window ,NodeJs里是 Global ;在严格模式下是 undefined 。


  1. var x = 1; 
  2. function getX(){ 
  3.     console.log(this.x); 
  4.  
  5. [viturl context].getX = getX; 
  6. [viturl context].getX(); 

闭包中的this


  1. var x = 1; 
  2. var foo = { 
  3.     x: 2, 
  4.     y: 3, 
  5.     getXY: function(){ 
  6.         (function(){ 
  7.             console.log("x:" + this.x); 
  8.             console.log("y:" + this.y);  
  9.         })(); 
  10.     } 
  11. foo.getXY();  

这段代码的上下文如下图:

这里需要注意的是,我们再研究函数中的 this 指向时,只需要关注 this 所在的函数是如何调用的, this 所在函数外的函数调用都是浮云,是不需要关注的。因此在所有的图示中,我们只需要关注红色框中的内容。

因此这段代码我们关注的部分只有:


  1. (function(){ 
  2.     console.log(this.x); 
  3. })();  

与普通函数调用一样,创建一个虚拟上下文对象,然后普通函数作为这个虚拟上下文对象的方法立即调用,匿名函数中的 this 也就指向了这个虚拟上下文。

参数中的this


  1. var x = 1; 
  2. var foo = { 
  3.     x: 2, 
  4.     getX: function(){ 
  5.         console.log(this.x); 
  6.     } 
  7. setTimeout(foo.getX, 1000);  

函数参数是值传递的,因此上面代码等同于以下代码:


  1. var getX = function(){ 
  2.     console.log(this.x); 
  3. }; 
  4. setTimeout(getX, 1000);  

然后我们又回到了普通函数调用的问题。

全局中的this

全局中的 this 指向全局的上下文


  1. var x = 1; 
  2. console.log(this.x); 

复杂情况下的this


  1. var x = 1; 
  2. var a = { 
  3.     x: 2, 
  4.     b: function(){ 
  5.         return function(){ 
  6.             return function foo(){ 
  7.                 console.log(this.x); 
  8.             }         
  9.         } 
  10.     } 
  11. }; 
  12.  
  13. (function(){ 
  14.     var x = 3; 
  15.     a.b()()(); 
  16. })();  

看到上面的情况是有很多个函数,但我们只需要关注 this 所在函数的调用方式,首先我们来简化一下如下:


  1. var x = 1; 
  2. (function(){ 
  3.     var x = 3; 
  4.     var foo = function(){ 
  5.         console.log(this.x); 
  6.     } 
  7.     foo(); 
  8. });  

this 所在的函数 foo 是个普通函数,我们创建一个虚拟上下文对象,然后普通函数作为这个虚拟上下文对象的方法立即调用。因此这个
this指向了这个虚拟上下文。在非严格模式下是全局上下文,浏览器里是 window ,NodeJs里是 Global ;在严格模式下是
undefined 。

总结

在需要判断 this 的指向时,我们可以安装这种思路来理解:

  • 判断 this 在全局中OR函数中,若在全局中则 this 指向全局,若在函数中则只关注这个函数并继续判断。
  • 判断 this 所在函数是否作为对象方法调用,若是则 this 指向这个对象,否则继续操作。
  • 创建一个虚拟上下文,并把this所在函数作为这个虚拟上下文的方法,此时 this 指向这个虚拟上下文。
  • 在非严格模式下虚拟上下文是全局上下文,浏览器里是 window ,Node.js里是 Global ;在严格模式下是 undefined 。

图示如下:

作者:佚名

来源:51CTO

时间: 2024-12-31 12:13:10

换个思路理解Javascript中的this的相关文章

AJAX入门之深入理解JavaScript中的函数

ajax|javascript|函数 概述 函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解.JavaScript中的函数不同于其他的语言,每个函数都是作为一个对象被维护和运行的.通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递.在继续讲述之前,先看一下函数的使用语法: function func1(-){-}var func2=function(-){-};var func3=function func4(-){-};var f

新手入门:理解JavaScript中函数的使用

javascript|函数 函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解. JavaScript中的函数不同于其他的语言,每个函数都是作为一个对象被维护和运行的.通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递.在继续讲述之前,先看一下函数的使用语法: function func1(-){-} var func2=function(-){-}; var func3=function func4(-){-}; var func5

深入理解Javascript中的this关键字

 这篇文章主要介绍了深入理解Javascript中的this关键字,本文讲解了方法调用模式.函数调用模式.构造器调用模式.apply调用模式中this的不同之处,需要的朋友可以参考下     自从接触javascript以来,对this参数的理解一直是模棱两可.虽有过深入去理解,但却也总感觉是那种浮于表面,没有完全理清头绪. 但对于this参数,确实会让人产生很多误解.那么this参数到底是何方神圣? 理解this this是一个与执行上下文(execution context,也就是作用域)相

深入理解JavaScript中的对象

  这篇文章主要介绍了深入理解JavaScript中的对象,是JS入门学习中的基础知识,需要的朋友可以参考下 JavaScript是一种面向对象编程(OOP)语言.一种编程语言可以被称为面向对象的,它为开发者提供了四种基本功能: 封装 - 存储相关的信息,无论是数据或方法,还是对象 聚合 - 存储一个对象到另一个对象的内部 继承 - 类的能力依赖于另一个类(或类数),用于其部分的属性和方法 多态性 - 编写函数或者方法,在各种不同的方式工作 对象是由属性.如果属性包含一个函数,它被认为是一个对象

如何理解 JavaScript 中的 Promise 机制

本文讲的是如何理解 JavaScript 中的 Promise 机制, Promise 的世界 原生 Promises 是在 ES2015 对 JavaScript 做出最大的改变.它的出现消除了采用 callback 机制的很多潜在问题,并允许我们采用近乎同步的逻辑去写异步代码. 可以说 promises 和 generators ,代表了异步编程的新标准.不论你是否用它,你都得 必须 明白它们究竟是什么. Promise 提供了相当简单的 API ,但也增加了一点学习曲线.如果你以前从没见过

深入理解JavaScript中的call、apply、bind方法的区别_javascript技巧

在JavaScript 中,this的指向是动态变化的,很可能在写程序的过程中,无意中破坏掉this的指向,所以我们需要一种可以把this的含义固定的技术,于是就有了call,apply 和bind这三个方法,来改变函数体内部 this 的指向,因为函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念 apply.call apply:应用某一对象的一个方法,用另一个对象替换当前对象 call:调用一个对象的一个方法,以另一个对象替换当前对象 function pers

深入理解Javascript中的自执行匿名函数_基础知识

格式: (function(){ //代码 })(); 解释:这是相当优雅的代码(如果你首次看见可能会一头雾水:)),包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数. 来个带参数的例子: (function(arg){ alert(arg+100); })(20); // 这个例子返回120. 回来看看jquery的插件编写 (function($) { // Code goes here })(jQuery

AJAX入门之深入理解JavaScript中的函数_AJAX相关

概述 函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解.JavaScript中的函数不同于其他的语言,每个函数都是作为一个对象被维护和运行的.通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递.在继续讲述之前,先看一下函数的使用语法:  function func1(-){-}var func2=function(-){-};var func3=function func4(-){-};var func5=new Function(

深入理解JavaScript中为什么string可以拥有方法_javascript技巧

引子 我们都知道,JavaScript数据类型分两大类,基本类型(或者称原始类型)和引用类型. 基本类型的值是保存在栈内存中的简单数据段,它们是按值访问的.JS中有五种基本类型:Undefined.Null.Boolean.Number和String. 引用类型的值是保存在堆内存中的对象,它的值是按引用访问的.引用类型主要有Object.Array.Function.RegExp.Date. 对象是拥有属性和方法的,所以我们看到下面这段代码一点也不奇怪. var favs=['鸡蛋','莲蓬']