最近写echars组件封装任务中,在整理自定义回调函数中的逻辑时因为this指向问题模糊不清,搞出了许多不生效的动作,浪费些许时间,项目一结束就立马着手把这块基础却很重要的知识做了下回顾,这里参考网上小伙伴以及自己的理解做下结合简单总结
特别注意:除特殊显式绑定外,查看this指向需要看最后执行调用的this的作用域而非定义时的作用域
一.隐式绑定-this指向window
1.全局环境下直接使用this-this指向window
console.log(this) // window
2.普通函数直接调用-this指向window
function foo(){
console.log(this) // window
}
foo()
3.普通函数嵌套使用-this指向window
let obj = {
name:'huan',
age: 16 ,
foo:function(){
console.log('父',this) // { name: 'huan', age: 16, foo: [Function: foo] }
function test(){
console.log('子',this) // window
}
test()
}
}
obj.foo()
4.立即执行函数ITIF-this指向window
(1).全局执行
(function(){
console.log(this) // window
})()
(2).嵌套执行
let obj = {
foo: function(){
(function(){
console.log(this) // window
})()
}
}
obj.foo()
5.闭包-this指向window
let obj = {
name:'huan',
age:15,
foo:function(){
console.log('1',this) // { name: 'huan', age: 15, foo: [Function: foo] }
let a = this.name
return function(){
console.log('2',this) // window
return a
}
}
}
let fn = obj.foo()
console.log('3',this) // window
fn()
5.对象绑定转移-this指向window
function foo(){
console.log(this)
}
let obj = {
name:'huan',
foo:foo
}
let foo2 = obj.foo // 只是进行了函数绑定,没有执行
foo2() // 真正执行时没有绑定对象 window
6.参数传递-this指向window
function foo(){
console.log(this) // // window
}
function bar1(fn){
fn() // 真正执行的时候没有绑定对象,指向window
}
let obj = {
name:'huan',
foo:foo
}
bar1(obj.foo)
7.定时器执行-this指向window
在定时器中是作为回调函数来执行的,因此回到主栈执行时是在全局执行上下文的环境中执行的
setTimeout(function(){
console.log(this) // window
},1000)
二.(隐式绑定)-对象绑定-this指向调用的对象
function foo(){
console.log(this) // obj2
}
let obj = {
foo:foo,
obj2:{
name:'huan',
foo:foo
}
}
// obj.foo()
obj.obj2.foo()
三.(显示绑定)-call()、apply()、bind()、forEach()
1.call()方法
call()方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。
let obj = {
name:'huan'
}
function foo(){
console.log(this) // obj
}
foo.call(obj)
2.apply()方法
apply()接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次。
let obj = {
name:'huan'
}
function foo(){
console.log(this) // obj
}
foo.apply(obj)
3.bind()方法
bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
let obj = {
name:'huan'
}
function foo(){
console.log(this) // obj
}
let fn = foo.bind(obj)
fn()
- forEach()方法
forEach接收两个参数,第一个是回调函数,第二个是函调函数中this的指向第一个参数回调函数接收三个参数,第一个是当前遍历的元素,第二个是当前遍历元素的索引,第三个是遍历的数组本身
function foo(el){
console.log(el)
console.log(this) // window
}
let arr = [1,2,3]
// arr.forEach(foo) //最终执行环境在window
let obj = {
name:'huan'
}
arr.forEach(foo,obj)
四.new关键字-this指向当前实例化的对象
function Foo(){
console.log(this)
}
let fn = new Foo()
五.优先级问题
new绑定 > 显示绑定 > 隐式绑定
function foo(y){
this.x = y
console.log(this)
}
let obj = {
name: 'huan'
}
let foo2 = foo.bind(obj)
foo2(2) //最后执行环境是在window
var foo3 = new foo2(3)
console.log('new',foo3) // new的优先级比较高
六.es6箭头函数-this继承父级(非箭头函数)作用域中的this指向
1.所有绑定规则不适应箭头函数
function foo(){
console.log('函数内部',this) // { name: 'huan', foo: [Function: foo] }
let foo2 = ()=>{
console.log('箭头函数内部',this) // { name: 'huan', foo: [Function: foo] }
}
return foo2
}
let obj = {
name:'huan',
foo:foo
}
obj.foo()()
2.显示绑定无效
function foo(){
console.log('函数内部',this) // { name: 'huan', foo: [Function: foo] }
let foo2 = ()=>{
console.log('箭头函数内部',this) // { name: 'huan', foo: [Function: foo] }
}
return foo2
}
let obj = {
name:'huan',
}
// let foo3 = foo()() // window
let foo4 = foo().call(obj) // window
3.隐式绑定无效
let fn = ()=>{
console.log('箭头函数隐式绑定',this)
}
let fn2 = function(){
console.log('普通函数隐式绑定',this)
}
let obj = {
foo:fn,
foo2:fn2
}
obj.foo() // windows
obj.foo2() // obj { foo: [Function: fn], foo2: [Function: fn2] }