this关键字
面向对象语言中 this 表示当前对象的一个引用,会随着执行环境的改变而改变。
就好像人称代词一样,“它”可能指阿猫阿狗,可能指一个城市,可能指一个国家,可能指一个星球。
比如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var message = '我是全局'
var a = {
message : '我是a',
getName:function() {
console.log(this.message)
}
}
a.getName()
var getWName = a.getName
getWName()
</script>
</body>
</html>
执行结果:
接下来我们从执行this的不同环境进行分析。
全局
- 其实核心就是指向调用它的对象。
- 在非严格模式下,全局变量,全局方法都是window对象的成员。
单独使用this
- 单独使用 this,则它指向全局(Global)对象。
- 在浏览器中,window 就是该全局对象为 [object Window]
- 在浏览器中:
console.log(this)
- 执行结果:
函数中使用 this
非严格模式
在函数中,函数的所属者默认绑定到 this 上。
在浏览器中,window 就是该全局对象为 [object Window]
function myFunction() {
return this;
}
严格模式
严格模式下函数是没有绑定到 this 上,这时候 this 是 undefined。
"use strict";
function myFunction() {
return this;
}
对象
在对象方法中, this 指向调用它所在方法的对象。
var name = '我是全局'
var a = {
name:'我是a',
b:{
name:'我是b',
getBName:function () {
console.log(this.name)
}
},
getAName:function () {
console.log(this.name)
}
}
a.b.getBName()
a.getAName()
console.log(name)
执行结果:
构造函数或class类
在构造函数中,new创建实例对象时,会把this指向创造出的实例
function Pro(){
this.x = '1';
this.y = function(){};
}
var p = new Pro();
事件绑定
事件绑定共有三种方式:行内绑定、动态绑定、事件监听
行内绑定又分两种
行内绑定1(全局)
<input type="button" value="按钮" onclick="clickFun()">
<script>
function clickFun(){
console.log(this) // 此函数的运行环境在全局window对象下,因此this指向window;
}
</script>
此时函数的运行环境在全局window对象下,因此this指向window
执行结果:
详细解释见:案例1
行内绑定2(当前节点)
<input type="button" value="按钮" onclick="console.log(this)">
<!-- 运行环境在节点对象中,因此this指向本节点对象 -->
运行环境在节点对象中,因此this指向本节点对象
执行结果:
原因
当事件触发时,onclick属性值就会作为JS代码被执行,当前运行环境下没有clickFun函数,因此浏览器就需要跳出当前运行环境,在整个环境中寻找一个叫clickFun的函数并执行这个函数,所以函数内部的this就指向了全局对象window;如果不是一个函数调用,直接在当前节点对象环境下使用this,那么显然this就会指向当前节点对象
动态绑定与事件监听
<input type="button" value="按钮" id="btn">
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){
this ; // this指向本节点对象
}
</script>
因为动态绑定的事件本就是为节点对象的属性(事件名称前面加’on’)重新赋值为一个匿名函数,因此函数在执行时就是在节点对象的环境下,this自然就指向了本节点对象;
事件监听中this指向的原理与动态绑定基本一致,不再阐述;
函数对象的call()、apply()
- 函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。
- 这个两个方法的最大作用基本就是用来强制指定函数调用时this的指向
- 两者的区别只在传参方式上。
call()
call方法使用的语法规则
函数名称.call(obj,arg1,arg2…argN);
参数说明:
obj:函数内this要指向的对象,
arg1,arg2…argN :参数列表,参数与参数之间使用一个逗号隔开
var a = {
name:'a'
}
function sayHello(age) {
console.log('大家好,我是'+this.name+'今年'+age+'岁')
}
sayHello.call(a,18)
执行结果:
apply()
函数名称.apply(obj,[arg1,arg2…,argN])
参数说明:
obj :this要指向的对象
[arg1,arg2…argN] : 参数列表,要求格式为数组
var a = {
name:'a'
}
function sayHello(age) {
console.log('大家好,我是'+this.name+'今年'+age+'岁')
}
sayHello.apply(a,[18])
执行结果:
案例详解
对象中的非成员函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var a = {
age:18,
test1:function(){
//在成员方法中写一个函数并调用
function aaa (){
console.log(this)
}
aaa()
}
}
a.test1()
</script>
</body>
</html>
执行结果:
- 在非严格模式的js中,非成员方法的函数默认绑定window;
- 这里的函数aaa并不是a的成员方法,所以默认绑定window
- 行内表达式(1)也是这个原理
变量与属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>console.log(this)</h1>
<script>
var message = '我是全局'
var a = {
message : '我是a',
getName:function() {
var message = '我是捣蛋的'
console.log('获取变量:'+message)
console.log('获取属性:'+this.message)
}
}
console.log('-----------------------------------------------------当前执行a的方法---------------------------------------------')
a.getName()
var getWName = a.getName
console.log('-----------------------------------------------------当前执行window的方法----------------------------------------')
getWName()
</script>
</body>
</html>
执行结果:
- 全局变量是window的属性
- a的message是它的成员属性
- getName方法中的message是局部变量,不会影响window和a的属性