this解惑
2019-05-18 23:50
501 查看
前言
要正确理解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(
nodeand 浏览器端)
非严格模式
浏览器端:
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个方法。
call和
apply会立即执行,
bind则返回一个绑定好
this指向的函数。
var obj = { name: 'foo', foo: function foo() { console.log(this); } } var objA = { name: 'objA', foo: obj.foo }; obj.foo.call(objA); // 将this指向objA obj.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不会被
call、
apply、
bind改变。
接下来,简单验证一下,由于
call和
apply会立即执行,无法被当作构造函数,只能选择
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的处理。比如事件处理函数,当触发相应事件时,调用事件对应的处理函数,此时访问到的
this为
undefined(ES6默认类与模块内就是严格模式),这就导致不能正确处理该组件的状态,甚至出错(处理函数内可能调用
this.setState方法)。所以在类组件内部声明方法时会需要我们进行强绑定。
接下来我们看看
React组件渲染流程:
new构造一个组件实例
instance,然后调用其
render方法进行渲染和事件绑定。
new构造的过程,
this已经确定指向构造的组件实例,所以你可以在
constructor进行
bind或直接使用箭头函数,这样函数内部
this就绑定到了
instance。
render函数里之所以能正常访问
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指向新创建的对象
更好的阅读体验在我的github,欢迎👏提issue。
相关文章推荐
- this指针解惑
- 深入JAVA002_对象的发布和逃逸 --this解惑
- Dow falls 97 points, worst showing this year
- base基本知识和this的比较
- c++ 中的this指针的理解和用法
- Intellij Idea debug报错【ERROR: Cannot load this JVM TI agent twice, check your java command line】
- 静态成员函数、this指针、对象指针和动态对象使用小结
- (二分) HDU 2199 Can you solve this equation?
- 【javascript】javascript中的this
- tomcat 6.0.44 “has failed to stop it. This is very likely to create a memory leak” 问题调查
- Spring will invoke specific constructor no matter this constructor is public or private
- *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<WKWebViewConfiguration 0x1701bcd20> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the k
- DOM里的this 和event.srcElement区别
- java关键字之this
- 【学习笔记javascript设计模式与开发实践(this、call和apply)----2】
- vs2012 error c4996: 'fopen': This function or variable may be unsafe
- MySql安装问题This application requires Visual Studio 2013 Redistributable. Please install
- host is not allowed to connect to this MySQL
- Visual Studio 2012 编译错误【error C4996: 'scanf': This function or variable may be unsafe. 】的解决方案
- 1103 host is not allowed to connect to this mysql server