lambda 表达式中的 this 与普通情况下的 this 指向

  • Java
  • JavaScript
    • this 绑定
    • 总结与提醒

  很多编程语言都支持 lambda 表达式,不过对于不同编程语言,其 lambda 表达式中 this 指向差异很大,有些甚至相反。下面举例如下。

Java

  对于 Java 语言,很普通情况下的 this 很好理解,这里不在详述。值得一提的是 Java 中的内部类。

  Java 的内部类有:成员内部类、局部内部类、匿名内部类、静态内部类。这里只讲述匿名内部类,因为其它的内部类中的 this 指向都不易混淆,所以从略。

  • 对于 Java 中的匿名内部类,编译器会自动生成它的类名,而自动生成的类名为外部类类名$数字。而匿名内部类中的 this,将指向的是这个内部类对象。

  • 对于 Java 中的 lambda 表达式中的 this,指向的是 lambda 表达式所在类的对象。也就是说,对一个类来说,lambda 表达式中的 this,与普通表达式中的 this,没有任何区别。

JavaScript

  关于 JavaSript 中的 this 使用,一般分为普通表达式中的 this、匿名函数中的 this 和箭头函数中的 this。

  注意:JavaScript 中的匿名函数与箭头函数不是同一个东西。JavaScript 中的匿名函数又叫 lambda 函数,但从形式上,JavaScript 中的箭头函数才像 Java 中的 lambda 表达式。例如。

/*** 常规函数*/
function showName() {console.log(this.name);
}/*** 匿名函数(lambda 函数)*/
let showSex = function() {console.log(this.sex);
}/*** 箭头函数*/
let showAge = () => console.log(this.age);
  • 对于普通表达式中的 this 所处的位置,可以分为以下情况:

    • 含 this 的表达式没有位于任何函数中。在这种情况,此 this 指向对象 window(非严格模式下)或为 undefined(严格模式下)。

    • 含 this 的表达式位于一个函数中。在这种情况,此 this 指向调用该函数的对象。

      特别地,当 this 直接位于一个构造函数中(即此 this 外最近一层的函数为构造函数),此 this 指向该构造函数所创建的对象(即,视调用构造函数的对象为该构造函数所创建的对象)。

      注意:不能武断地通过判断此含 this 的表达式所处的位置来断定 this 的指向。即,不能认为此含 this 的表达式位于某个对象内,就认为该 this 指向那个对象。判断 this 的指向还需要通过判断含 this 的函数在哪里被调用。

    例如(假设下面的代码均位于非严格模式下):

    window.color = 'red';
    let obj = {color: 'blue'
    };function sayColor() {console.log(this.color);
    }/*** 没有使用对象来调用 sayColor(),this 默认指向 window。*/
    sayColor(); // redobj.sayColor = sayColor;
    /*** 使用对象来调用 sayColor(),this 指向对象 obj。*/
    obj.sayColor(); // blue
    
  • 对于匿名函数中的 this,它与普通表达式中的 this 的指向规则是一样的,因此此处不再详述。

  • 对于箭头函数中的 this,其指向直接包含此箭头函数定义的函数所属的对象。

    【注意】

    • 是包含箭头函数的函数,不是包含 this 的箭头函数。

    • “直接”指的是包含箭头函数的函数必须是箭头函数外的最近一层的函数。

    • 当箭头函数没有位于某一个函数中时,此处的“函数”理解为“上下文”。

    不过,由于 JavaScript 中的函数的归属可以改变,因此此处也不能直接通过判断此含 this 的表达式所处的位置来断定 this 的指向,还是要看含该箭头函数的函数被调用的位置。

    例如,对于普通情况下:

    window.color = 'red';
    let obj = {  color: 'blue'
    };/*** 原箭头函数没有位于某一个函数中,而位于全局*/
    let sayColorGlobalArrowFun = () => console.log(this.color);
    /*** 原箭头函数没有位于某一个函数中,那就更没有该函数显式的所属对象了,* 因此默认所属对象为 window。于是 this 恒指向 window*/
    sayColorGlobalArrowFun(); // redobj.sayColorGlobalArrowFun = sayColorGlobalArrowFun;
    /*** 原箭头函数没有位于某一个函数中,那就更没有该函数显式的所属对象了,* 因此默认所属对象为 window。于是 this 恒指向 window*/
    obj.sayColorGlobalArrowFun(); // redfunction sayColorNorFun() { // NorFun:Normal Function/*** 原箭头函数代码位于函数 sayColorNorFun 中*/let sayColorLocalArrowFun = () => console.log(this.color);            sayColorLocalArrowFun();
    }
    /*** 原箭头函数代码位于函数 sayColorNorFun 中,* 而此处函数 sayColorNorFun 属于对象 window。* 于是 this 指向 window*/
    sayColorNorFun(); // redobj.sayColorNorFun = sayColorNorFun;
    /*** 原箭头函数代码位于函数 sayColorNorFun 中,* 而此处函数 sayColorNorFun 属于对象 obj。* 于是 this 指向 obj*/
    obj.sayColorNorFun(); // blue
    

    对于将包含箭头函数的函数作为构造函数时:

    function King() {/*** this 直接位于一个普通函数中。在本文后面的调用中,其指向 King 对象*/this.royaltyName = 'Henry';/*** this 直接位于一个箭头函数中。在本文后面的调用中,其指向 King 对象* * 注意:不能看代码看到这里就断定此处的 this 恒指向 window,* 要看含函数 King() 被调用处附近的代码。* 但通常情况下,可以猜测 this 恒指向 window*/setTimeout(() => console.log(this.royaltyName), 1000);
    }function Queen() {/*** this 直接位于一个普通函数中。在本文后面的调用中,其指向 Queen 对象*/this.royaltyName = 'Elizabeth';/*** this 直接位于一个匿名函数中*/setTimeout(function() { console.log(this.royaltyName); }, 1000);
    }/*** 此处将函数 King() 作为构造函数,因此 this 指向此处“new”创建的 King 对象,* 同时还调用了函数 King() */
    new King(); // Henry/*** 此处将函数 Queen() 作为构造函数,但由于 this 位于一个匿名函数中,* 而此匿名函数将由函数 setTimeout 来调用,* 而函数 setTimeout 将不会使用显式的对象来调用此匿名函数,因此视调用方为 window* * 注意:此处并不是因为“new Queen()”位于全局,所以认为调用方为 window,* 而是因为直接包含此箭头函数代码的是一个匿名函数,而此匿名函数的调用方为 window*/
    new Queen(); // undefined
    

