this

this 取什么值是在函数执行的时候确认的,不是在函数定义的时候确认的

this 的不同应用场景,this 的指向

函数在调用时,js 会默认给 this 绑定一个值,this 的值与绑定方式、调用方式和调用位置有关,this 是在运行时被绑定的。下面有几种 this 的应用场景:

  • 当作普通函数被调用
  • 作为对象的方法被调用
  • 使用 call、apply、bind
  • 监听 DOM 事件所执行的函数
  • 在 class 的方法中调用
  • 箭头函数

1.0 当作普通函数被调用

普通函数的独立调用可以称为默认绑定,指向 window

  1. function fn() {
  2. console.log(this);
  3. }
  4. fn() // window
  5. // 定时器内部是用 apply 把 this 绑定在 window 上
  6. // 所以定时器指向的也是 window
  7. setTimeout(function() {
  8. console.log(this) // window
  9. },0)

2.0 作为对象的方法被调用

函数是通过某个对象进行调用可以称为隐式绑定,指向拥有该方法的对象

  • 如果有多个对象进行嵌套,this 指向的是调用位置,也就是第一个对象
  • 如果你把对象里的函数赋值给新的变量来调用,那么 this 指向的是新的变量
  1. function fn() {
  2. console.log(this);
  3. }
  4. var obj1 = {
  5. name: "obj1",
  6. fn: fn
  7. }
  8. obj1.fn() // obj1对象
  9. // 如果有多个对象进行嵌套,this 指向的是调用位置,也就是第一个对象
  10. var obj2 = {
  11. name: "obj2",
  12. obj1: obj1
  13. }
  14. obj2.obj1.fn() // obj1对象
  15. // 如果你把对象里的函数赋值给新的变量来调用,那么 this 指向的是新的变量
  16. var newFn = obj.fn
  17. newFn() // window

3.0 使用 call、apply、bind

使用这种方法是对象内部不想放入该函数,然后通过绑定的方式进行调用,这种绑定可以称为显式绑定,指向方法所绑定的对象

  1. function fn() {
  2. console.log(this);
  3. }
  4. var obj = {
  5. name: "fan",
  6. }
  7. fn.call(obj) // {name: "fan"}
  8. fn.apply(obj.name) // String {"fan"}
  9. // bind() 返回的是一个函数
  10. var foo = fn.bind(obj)
  11. foo() // {name: "fan"}

4.0 监听 DOM 事件所执行的函数

给 dom 添加点击事件,当用户点击后,绑定的函数被调用时,会执行回调,把函数绑定到 box 元素节点,指向的是所绑定的元素节点

  1. var div = document.createElement('div')
  2. div.setAttribute('id','box')
  3. div.style.width = "100px"
  4. div.style.height = "100px"
  5. div.style.background = "#ccc"
  6. document.body.appendChild(div)
  7. var box = document.querySelector('#box')
  8. box.onclick = function() {
  9. console.log(this) // box节点
  10. }

5.0 在 class 的方法中调用

class 和构造函数是一个意思,都是通过 new 关键字来实例化,实例化会创建一个全新的对象,这个对象的的原型对象会等于类的原型 fan.__proto__ === Student.prototype ,方法调用的时候,指向的是实例化出来的新对象

  1. class Student {
  2. constructor(name) {
  3. this.name = name
  4. }
  5. sayHi() {
  6. console.log(this);
  7. }
  8. }
  9. var fan = new Student("fan")
  10. console.log(fan) // fan 对象
  11. fan.sayHi() // fan 对象

6.0 箭头函数

箭头函数是没有 this 的,它的 this 是通过外层作用域来决定的

  1. var obj = {
  2. fn: () => {
  3. console.log(this)
  4. }
  5. }
  6. obj.fn() // window

this 的规则优先级

new绑定 > 显式绑定(bind)> 隐式绑定 > 默认绑定

手写 bind 函数

  1. // 实现 bind 函数
  2. Function.prototype.bind1 = function(...args) {
  3. // 获取传入的值
  4. // const arr = Array.prototype.slice.call(arguments)
  5. const arr = [...args];
  6. // 获取 this (数组第一项)
  7. const t = arr.shift()
  8. // fn1.bind(...) 中的 fn1
  9. const self = this;
  10. // return 一个函数
  11. return function() {
  12. return self.apply(t, arr)
  13. }
  14. }
  15. function fn(a, b) {
  16. console.log('this', this);
  17. console.log(a, b);
  18. return 'this is fn'
  19. }
  20. const fn2 = fn.bind1({x: 100}, 100, 200)
  21. const res = fn2()
  22. console.log(res);

