理解 this
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的更多相关文章
- 理解CSS视觉格式化
前面的话 CSS视觉格式化这个词可能比较陌生,但说起盒模型可能就恍然大悟了.实际上,盒模型只是CSS视觉格式化的一部分.视觉格式化分为块级和行内两种处理方式.理解视觉格式化,可以确定得到的效果是应 ...
- 彻底理解AC多模式匹配算法
(本文尤其适合遍览网上的讲解而仍百思不得姐的同学) 一.原理 AC自动机首先将模式组记录为Trie字典树的形式,以节点表示不同状态,边上标以字母表中的字符,表示状态的转移.根节点状态记为0状态,表示起 ...
- 理解加密算法(三)——创建CA机构,签发证书并开始TLS通信
接理解加密算法(一)--加密算法分类.理解加密算法(二)--TLS/SSL 1 不安全的TCP通信 普通的TCP通信数据是明文传输的,所以存在数据泄露和被篡改的风险,我们可以写一段测试代码试验一下. ...
- node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理
一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...
- 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念
一.前言 DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ...
- 学习AOP之透过Spring的Ioc理解Advisor
花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...
- ThreadLocal简单理解
在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...
- JS核心系列:理解 new 的运行机制
和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...
- 深入理解JS 执行细节
javascript从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习JS引擎工作机制之前,我们需要引入几个相关的概念:执行环境栈.全局对象.执行环境.变量对象.活动对象.作用域和作用域链等 ...
- 浅谈我对DDD领域驱动设计的理解
从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...
随机推荐
- Access数据库及注入方法
目录 Access数据库 Access数据库中的函数 盲注Access数据库 Sqlmap注入Access数据库 Access数据库 Microsoft Office Access是由微软发布的关系数 ...
- [LeetCode每日一题]1143. 最长公共子序列
[LeetCode每日一题]1143. 最长公共子序列 问题 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度.如果不存在 公共子序列 ,返回 0 . 一个字符串 ...
- 【vue-03】组件化开发 component
vue组件化思想 组件化是vue的一个重要思想 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构建我们的应用. 任何的应用都会被抽象成一颗组件树. 注册组件 组件的使用分成三个步骤:创建 ...
- 解决无法远程登录Docker中的MySQL
步骤: 进入mysql容器 登录mysql 授予远程登录权限 刷新权限,并退出 命令参考: docker exec -it [mysql] /bin/bash mysql -u root -p ALT ...
- Asp.NetCore Web开发之Nlog日志配置
接着讲基于ASP .net Core 的web开发,这节主要讲一下如何使用和配置Nlog进行日志记录. 日志在开发中的作用是很重要的,使用日志,程序出了错误可以及时捕获并记录下来,开发人员可以通过日志 ...
- SpringBoot简单尝试
一.spring boot核心 配置在类路径下autoconfigure下(多瞅瞅) @SpringBootApplication里的重要注解(@Configuration,@EnableAutoCo ...
- VSCode配置MSVC+VSCode使用easyx库,2021.5.13日配置
VSCode配置MSVC+VSCode使用easyx库,2021.5.13日配置~~ 想必很多人和我一样,想用vscode编程c++,easyx库不支持MinGW,一般人都是直接使用vs2019安装e ...
- Javac·编码GBK的不可映射字符
阅文时长 | 0.04分钟 字数统计 | 79.2字符 主要内容 | 1.引言&背景 2.声明与参考资料 『Javac·编码GBK的不可映射字符』 编写人 | SCscHero 编写时间 | ...
- MSSQL·查看数据库编码格式
阅文时长 | 0.67分钟 字数统计 | 837.6字符 主要内容 | 1.引言&背景 2.声明与参考资料 『MSSQL·查看数据库编码格式』 编写人 | SCscHero 编写时间 | 20 ...
- 【转载】使用rdp协议访问远程Linux桌面
使用rdp协议访问远程Linux桌面 2017年03月31日 16:23:49 太阳之影 阅读数:2169 一般情况下,如果需要登陆远程Linux系统,我们会使用ssh/telnet来完成,如果需 ...