this 绑定

  既然 JavaScript 中普通函数与匿名函数中的 this 如此危险,有什么办法可以回避这个风险呢?一种方法是改用箭头函数,如果箭头函数所处的上下文是确定的对象,那么它的 this 指向就不会改变。

  但有些时候,需要这个 this 不事先确定,这有点像面向对象语言中基类方法的 this 一样。在 JavaScript 中,有时为了减少代码冗余,会将一些共同代码置入一个模块中,供其它模块使用,这个时候,希望 this 指向调用模块的上下文。不过考虑函数传递调用(一个函数调用另一个函数,而后者再调用另一个函数),这个时候判断 this 的指向会变得很麻烦。为此,JavaScript 提供了函数 bind 来终结这个判断难题。函数 bind 可以间接强制规定一个函数中恒定的 this 指向。

  在 JavaScript 中,每个函数都是一个对象,每个函数对象都有一个函数 bind,它接收一个对象参数,然后返回一个函数。返回的这个函数与自己几乎一样,区别是,这个函数的 this 被强制绑定为传入的这个对象参数。示例如下:

window.color = 'red';var obj = {color: 'blue'
};function sayColor0() {console.log(this.color);
}let sayColor = sayColor0().bind(obj);sayColor0(); // red
sayColor(); // blue

总结与提醒

  • 当两个 this 分别位于一个普通函数和一个箭头函数中时,它们的区别是,前者的 this 指向该普通函数(在被调用时)所属的对象(调用该普通函数的对象),而后者的 this 指向包含箭头函数定义的上下文(在被调用时)所属的对象。

  • 如果把箭头函数放置在一个函数中,则此箭头函数中的 this 与普通函数的 this 的效果一样的。因此,一般来说,不要把含 this 的箭头函数放置在一个函数中,而应该直接暴露在最外层的上下文中。这样,此箭头函数就永远指向当时的上下文,而不管之后又将此箭头函数传递给了谁。

  • 之所以 this 在普通函数与箭头函数中的行为有所不同,是因为 JavaScript 中的普通函数、匿名函数(lambda 函数)都属于函数,但箭头函数不属于函数,它只是一种特殊的表达式。而在 JavaScript 中,一个函数在被调用时,会为其创建 this 与 arguments,因为箭头函数不属于函数,所以它自己没有 this,它的 this 只能来源于外部的 this——也就是包含箭头函数定义的上下文。

