前面讲过了,在全局作用域下声明的函数或者变量,默认是window这个对象的属性了,前面再议函数时,说过,上下文这个概念跟作用域的概念的区别,上下文是一个目标变量被当成某个对象的属性或者方法时,这里的对象是目标的上下文,我是这么理解的。而this在代码层面上就是时刻指向这个对象了。
内部函数
这里的this怎么会这样呢?可以这样理解,要执行的是f0,所以f0是目标函数,这时候里面可以是这样的:
function f0(){
// var this = window 这里隐含着一个这样的声明了。
function f2(){
console.log(this)
}
f2()
}
f0()
setTimeout setInterval
document.addEventListener('click',function(e){
console.log(this)
setTimeout(function(){
console.log(this)
},200)
},false)
运行如图:
之前做项目封装的时候,在这个定时器上栽过很多跟头的,因为这个定时器是个独立的函数,不是跟内嵌的一样,因为它运行是异步运行在任务列表里的,我认为在任务队列里的时候它是全局作用域的变量,否则取消定时器不就只能放在它所在作用域里取消它了吗?那如何实时取消操作它呢?当然这里不能再说作用域了,比如这个是相对于变量的存在意义的范畴,而不是作为变量所属的对象研究的上下文。
这里老师给了一个一般性结论:
如果碰到了回调函数,那this基本都变了,否则也不会用回调函数了。
构造函数
上次写原型的时候,写new的作用,第一步就是创建一个空对象,空对象的proto等于原函数的prototype,第二部就是运行这个函数,运行时,函数里的this指向这个空对象,最后把加工了的空对象返回。
作为对象方法调用
var obj = {
name:'haha',
printname : function(){
console.log(this.name)
}
}
obj.printname() //'haha'
对象的方法调用,这个方法的对象就是这个对象了,this直接指向它喽。
陷阱:
fn是在全局声明的,就是window的属性了,只不过这个赋值的范畴让你迷惑,就是声明加赋值嘛。
DOM绑定事件
除了低版本的bug指向window,事件处理中的this代表事件源DOM对象。
document.addEventListener('click',function(e){
console.log(this)
var self = this //经常用到的技巧,拿不准了,提前把this的指针赋值给一个变量,
//因为默认this这四个字母代表了当下目标变量的上下文,铁打的this,流水的变量。
setTimeout(function(){
console.log(this)
console.log(self)
},200)
},false)
Function.prototype.bind
bind返回一个新函数,并使函数内部的this为传入的第一个参数。
bind执行时,也就是返回的函数执行时,那个对象作为参数传了进来。换句话说,我们为这个函数设置了新对象。
所有的函数都是Function创造的,在Function.prototype.bind上加一个函数,所有的函数都有bind这个方法了。
它的作用有利于复用函数,而且是在指定的上下文里去运行。注意最后面的括号不要忘了,因为返回的是函数,要执行就要加。
再看看这样呢:
this.say是对的,this就是指的page,这时候,相当于事件被解读状态。但是触发事件运行事件的回调say函数时,里面的this就变了,代表了当前的事件事件节点。
验证一下;
var page = {
init:function(){
this.node = document.body
this.bind()
},
bind:function(){
this.node.addEventListener('click',this.say)
},
say:function(){
console.log(this)
console.log('haha')
}
}
page.init()
结果;那怎么办呢?
var page = {
init:function(){
this.node = document.body
this.bind()
},
bind:function(){
this.node.addEventListener('click',this.say.bind(this))
},
say:function(){
console.log(this)
console.log('haha')
}
}
page.init()
那就对于上面那个DOM事件用bind处理一下吧:
document.addEventListener('click',function(e){
console.log(this)
setTimeout((function(){
console.log(this)
}).bind(this),200)
},false)
call ,apply设置this
fn.call(context,x,y,a,b,,,,)
fn.apply(contet,array)
//第一个参数是设置this的对象,剩下的是参数,一个是枚举形式,一个是数组形式。
function sum(){
var result = 0
var arg = Array.prototype.slice.call(arguments,0)
var agu = Array.prototype.sort.call(arguments)
var max = Math.max.apply(null,arg) //之前讲过参数的传递,传少了会默认从第一个参数开始匹配,
//第一个是this的指向,必须要设,就设null,否则报错的
Array.prototype.forEach.call(arguments,function(val){
result += val
})
console.log(result,arg,agu,max)
return result
}
sum(1,4,7,2)
就是简化我们的代码,一般call的处理速度更快。
caller
在函数A调用函数B时,被调用的B会自动生成一个caller属性,指向调用它的函数对象,如果没有被调用,或者并非被其他函数调用,caller= null。
function fn1(){
console.log(fn1.caller)
function fn2(){
console.log(fn2.caller)
}
fn2()
}
fn1()
arguments
函数在调用时,传入的参数被加工生产出的一个类数组对象.
callee
当函数被调用时,它的arguments.callee对象会指向这个函数自身。对自己的引用,比如递归。
匿名函数特好用。
var i =1
window.onclick = function(){
console.log(i)
if(i<20){
i++
setTimeout(arguments.callee,3000)
}
} //可以试试这个函数,相当于自己设定条件,只要满足了什么,
//它就可以无限调用自身的。可以做监察检测之类,判断状态,,,
//但是严格模式下用不了了,平时玩玩就可以了。
函数变量
- 实例变量:this,类的实例才能访问到的
- 静态变量:属性,直接类型对象能访问到的
- 私有变量:局部变量,当前作用域内有效变量
function A(){
var a =1 //局部变量
this.b = 2 //实例变量,必须要创造实例
}
A.c = 3 //属性对象
console.log(a) //error
console.log(A.b,A.c) //undefined ,3
var d = new A()
console.log(a) //error
console.log(d.b,d.c) //2,undefined
this练习
函数调用的步骤认识
fn.call(context,x,y,,,,) //任何函数调用都这样的,只不过大多数指向自身的设置为null。
fn(x,y) === fn.call(undefined,x,y)
obj.child.mesold(x,y) ===obj.child.mesold.call(obj.child,x,y)
function fn(){
console.log(this)
}
fn() === fn.call(undefined)
//如果传的是null,undefined,那么window默认是context,严格模式下写什么就是什么,没window的事儿。
var obj = {
foo:function(){
console.log(this)
}
}
var bar = obj.foo
obj.foo() === obj.foo.call(obj) //foo前面是obj,那就是obj
bar() === bar.call(null) //bar前面没东西,那就是null
function fn(){
console.log(this)
}
var arr = [fn,fn2]
arr[0]() ===arr.0.call(arr)
Event中的this
body.addEventListener('click',funcftion hand(){
console.log(this)
})
this都是由call,apply指定的,要知道this指谁,就要看hand函数前缀是哪个,但是事件是内置的方法,看不了源码,MDN上说,当事件触发时,
hand.call(event.currentTarget,event) //触发事件的作为this
,,,,
$ul.on('click','li',function(){
console.log(this)
}) //Jquary?
文档中说的意思:this指向当前正在执行的事件的元素。对直接事件就是绑定的元素。对代理事件,就是当前触发事件的元素。
最后:
不要瞎猜,不清楚就console.log(this).