博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
this解惑
阅读量:6798 次
发布时间:2019-06-26

本文共 4034 字,大约阅读时间需要 13 分钟。

前言

要正确理解this,首先得理解执行上下文,这里推荐汤姆大叔的,因为this是在运行代码时确认具体指向谁,箭头函数除外。

全局作用域中的this

node: 每个javaScript文件都是一个模块,this指向空对象(module.exports

this.a = 1;console.log(this, module.exports);// { a: 1 } { a: 1 }

当然也有些意外,比如下面这种情况:

this.a = 1;module.exports = {}console.log(this, module.exports);// { a: 1 } {}

浏览器端: this指向window

函数作用域中的this

这里分为两种,一种是全局作用域下直接执行函数,另外一种是被当作某个对象的属性的时候执行。eval的情况这里不作讨论。

全局环境下执行
function foo() {  console.log(this); // 此时的执行上下文为全局对象}foo();// node global, 浏览器 window

当然严格模式下有不同,具体区别如下:

严格模式

this指向undefined(node and 浏览器端)

非严格模式

浏览器端: this指向全局变量window

node: this指向global

被当作属性调用

当函数作为一个对象的属性时,node和浏览器端一致,指向调用该属性的对象

var obj = {  name: 'foo',  foo: function foo() {    console.log(this);  }}obj.foo();// { name: 'foo', foo: [Function: foo] }

接下来,做一些升级。

var obj = {  name: 'foo',  foo: function foo() {    console.log(this);  }}var objA = obj.foo;objA();// node环境指向global,浏览器端指向window,严格模式下均指向undefined--------------------------------------------------------------var obj = {  name: 'foo',  foo: function foo() {    console.log(this);  }}var objA = {  name: 'objA',  foo: obj.foo};objA.foo();// { name: 'objA', foo: [Function: foo] }
call、apply、bind

如果想手动更改函数里的this指向,可通过上述3个方法。callapply会立即执行,bind则返回一个绑定好this指向的函数。

var obj = {  name: 'foo',  foo: function foo() {    console.log(this);  }}var objA = {  name: 'objA',  foo: obj.foo};obj.foo.call(objA); // 将this指向objAobj.foo.apply(objA);obj.foo.bind(objA)(); // bind函数会返回一个绑定好this的函数,可供以后调用/**{ name: 'objA', foo: [Function: foo] }{ name: 'objA', foo: [Function: foo] }{ name: 'objA', foo: [Function: foo] }*/

这里对上述3个方法进行更细的说明,方便更好的理解之间的差异。

var obj = {  name: 'foo',  foo: function foo() {    console.log(this, arguments); // 通过arguments对象访问函数传入的参数列表,类似数组但不是数组,可通过arguments[0]访问到传入的Tom  }}var objA = {  name: 'objA',  foo: obj.foo};obj.foo.call(objA, 'Tom', 'Jerry');obj.foo.apply(objA, ['Tom', 'Jerry']);obj.foo.bind(objA, 'Tom', 'Jerry')(1);/**{ name: 'objA', foo: [Function: foo] } [Arguments] { '0': 'Tom', '1': 'Jerry' }{ name: 'objA', foo: [Function: foo] } [Arguments] { '0': 'Tom', '1': 'Jerry' }{ name: 'objA', foo: [Function: foo] } [Arguments] { '0': 'Tom', '1': 'Jerry', '2': 1 }可以看到call和bind是按序列传参,而apply是按数组传参,bind不会更改传参的顺序*/
new构造

当函数被当作构造函数调用时,this指向构造的那个对象。

注:new调用中的this不会被callapplybind改变。

接下来,简单验证一下,由于callapply会立即执行,无法被当作构造函数,只能选择bind

function Foo() {  console.log(this);}var foo = Foo.bind({ name: 'Tom' });foo();// { name: 'Tom' }new foo();// Foo {}

箭头函数中的this

this在定义时,就已经知道其具体指向,因为在运行到声明的箭头函数时,会将this进行强绑定到外部作用域中的this,且无法更改。可以理解为继承了外部作用域中的this。由于箭头函数的this是确定的,无法更改,因此也无法被当作构造函数调用。

外部作用域为全局作用域:

var foo = () => {  console.log(this);}this.a = 1;foo();// 或者下面代码var obj = {  name: 'obj',  foo: () => {    console.log(this);  }}var foo = obj.foo;obj.foo();foo();foo.call({ name: 'Tom' });/**因为obj是在全局作用域下被定义,所以外部作用域为全局对象node: 指向module.exports浏览器:指向window*/

外部作用域为函数作用域:

function foo() {  var a = () => {    console.log(this); // 继承外部作用域foo函数的this  };  a();}foo();foo.call({ name: 'foo' });new foo();/**这里foo函数中的this并不确定,由于调用方式不同,其this指向也不同*/

相信写ES6类的情况很多,本人经常写React类组件,刚开始初学者会好奇为什么在类组件里写方法时要用bind或者箭头函数来强绑定this。因为一般类组件里的方法,都会设计到this的处理。比如事件处理函数,当触发相应事件时,调用事件对应的处理函数,此时访问到的thisundefined(ES6默认类与模块内就是严格模式),这就导致不能正确处理该组件的状态,甚至出错(处理函数内可能调用this.setState方法)。所以在类组件内部声明方法时会需要我们进行强绑定。

接下来我们看看React组件渲染流程:new构造一个组件实例instance,然后调用其render方法进行渲染和事件绑定。new构造的过程,this已经确定指向构造的组件实例,所以你可以在constructor进行bind或直接使用箭头函数,这样函数内部this就绑定到了instancerender函数里之所以能正常访问this,是因为以instance.render()进行渲染。

当然这里不特指React类组件,只要是ES6类,只能用new构造调用,否则会报错,所以ES6类里this指向是确定的,可以放心使用箭头函数。

迷惑的代码

还有一个比较迷惑的地方,遇到的机会很少,代码如下:

(function(){    console.log(this); // 运行结果和全局作用域下执行结果一致})();// 由于没有以对象属性的方式调用,则被认为是全局环境下调用--------------------------------------------------------(function(){  console.log(this);}).call({ name: 'Hello World' });// { name: 'Hello World' },this指向可以被改变--------------------------------------------------------new (function(name){  this.name = name;  console.log(this);})('Tom');// { name: 'Tom' },this指向新创建的对象

更好的阅读体验在我的,欢迎?提issue。

转载于:https://www.cnblogs.com/raion/p/10887691.html

你可能感兴趣的文章
用CIL写程序:从“call vs callvirt”看方法调用
查看>>
远程连接mysql数据库提示:ERROR 1130的解决办法
查看>>
值传递、指针传递、引用传递的区别
查看>>
无法解析的外部符号 _WinMain@16 fatal error LNK1120: 1 个无法解析的外部命令
查看>>
linux 内核代码构架图
查看>>
JDBC的基本用法
查看>>
一个关于1到100之间和与积的数学题
查看>>
51 Nod 1057 N的阶乘【Java大数乱搞】
查看>>
Cocos2d-X中的ZORDER和Tag
查看>>
【git】git pull
查看>>
Java NIO(一)I/O模型概述
查看>>
【转】对博士学位说永别
查看>>
SQL Server等待事件—RESOURCE_SEMAPHORE_QUERY_COMPILE
查看>>
windowns 2008(apache2.2.25 x86 openssl0.98y) 升级openssl1.0.1e(为了支持小程序接口TLS1.2)
查看>>
在.NET下如何预防XXE注入攻击
查看>>
HTC T8878刷机手册
查看>>
修改Discuz! X2文章标题字数限制
查看>>
glloader移植到了Android
查看>>
【转载】dotnet 线程同步
查看>>
static_cast与dynamic_cast转换
查看>>