lambda 表达式中的 this 与普通情况下的 this 指向相关推荐

  1. java lambda 表达式中的双冒号和箭头的用法 ::

    先构造一些数据,创建一个User类 java lambda 表达式中的双冒号的用法 :: 双冒号运算就是Java中的[方法引用],[方法引用]的格式是 类名::方法名 如下图所示 User是一个类, ...

  2. java lambda 两个冒号_java lambda 表达式中的双冒号的用法说明 ::

    双冒号运算就是java中的[方法引用],[方法引用]的格式是 类名::方法名 注意是方法名哦,后面没有括号"()"哒.为啥不要括号,因为这样的是式子并不代表一定会调用这个方法.这种 ...

  3. 无状态编程, lambda 表达式中传入的局部变量,为什么需要是不可变的(final)

    无状态编程 说明 @author JellyfishMIX - github / blog.jellyfishmix.com LICENSE GPL-2.0 前言 本文将会根据以下顺序进行叙述: la ...

  4. python大于等于怎么表示_如何在rejectdb中应用python lambda表达式中的大于等于

    我在reinstdb表中有下面的json记录.在[{ "pid": 12, "sk": [ { "sid": 30, "et&qu ...

  5. java 8 lambda表达式中的异常处理

    文章目录 简介 处理Unchecked Exception 处理checked Exception 总结 java 8 lambda表达式中的异常处理 简介 java 8中引入了lambda表达式,l ...

  6. 编译器说 Lambda 表达式中的变量必须是 final 的,我偏不信 | 原力计划

    作者 | 沉默王二 来源 | CSDN博客专家 出品 | CSDN(ID:CSDNnews) 偶尔,我们需要在 Lambda 表达式中修改变量的值,但如果直接尝试修改的话,编译器不会视而不见听而不闻, ...

  7. java supplier t_在Java中如何使用lambda表达式中的Supplier T接口?

    Supplier接口是一个预定义的接口,表示结果的提供者.它是使用lambda表达式.方法引用或默认构造函数实例化的.Supplier接口的函数方法是get()方法.此接口属于java.util.fu ...

  8. Lambda表达式中引用变量的问题

    Lambda表达式中引用变量的问题 Lambda表达式内部自定义的变量肯定没问题.引用的外部final变量也没问题.问题在于effectively final变量的理解,及应用场景的认识.引用的外部变 ...

  9. 下面是以十六进制格式存储的一个 UDP 首部:~~~TCP连接使用1000字节的窗口值,而上一次的确认号是22001~~那么下一个报文段的序号是否就是 x + 1 呢?在本题中列出的 8 种情况下,画

    5-10 试说明运输层中伪首部的作用 用于计算运输层数据报校验和 5-11 某个应用进程使用运输层的用户数据报UDP,然而继续向下交给IP层后,又封装成IP数据报.既然都是数据报,可否跳过UDP而直接 ...

最新文章

  1. Tesla Model汽车架构与FSD供应链
  2. Python:urllib2模块的基本使用
  3. mac、linux 查看端口占用程序
  4. ssl双向认证_SSL握手协议不清楚?vTrus为你讲解其详细流程
  5. Tableau实战系列Tableau基础概念全解析 (三)-维度和度量
  6. RaspberryPI 3 系统安装及常见问题
  7. 关于重写equals()与hashCode()
  8. MySQL安装时出现的问题
  9. 爬虫教程( 4 ) --- 分布式爬虫 scrapy-redis、集群
  10. C#_MVC_ajax for form
  11. pytest测试框架(四)---使用pytest-html生成测试报告
  12. Kotlin 丢失了一些 JRE 类
  13. html网页打不开二级网页,二级网页打不开,小编教你浏览器二级页面打不开怎么解决...
  14. 151.翻转字符串里的单词
  15. labview高级视频150讲下载_视频剪辑篇|讲真的,这些软件素材资源我真舍不得分享!(附下载包)...
  16. 电气自动化专业英文词汇及缩写
  17. 在本机搭建FTP服务器
  18. Crowding Counter 之 根据.mat标注制作密度图进行训练
  19. 符号之间,记住你所需要的正则表达式
  20. 清理注册表 php,win10系统如何清理自带注册表

热门文章

  1. 【机器学习】粗糙集属性约简算法与mRMR算法的本质区别
  2. Python数据可视化1.2 数据转换
  3. pomelo中的next
  4. div border-radius
  5. 造字工房全套正版精美中文设计字体免费下载使用 (个人非商用/全集打包)
  6. Web开发人员有用的代码比较工具
  7. 网络安全隐性杀手:三类危险的TXT文件
  8. Spring学习网址
  9. Flask 实战:如何从零开发“知乎”
  10. Linux 系统安装MySQL