this

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

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

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

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

1.0 当作普通函数被调用

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

function fn() {
console.log(this);
}
fn() // window
// 定时器内部是用 apply 把 this 绑定在 window 上
// 所以定时器指向的也是 window
setTimeout(function() {
console.log(this) // window
},0)

2.0 作为对象的方法被调用

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

  • 如果有多个对象进行嵌套,this 指向的是调用位置,也就是第一个对象
  • 如果你把对象里的函数赋值给新的变量来调用,那么 this 指向的是新的变量
function fn() {
console.log(this);
}
var obj1 = {
name: "obj1",
fn: fn
}
obj1.fn() // obj1对象
// 如果有多个对象进行嵌套,this 指向的是调用位置,也就是第一个对象
var obj2 = {
name: "obj2",
obj1: obj1
}
obj2.obj1.fn() // obj1对象
// 如果你把对象里的函数赋值给新的变量来调用,那么 this 指向的是新的变量
var newFn = obj.fn
newFn() // window

3.0 使用 call、apply、bind

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

function fn() {
console.log(this);
}
var obj = {
name: "fan",
}
fn.call(obj) // {name: "fan"}
fn.apply(obj.name) // String {"fan"}
// bind() 返回的是一个函数
var foo = fn.bind(obj)
foo() // {name: "fan"}

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

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

var div = document.createElement('div')
div.setAttribute('id','box')
div.style.width = "100px"
div.style.height = "100px"
div.style.background = "#ccc"
document.body.appendChild(div)
var box = document.querySelector('#box')
box.onclick = function() {
console.log(this) // box节点
}

5.0 在 class 的方法中调用

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

class Student {
constructor(name) {
this.name = name
}
sayHi() {
console.log(this);
}
}
var fan = new Student("fan")
console.log(fan) // fan 对象
fan.sayHi() // fan 对象

6.0 箭头函数

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

var obj = {
fn: () => {
console.log(this)
}
}
obj.fn() // window

this 的规则优先级

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

手写 bind 函数

// 实现 bind 函数
Function.prototype.bind1 = function(...args) {
// 获取传入的值
// const arr = Array.prototype.slice.call(arguments)
const arr = [...args]; // 获取 this (数组第一项)
const t = arr.shift() // fn1.bind(...) 中的 fn1
const self = this; // return 一个函数
return function() {
return self.apply(t, arr)
}
} function fn(a, b) {
console.log('this', this);
console.log(a, b);
return 'this is fn'
} const fn2 = fn.bind1({x: 100}, 100, 200)
const res = fn2()
console.log(res);

this 面试题

面试题一

var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
sss(); // window
person.sayName(); // person
(person.sayName)(); //person
(b = person.sayName)(); //window
}
sayName();

面试题二

var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
} var person2 = { name: 'person2' } person1.foo1(); // person1
person1.foo1.call(person2); // person2 person1.foo2(); // window
person1.foo2.call(person2); // window person1.foo3()(); // window
person1.foo3.call(person2)(); // window
person1.foo3().call(person2); // person2 person1.foo4()(); // person1
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1

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

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

面试题三

var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
},
this.foo2 = () => console.log(this.name),
this.foo3 = function () {
return function () {
console.log(this.name)
}
},
this.foo4 = function () {
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2') person1.foo1() // person1
person1.foo1.call(person2) // person2 person1.foo2() // person1
person1.foo2.call(person2) // person1 person1.foo3()() // window
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2 person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1

面试题四

var name = 'window'
function Person (name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name)
}
},
foo2: function () {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2') person1.obj.foo1()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2 person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj

解析:

person1.obj.foo1()() // obj.foo1()返回的是一个函数,这个函数会在全局执行
person1.obj.foo1.call(person2)() // 最后拿到的还是返回的函数,会在全局执行
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. python 压缩模块大杂烩(zipfile,bz2,lzma,gzip,tarfile,zlib)

    [*] 以下压缩模块请结合python的官方文档(https://docs.python.org/3.5/library/index.html)来实践或者对比(我的是python 3.5) 1.pyt ...

  2. POJ2044 深搜+剪枝(云彩下雨)

    题意:        有一个城镇,是4*4的大小的,然后你控制一块云彩,2*2的,你每天可以有9种走的方法,上下左右,或者不动,走的时候可以走1或者2步,云彩所在的地方肯定会下雨,然后给你做多365天 ...

  3. [转载] 关于Win7 x64下过TP保护的一些思路,内核层过保护,驱动过保护

    首先特别感谢梦老大,本人一直没搞懂异常处理机制,看了他的教程之后终于明白了.在他的教程里我学到了不少东西.第一次在论坛发帖,就说说Win7 x64位下怎么过TP保护.如果有讲错的地方,还望指出.说不定 ...

  4. [CTF]盲文对照表

    [CTF]盲文对照表 摘自:https://wenku.baidu.com/view/28b04fd380eb6294dd886ca7.html 学点盲文 盲文又称点字,国际通用的点字由6个凸起的圆点 ...

  5. [CTF]思维导向图

    [CTF]思维导向图 ---------------来自大佬的CTF思维导向图 Angel_Kitty https://www.cnblogs.com/ECJTUACM-873284962/ 给信息安 ...

  6. canvas绘制虚线图表

    最近有读者加我微信咨询这个问题,如下图所示: 要实现的效果如下: 其实难度不大,但是考虑一些人员对于canvas不熟悉,还是简单的介绍下. 其实该图表,就是一个圆圈外面在套一个圆弧的效果, 主要的难点 ...

  7. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  8. Java并发容器篇

    作者:汤圆 个人博客:javalover.cc 前言 断断续续一个多月,也写了十几篇原创文章,感觉真的很不一样: 不能说技术有很大的进步,但是想法确实跟以前有所不同: 还没开始的时候,想着要学的东西太 ...

  9. Python分支结构你真的搞定了吗?

    分支结构 分支结构能够让计算机像人一样进行思考,应对不同的场景做出不同的回应. Python中不支持switch语法,目前仅支持if/else形式,但是在Python3.10的测试版本中,貌似支持了s ...

  10. VSCode配置MSVC+VSCode使用easyx库,2021.5.13日配置

    VSCode配置MSVC+VSCode使用easyx库,2021.5.13日配置~~ 想必很多人和我一样,想用vscode编程c++,easyx库不支持MinGW,一般人都是直接使用vs2019安装e ...