函数调用
通常有三种函数执行方式
func(a, b)
obj.child.fn(a, b)
func.call(context, a, b)
一般来说前面两种方式大家用的比较多,但是其实第三种才是函数调用的本质。
fn.call(context, a, b)
其他两种可以等价地变为 call 形式:
fn(a, b)
// 等价于
fn.call(undefind, a, b)
obj.child.fn(a, b)
// 等价于
obj.child.fn.call(obj.child, a, b)
- 严格模式,fn的this就是call第一个参数undefined
- 非严格模式,call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象
函数执行的本质:函数的执行,都可以等价看做为fn.call的调用
var obj = {
fn: function(a, b){
console.log(this)
},
child: {
fn2: function(){
console.log(this)
}
}
}
obj.fn(2, 3)
// 等价
obj.fn.call(obj, 2, 3) // fn的this 为 obj
obj.child.fn2()
// 等价
obj.child.fn2.call(obj.child) // fn2的this 为 obj.child
this 就是你 call 一个函数时,传入的第一个参数。
例题1:
button.onclick = function f1(){
console.log(this) // 触发事件的元素。 button
}`
- this 是什么?去看 onclick 的源码呀 -> 做不到
- MDN 的开发者知道 onclick 的源码
- MDN 的开发者写了文档
- 看文档
例题2:
button.addEventListener('click', function(){
console.log(this) //该元素的引用 button
})
https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
- 去看 addEventListener 的源码呀 -> 做不到
- MDN 的开发者知道 addEventListener 的源码
- MDN 的开发者写了文档
- 看文档
例题3:
$('ul').on('click', 'li', function(){ // li 是 selector
console.log(this) //this 则代表了与 selector 相匹配的元素
// 这里的 this 是某个 li 元素
})
})
当jQuery的调用处理程序时,this关键字指向的是当前正在执行事件的元素。对于直接事件而言,this 代表绑定事件的元素。对于代理事件而言,this 则代表了与 selector 相匹配的元素。若要使用 jQuery 的相关方法,可以根据当前元素创建一个 jQuery 对象,即使用 $(this)。
- 去看 on 的源码呀 -> 做不到
- jQuery 的开发者知道 onclick 的源码,f1.call(???)
- jQuery 的开发者写了文档
- 看文档
特殊情况:自己抢先的调用 它,自己去 call.
button.onclick.call({name:'xxx'}) //this 是 xxx
只要没看见 call ,就不知道 this 什么。因为 this 是 call 的第一个参数
例题4:
function x(){
return object = {
name: 'object',
options: null,
f1(x){
this,options = x //所以这个 this 是外面的 x ,外面的 x 就是object
this.f2() //这个 this 是 f1 call 的第一个参数 ,所以这个this 就是 object
},
f2(){
this.options.f2.call(this) //这个 this 也是 object
// 这个 this.options.f2 就是 x.f2
// x.f2 也就是 options.f2
// 也就是调用下面的 options 的 f2 函数,
//调这个函数传了一个 this,这个this 就是 object
}
}
}
var options = {
name: 'options',
f1(){},
f2(){
console.log(this) //this 是啥?
}
}
var x = X()
x.f1(options) //call 的第一个参数是 x
问:options 中 f2 的 this 是啥?
答:object
this.f2() 中的 this 是 x.f1(options)call 的第一个参数 ,call的第一个参数是 x.
,所以this.f2() 中this 是外面的 x , 外面的 x 就是 object 。this.f2() 中this 就是 object。
f2(){ this.options.f2.call(this) } 这个 this 也是 object。
this.options =x , 这个 this.options.f2 就是 x.f2
x.f2 也就是 options.f2,因为 x.f1(options)传的参数是 options。也就是调用 options 的 f2 函数,调这个函数传了一个 this,这个this 就是 object
例题5:
var a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m()
上面的a.b.m()执行后里面的this.p是什么?
答:因为前面说了,this就是函数call的第一个参数,上面的a.b.m()写成call的形式就是a.b.m.call()也就是(a.b).m.call(a.b),所以this是a.b,也可以而b里面没有p所以是undefined
例题6:
var obj = {
fn(){
return function(){
console.log(this)
}
},
f1(){
var f =this.fn()
f()
}
}
obj.f1()
上面的代码因为是普通函数里的this,所以与它调用的时候有关,因为它是return的所以实际上就是f()的时候调用的,转成f.call(undefined),this就是window
例题7:
var obj = {
fn(){
return ()=>{
console.log(this)
}
},
f1(){
var f =this.fn()
f()
}
}
obj.f1()
上面的代码因为this是在一个箭头函数里,所以直接忽略,它的this就是它的外层函数fn里的this,而fn是普通函数,所以它的this就是它在调用的时候call的第一个参数,也就是this.fn.call(this)这里的this就是f1这个函数被调用时的this,也就是obj.f1.call(obj)所以this是obj
例题8:
window.xx = 'hhhhhhhhhh'
let c = {
xx:'name',
find(){
console.log(this.xx)
},
throttle(fn){
var timer = null
return ()=>{
clearTimeout(timer)
timer = setTimeout(()=>{
fn()
},1000)
}
},
bindEvent(){
var f = this.throttle(this.find)
f()
}
}
c.bindEvent()
上面的find里的this是什么?
答:是window,因为函数里的this只与函数被调用时有关,如果不是箭头函数,那这个函数调用的时候只需要转成call的形式就能知道this了,上面的find函数也就是fn,fn被调用的时候是fn(),因为find本身不是箭头函数,所以可以转成fn.call(undefined),所以this是window
例题9:
对于class中的this,如果是被直接call(undefined)那么this就是undefined
class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
let obj = new Animal();
let speak = obj.speak;
speak(); // undefined
上面的speak()转成call的形式就是speak.call(undefined),因为class中所有的函数、方法、构造函数、getters或setters都在严格模式下执行。因此如果我们没有指定this的值,也就是call的时候传入的是undefined那么this就是undefined
有关 this 的理解,方老师的这篇 文章 很完美
网友评论