this 面试题

面试题一

  1. var name = "window";
  2. var person = {
  3. name: "person",
  4. sayName: function () {
  5. console.log(this.name);
  6. }
  7. };
  8. function sayName() {
  9. var sss = person.sayName;
  10. sss(); // window
  11. person.sayName(); // person
  12. (person.sayName)(); //person
  13. (b = person.sayName)(); //window
  14. }
  15. sayName();

面试题二

  1. var name = 'window'
  2. var person1 = {
  3. name: 'person1',
  4. foo1: function () {
  5. console.log(this.name)
  6. },
  7. foo2: () => console.log(this.name),
  8. foo3: function () {
  9. return function () {
  10. console.log(this.name)
  11. }
  12. },
  13. foo4: function () {
  14. return () => {
  15. console.log(this.name)
  16. }
  17. }
  18. }
  19. var person2 = { name: 'person2' }
  20. person1.foo1(); // person1
  21. person1.foo1.call(person2); // person2
  22. person1.foo2(); // window
  23. person1.foo2.call(person2); // window
  24. person1.foo3()(); // window
  25. person1.foo3.call(person2)(); // window
  26. person1.foo3().call(person2); // person2
  27. person1.foo4()(); // person1
  28. person1.foo4.call(person2)(); // person2
  29. person1.foo4().call(person2); // person1

易错解析:(箭头函数只看上层作用域)

  1. person1.foo2.call(person2) // foo2() 是箭头函数,不适用于显示绑定的规则
  2. person1.foo3()() // 相当于在全局调用 foo3() 返回的函数
  3. person1.foo3.call(person2)() // 显式绑定到 person2 中调用该函数,还是返回一个函数在全局调用
  4. person1.foo3().call(person2) // 拿到返回值再进行显式绑定,绑定的就是没有返回值的函数,相当于调用 person2 中的方法
  5. person1.foo4.call(person2)() // 显式绑定之后调用,返回的是箭头函数,箭头函数向上层作用域找,找到的是 显式绑定的 person2
  6. person1.foo4().call(person2) // 显式绑定之前调用,返回的箭头函数向上层作用域找,找到的是 person1

面试题三

  1. var name = 'window'
  2. function Person (name) {
  3. this.name = name
  4. this.foo1 = function () {
  5. console.log(this.name)
  6. },
  7. this.foo2 = () => console.log(this.name),
  8. this.foo3 = function () {
  9. return function () {
  10. console.log(this.name)
  11. }
  12. },
  13. this.foo4 = function () {
  14. return () => {
  15. console.log(this.name)
  16. }
  17. }
  18. }
  19. var person1 = new Person('person1')
  20. var person2 = new Person('person2')
  21. person1.foo1() // person1
  22. person1.foo1.call(person2) // person2
  23. person1.foo2() // person1
  24. person1.foo2.call(person2) // person1
  25. person1.foo3()() // window
  26. person1.foo3.call(person2)() // window
  27. person1.foo3().call(person2) // person2
  28. person1.foo4()() // person1
  29. person1.foo4.call(person2)() // person2
  30. person1.foo4().call(person2) // person1

面试题四

  1. var name = 'window'
  2. function Person (name) {
  3. this.name = name
  4. this.obj = {
  5. name: 'obj',
  6. foo1: function () {
  7. return function () {
  8. console.log(this.name)
  9. }
  10. },
  11. foo2: function () {
  12. return () => {
  13. console.log(this.name)
  14. }
  15. }
  16. }
  17. }
  18. var person1 = new Person('person1')
  19. var person2 = new Person('person2')
  20. person1.obj.foo1()() // window
  21. person1.obj.foo1.call(person2)() // window
  22. person1.obj.foo1().call(person2) // person2
  23. person1.obj.foo2()() // obj
  24. person1.obj.foo2.call(person2)() // person2
  25. person1.obj.foo2().call(person2) // obj

解析:

  1. person1.obj.foo1()() // obj.foo1()返回的是一个函数,这个函数会在全局执行
  2. person1.obj.foo1.call(person2)() // 最后拿到的还是返回的函数,会在全局执行
  3. person1.obj.foo2()() // 返回一个箭头函数,只要记住是箭头函数就往上层作用域找

