Javascript中this的指向
this是函数运行时,自动生成的函数运行环境对象
(自己理解的话,this其实就是根据函数引用地址的作用域来判断执行环境)
-
什么是执行环境?
js高级程序第4章原话
所有变量(包括基本类型和引用类型)都存在于一个执行环境(也称为作用域)当中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。
而实际上我们可以理解:在编译过程中,在AST生成可执行指令的过程中,会确定词法作用域,这个是严格意义的作用域,而在代码执行过程中,执行环境根据词法作用域和执行的代码确定了变量生命周期和被访问权限。
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.foo() // 1
foo() // 2
this的常见使用场景
-
作为一般函数执行时,this 指代全局对象
-
作为对象属性执行时,this 指代调用方法的对象
-
作为构造函数调用时,this 指代 new 出的对象
-
函数通过call、apply、bind 调用时,this 指代call、apply、bind的第一个参数
this的原理
先提两个问题:
- 为什么函数中需要this
- this是怎么做到可以指向函数的运行环境的
原理
this的设计和内存里的数据结构有关系
JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 }
,然后把这个对象的内存地址赋值给变量obj
。
var obj = {foo:5}
原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的foo
属性,实际上是以下面的形式保存的。
可问题是如果属性值保存的是一个函数
var obj = { foo: function () {console.log(this.x)},x:2};
var x =1;
var foo = obj.foo;
foo();
obj.foo();
// 可以理解成这样
var foo = function () {
console.log(this.x);
}
var x = 1;
var obj = {
foo: foo,
x: 2,
};
// 单独执行
foo() // 1
// obj 环境执行
obj.foo() // 2
这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo
属性的value
属性。
由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。
回到开头提出的问题
obj.foo()
是通过obj
找到foo
,所以就是在obj
环境执行。一旦var foo = obj.foo
,变量foo
就直接指向函数本身,所以foo()
就变成在全局环境执行。
而JavaScript 允许在函数体内部,引用当前环境的变量。
现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this
就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
回调函数中的this
function haha(){
console.log(this);
}
function huidiao(diao){
diao(); // window
// 因为diao就是函数的直接执行,没有引用地址的关系,所以回调函数的this通常都指向最顶层环境
}
var obj = {a:hah}
huidiao(obj.a)
点击事件函数中的this
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
// 如果是元素中绑定事件,this指向window
<button id="haha" onclick="dianji()">anniu</button>
</body>
<script>
let haha = document.getElementById('haha');
console.log('hah',haha);
let dianji = function(e){
console.log(this,'=======');
}
// 如果是js绑定事件,this指向DOM元素对象
haha.onclick=dianji
</script>
</html>
原因
// 如果是html注册事件,onclick如下
Window:{
document:{
all:{
button#haha:{
attributes:{
id:{value:'haha'}
onclick:{value:'dianji()'}
}
},
onclick:function onclikc(event){dianji()}
}
}
}
// 如果是js注册事件,onclick如下(包含一个元素同时绑定了html事件和js事件)
Window:{
document:{
all:{
button#haha:{
attributes:{
id:{value:'haha'}
onclick:{value:'dianji()'}
}
},
onclick:function(e){console.log(this,'=======')};
}
}
}
}
什么时候需要重命名this
当函数嵌套且内层函数和外层函数指向的环境上下文不一致时
或者用箭头函数来解决,箭头函数不会生成this
$('#conten').click(function(){
//this是被点击的#conten
var that =this;
$('.conten').each(function(){
//this是.conten循环中当前的对象
//that仍是刚才被点击我的#conten
})
})