this 到底指向谁
this
的指向,是在调用函数时根据执行上下文所动态确定的。
- 在函数体中,简单调用该函数时(非显式/隐式绑定下),严格模式下
this
绑定到undefined
,否则绑定到全局对象window
/global
; - 一般构造函数
new
调用,绑定到新创建的对象上; - 一般由
call
/apply
/bind
方法显式调用,绑定到指定参数的对象上; - 一般由上下文对象调用,绑定在该对象上;
- 箭头函数中,根据外层上下文绑定的
this
决定this
指向。
例题组合 1:全局环境下的 this
函数在浏览器全局环境中被简单调用,非严格模式下指向 window
,严格模式下指向 undefined
。
function fn1() {
console.log(this)
}
function fn2() {
'use strict'
console.log(this)
}
fn1() // window
fn2() // undefined
在执行函数时,如果函数中的 this
是被上一级的对象所调用,那么 this
指向的就是上一级的对象; 否则指向全局环境。
const foo = {
bar: 10,
fn: function() {
console.log(this)
console.log(this.bar)
}
}
var fn1 = foo.fn
fn1() // 直接调用,this 指向 window,window.bar => undefined
foo.fn() // 通过 foo 调用,this 指向 foo,foo.bar => 10
const student = {
name: 'Lucas',
fn: function() {
return this
}
}
console.log(student.fn() === student) // true
var fn1 = student.fn;
console.log(fn1() === student) // false
例题组合 2:上下文对象调用中的 this
const person = {
name: 'Lucas',
brother: {
name: 'Mike',
fn: function() {
return this.name
}
}
}
console.log(person.brother.fn()) // this 指向最后调用它的对象,输出 => Mike
const o1 = {
text: 'o1',
fn: function() {
return this.text
}
}
const o2 = {
text: 'o2',
fn: function() {
return o1.fn()
}
}
const o3 = {
text: 'o3',
fn: function() {
var fn = o1.fn
return fn()
}
}
console.log(o1.fn()) // this 指向 o1,输出 o1
console.log(o2.fn()) // 最终调用 fn 的是 o1,所以 this 指向 o1,输出 o1
console.log(o3.fn()) // fn() 是被直接调用的,所以 this 指向 window,window.text => undefind
如果想让
console.log(o2.fn())
输出 o2
,除了使用 bind
/call
/apply
以外
console.log(o1.fn.call(o2))
console.log(o1.fn.apply(o2))
console.log(o1.fn.bind(o2)())
还可以用这种方式
const o1 = {
text: 'o1',
fn: function() {
return this.text
}
}
const o2 = {
text: 'o2',
fn: o1.fn
}
console.log(o2.fn()) // o1.fn 挂到了 o2 对象上,所以 this 指向 o2,输出 o2
this
指向最后调用它的对象,在 fn
执行时,挂到 o2
对象上即可,提前进行了赋值操作
例题组合 3:bind/call/apply 改变 this 指向
bind
/call
/apply
都是用来改变相关函数 this
指向的,但是 call
/apply
是直接进行相关函数调用; bind
不会执行相关函数,而是返回一个新的函数,这个新的函数已经自动绑定了新的 this
指向,开发者只需要手动调用即可。
三者代码上的区别如下:
const target = {}
fn.call(target, 'arg1', 'arg2')
const target = {}
fn.apply(target, ['arg1', 'arg2'])
const target = {}
fn.bind(target, 'arg1', 'arg2')()
const foo = {
name: 'lucas',
logName: function () {
console.log(this.name)
}
}
const bar = {
name: 'mike'
}
foo.logName() // lucas; logName 由 foo 调用,this 指向 foo
foo.logName.call(bar) // mike; logName 被 call() 改变 this 的指向为 bar
例题组合 4:构造函数和 this
function Foo() {
this.bar = 'Lucas'
}
const instance = new Foo()
console.log(instance.bar) // Lucas
new 操作符调用构造函数具体做了什么?
如下:
- 创建一个新的对象
- 将构造函数的 this 指向这个新对象
- 为这个对象添加属性、方法等
- 最终返回新对象
以上过程,也可以用代码表述:
var obj = {}
obj.__proto__ = Foo.prototype
Foo.call(obj)
如果在构造函数中出现了显式 return 的情况,那么需要注意分为两种场景:
function Foo() {
this.user = 'Lucas'
const o = {}
return o
}
const instance = new Foo()
console.log(instance.user) // undefined,此时 instance 返回的是空对象 o
function Foo() {
this.user = 'Lucas'
return 1
}
const instance = new Foo()
console.log(instance.user) // Lucas,此时 instance 返回的是目标对象实例 this
结论:如果构造函数中显式返回一个值,且返回的是一个对象,那么
this
就指向这个返回的对象; 如果返回的不是一个对象,那么this
仍然指向实例。
例题组合 5:箭头函数中的 this 指向
结论:箭头函数使用
this
不适用以上标准规则,而是根据外层(函数或者全局)上下文决定。
this
出现在 setTimeout()
中的匿名函数,因此 this
指向 window
对象。
const foo = {
fn: function () {
setTimeout(function () {
console.log(this === window) // true
console.log(this === foo) // false
});
}
}
foo.fn()
如果需要 this
指向 foo
这个 object 对象,可以使用箭头函数解决。
const foo = {
fn: function () {
setTimeout(() => {
console.log(this === window) // false
console.log(this === foo) // true
});
}
}
foo.fn()
例题组合 6:this 优先级相关
我们常常把通过 call
、apply
、bind
、new
对 this
绑定的情况称为显式绑定; 根据调用关系确定的 this
指向称为隐式绑定。
function foo(a) {
console.log(this.a)
}
const obj1 = {
a: 1,
foo: foo
}
const obj2 = {
a: 2,
foo: foo
}
obj1.foo.call(obj2) // 2
obj2.foo.call(obj1) // 1
输出分别为 2、1,也就是说 call
、apply
的显式绑定一般来说优先级更高
function foo(a) {
this.a = a
}
const obj1 = {}
var bar = foo.bind(obj1)
bar(2)
console.log(obj1.a) // 2
上面代码将 bar
函数中的 this
指向为 obj1
对象。执行 bar(2)
后,obj1.a
的值为 2。即经过 bar(2)
执行后,obj1
对象为:{a: 2}
。
当再使用 bar 作为构造函数时:
var baz = new bar(3)
console.log(baz.a) // 3
console.log(obj1.a) // 2
bar
函数本身是通过 bind
方法构造的函数,其内部已经将 this
绑定为 obj1
,它再作为构造函数通过 new
调用时,返回的实例就会与 obj1
解绑,也就是说:
new 绑定修改了 bind
绑定中的 this
,因此 new
绑定的优先级比 bind
绑定更高。
function foo() {
return () => {
console.log(this.a)
}
}
const obj1 = {
a: 2
}
const obj2 = {
a: 3
}
const bar = foo.call(obj1) // 将 foo() 的 this 绑定为 obj1
bar() // 2; 此时 bar 为 foo() 中返回的箭头函数,箭头函数的 this 指向上层的 this,所以 bar 的 this 也会绑定到 obj1
bar.call(obj2) // 2; 箭头函数的绑定无法被修改,也就是说如果 bar 为普通函数,this 就可以被修改绑定
var a = 123
const foo = () => () => {
console.log(this.a)
}
const obj1 = {
a: 2
}
const obj2 = {
a: 3
}
const bar = foo.call(obj1) // 此时 foo() 为箭头函数,所以 this 指向 window
bar.call(obj2) // 123; bar 作为箭头函数指向上层,所以 this 也指向 window
如果将 var a = 123
改为 const a = 123
,将会输出 undefined
。因为使用 const
声明的变量不会挂载到 window
全局对象当中。因此 this
指向 window
时,自然也找不到 a
变量了。
参考文章:《前端开发核心知识进阶》LucasHC(侯策):一网打尽 this,对执行上下文说 Yes
this 到底指向谁的更多相关文章
- javaScript中this到底指向谁
1.前言 在JavaScript中,this的指向一直是大多数初学者的易错点,总是搞不清楚this到底指向谁,而在求职面试中,this的指向问题往往又是高频考点.本篇博文就来总结一下在JavaScri ...
- this, 你到底指向谁?
JS中, this的值到底是什么? 几个月之前, 拜读了<javascript语言精髓>, 里面对于这个问题, 做出了很好的解释... JS中, this的值取决于调用的模式, 而JS中共 ...
- js 中this到底指向哪里?
其实js的this指向很简单.我们记住下面3种情况. this 指向的是浏览器中的window.代码如下: function fn(){ this.name='yangkun'; this.age=2 ...
- JS中this到底指向谁?
关于this的指向,是一个令人很头疼的问题.但是,你运气好,碰到了我.老夫这儿有本祖传秘籍,看懂这个,妈妈再也不用担心你的this指向不对啦! 归根结底,this指向就一句话:谁最终调用函数,this ...
- this到底指向哪里
this指向调用它的对象 首先要明确,this指向调用方,谁调用,this指向谁. 直接调用 举个栗子: var test = 'window' ; function testThis () { va ...
- Groovy中Closure的this到底指向谁?
Groovy in Action(中文版)第136页明确说Closure的this指向Closure自己.并且从代码注释处作者也是这样理解的: class Mother{ int field = ...
- JavaScript的this指针到底指向哪?
编程过程中,着实十分困扰this的指向性,经过查阅一番资料,终于搞清楚了,在这里总结一下,全文分为以下三个部分: 什么是this指针? this指针指向哪里? 何时使用this? 一 什么是this指 ...
- 【javascript 技巧】谈谈setTimeout的作用域以及this的指向问题
setTimeout的用法详见:http://www.w3school.com.cn/htmldom/met_win_settimeout.asp 是的,setTimeout的常见用法是让某个方法延迟 ...
- what's this? 浅谈js中this的指向问题
刚刚学习js的朋友可能和我一样,看到代码中的this总是一脸懵逼,不知道this到底指向谁.经过一段时间的了解,我想跟大家分享下自己的理解. 何时出现this 函数在调用的时候,会自动获得两个特殊变量 ...
随机推荐
- java 获取文本一行一行读
直接上代码: 如果出现乱码:请改一下编码:我这里使用utf-8是会乱码的,改GBK就好了 // 读取文件内容 public static String readFile(String path) {/ ...
- LeetCode 129. 求根到叶子节点数字之和(Sum Root to Leaf Numbers)
题目描述 给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字. 例如,从根到叶子节点路径 1->2->3 代表数字 123. 计算从根到叶子节点 ...
- spark streaming 1: SparkContex
StreamingContext 和SparkContex的用途是差不多的,作为spark stream的入口,提供配置.生成DStream等功能. 总体来看,spark stream包括如下模块: ...
- 半硬化树脂PP的型号
1080是PP半固化胶片的型号(perperg),还有7628,2116,2113,2112,1506等等型号,每种型号不一样代表其PP内部的玻纤布不一样,比如7628的玻纤布相对较粗.数值较小则玻纤 ...
- 阶段3 2.Spring_07.银行转账案例_2 案例中添加转账方法并演示事务问题
使用xmlioc这个项目进行完善. 创建一个新的工程把之前的代码都复制过来. 复制pom.xml内的依赖项 java下的com包复制过来. 配置文件复制过来 测试类固执过来 内容进行删减 测试类的方法 ...
- Dart学习笔记-变量常量数据类型
变量和常量 1.变量的定义 main() { var t_str = 'hello world'; var t_num = 123456; String t_str2 = '你好,我很高兴'; int ...
- jeecms v9图标不显示问题
- jmeter业务建模中遇到的问题
1.jmeter函数助手中的jexl3函数,不支持${__jexl3(15<${__Random(1,100,)}<36,)}这种写法,须这样写${__jexl3(15<${__Ra ...
- P2814 家谱
我真没什么创意了woc.. so,为什么一道水题是蓝色的???哦哦哦,水好像就是蓝色的,emmm那就不是恶意评分了嘤嘤嘤 ... 好吧实际上可能是非c党对于字符串的处理需要进行编号和结构体,会麻烦一点 ...
- python3 selenuim PC端使用chrome模拟手机进行H5自动化
情况说明:初次在做PC端使用chrome进行H5自动化测试时,以为和app端自动化一样使用click()就可以对按钮进行点击,找了好几天也没有找到解决方法,有些人说是工程问题,有些人是使用微信进行H5 ...