理解 this的更多相关文章

  1. 理解CSS视觉格式化

    前面的话   CSS视觉格式化这个词可能比较陌生,但说起盒模型可能就恍然大悟了.实际上,盒模型只是CSS视觉格式化的一部分.视觉格式化分为块级和行内两种处理方式.理解视觉格式化,可以确定得到的效果是应 ...

  2. 彻底理解AC多模式匹配算法

    (本文尤其适合遍览网上的讲解而仍百思不得姐的同学) 一.原理 AC自动机首先将模式组记录为Trie字典树的形式,以节点表示不同状态,边上标以字母表中的字符,表示状态的转移.根节点状态记为0状态,表示起 ...

  3. 理解加密算法(三)——创建CA机构,签发证书并开始TLS通信

    接理解加密算法(一)--加密算法分类.理解加密算法(二)--TLS/SSL 1 不安全的TCP通信 普通的TCP通信数据是明文传输的,所以存在数据泄露和被篡改的风险,我们可以写一段测试代码试验一下. ...

  4. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  5. 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念

    一.前言     DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ...

  6. 学习AOP之透过Spring的Ioc理解Advisor

    花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...

  7. ThreadLocal简单理解

    在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...

  8. JS核心系列:理解 new 的运行机制

    和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...

  9. 深入理解JS 执行细节

    javascript从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习JS引擎工作机制之前,我们需要引入几个相关的概念:执行环境栈.全局对象.执行环境.变量对象.活动对象.作用域和作用域链等 ...

  10. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

随机推荐

  1. CVE-2012-3569:VMware OVF Tool 格式化字符串漏洞调试分析

    0x01 简介 VMware OVF Tool 是一个命令行实用程序,允许您从许多 VMware 产品导入和导出 OVF 包.在 2.1.0 - 2.1.3 之间的版本中存在格式化字符串漏洞,通过修改 ...

  2. Intel汇编程序设计-高级过程(上)

    第八章 高级过程 8.1 简介 本章主要讲: 堆栈框架 变量作用域和生存期 对战参数的类型 通过传递值或者传递引用来传递参数 在堆栈上创建和初始化局部变量 递归 编写多模块程序 内存模型和语言关键字 ...

  3. myysql 不能远程访问的解决办法

    1.通过navicat或者命令行,将user表中原来host=localhost的改为host=% 命令行方式: mysql> update user set host = '%' where ...

  4. 企业CRM系统选型的标准有哪些?

    随着市场的发展,企业开始意识到客户的重要性.越来越多的企业形成了"以客户为核心"的理念,更加注重客户数据和管理,因此CRM客户关系管理系统成为企业的首选.选择一个适合企业的CRM系 ...

  5. prometheus管理api

    健康检查:GET /-/healthy 准备检查:GET /-/ready 停止服务:PUT|POST /-/quit 重载配置文件 PUT|POST /-/reload reference mana ...

  6. ssh无法启动 (code=exited, status=255)

    ssh无法启动 (code=exited, status=255) 2019年1月30日ssh 服务器运行了一些脚本后,突然发现无法ssh了. root@X61T:/home/liang# servi ...

  7. 服务器硬件必须支持M2 或PCIE才能支持NVME

    兆芯服务器不支持NVME. 服务器硬件必须支持M2 或PCIE才能支持NVME.1 因为物理接口只有M2 SATA 和PCIE这三中但是NVME只支持M2 和PCIE这2种2所以 NVME不支持SAT ...

  8. Chm文件阅读软件测试需求

    转发: .Chm文件阅读软件测试需求 . xchm问题太多就不考虑了: kchm是问题少一些 windows打开是乱码就不去考虑 必须在windows打开正常的再在龙芯上打开 1windows打开是乱 ...

  9. LDAP协议入门

    LDAP协议入门(轻型目录访问协议) LDAP简介 轻型目录访问协议,全称:Lightweight Directory Access Protocol,缩写:LDAP,它是基于X.500标准的,但是简 ...

  10. 1.7 Systemd初始化进程

    1.7 Systemd初始化进程 Linux操作系统的开机过程是这样的,即从BIOS开始,然后进入Boot Loader,再加载系统内核,然后内核进行初始化,最后启动初始化进程.初始化进程作为Linu ...