this指向全解析
定义
this实质上是函数内部的执行期的上下文指向
this只有在执行期才能明确指向,换言之,函数的执行条件的环境决定了this的指向
要点
普通函数的this指向window(非严格模式)
普通函数的this——严格模式下undefined
对象的函数中的this指向调用函数的对象实例
构造函数的this指向实例化对象
原型的this指向实例化对象
定时器中的this指向window
事件处理函数中的this指向触发事件的目标对象
普通函数的this——非严格模式下指向window
function test(){
this.a = 1; // window.a = 1 相当于全局
console.log(this);
console.log(this.a);
}
test()
// 1
普通函数的this——严格模式下undefined
var a = 1;
function test(){
'use strict';
console.log(this);
console.log(this.a);
}
test()
// error 严格模式下
对象方法中的this——指向调用函数的对象
var obj = {
a: 1,
test: function(){
console.log(this);
console.log(this.a)
}
}
// {a: 1, test: f}
// 1
function(a){
this.a = a;
console.log(this.a);
// return undefined
}
console.log(test(1).a) // undefined
不写返回值,相当于返回undefined
function test(a){
this.a = a;
console.log(this.a); // 1
console.log(window.a); // 1
}
test(1)
// 普通函数中,this相当于window
构造函数中的this——指向调用该构造函数的实例
function Test(a){
this.a = a;
console.log(this.a)
console.log(window.a)
}
var t1 = new Test(1)
// 1
// undefined
原型的this——指向实例化对象
// 原型上的this依然指向实例化对象
function Test(a){
this.a = a;
}
Test.prototype.sayHi = function(){
console.log(this.a)
}
var test = new Test(1);
test.sayHi(); // 1
定时器函数setTimeout setInterval 中的this——指向window
如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。
这是因为JS的定时器方法是定义在window下的。
1.在外部函数中将this存为一个变量,回调函数中使用该变量,而不是直接使用this
var name = 'window';
var obj = {
name: 'obj',
fn: function () {
var that = this;
var timer = null;
clearInterval(timer);
timer = setInterval(function () {
console.log(that.name); //obj
}, 1000)
}
}
2.箭头函数没有自己的this,用的是外面一层的this
var name = 'window';
var obj = { name: 'obj',
fn: function () {undefined
var timer = null;
clearInterval(timer);
timer = setInterval( () => {
undefined
console.log(this.name); //obj
}, 1000)
}
}
3.bind改变指向
let time=setInterval(function () {
this.second--;
if(this.second==0){
location.replace("./login.html")
}
}.bind(this),1000,1000)
事件处理函数中的this——指向绑定该函数的DOM元素
var btn = document.querySelector('#btn')
btn.onclick = function(){
console.log(this);
this.innerHTML = '加载中...';
this.disabled = true;
setTimeout(function(){
console.log(this);
this.innerHTML = '点击';
this.disabled = false;
// 出现问题,这里的this是window
// 解决方式1:箭头函数没有自己的this,用的是外面一层的this
// 解决方式2:bind
// 解决方式3:在外面定义一个变量var a = this,把a传进去
})
}
// <button id="btn">点击</button>
解决方式2
var btn = document.querySelector('#btn')
btn.onclick = function(){
console.log(this);
this.innerHTML = '加载中...';
this.disabled = true;
setTimeout(function(){
console.log(this);
this.innerHTML = '点击';
this.disabled = true;
}.bind(this), 2000) // bind的this指向的是绑定该事件处理函数的dom元素
}
补充bind函数用法
call/apply bind都可以用来改变this指向
call(context, arg1, arg2, arg3)
apply(context, [arg1, arg2, arg3])
bind(context, arg1, arg2, arg3)
区别是call/apply会立即执行,bind返回一个新的函数(this的指向改变)而不立即执行
var newFn = function test(a, b, c){
this最开始指向window,但由于使用了bind,因此this指向context
}.bind(context)
模块中的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>
<button>点击</button>
<script>
;(function () {
function Test(a, b){
// 构造函数中的this指向实例化对象
this.oBtn = document.querySelector('button');
this.a = a;
this.b = b;
this.init()
}
Test.prototype = {
init: function(){
// 原型中的this指向实例化对象
this.bindEvent();
},
bindEvent: function(){
this.oBtn.addEventListener('click', this.plus.bind(this), false)
// plus前面的this:事件处理函数中的this指向绑定该事件的DOM
// bind里面的this:处在原型的作用域中,所以也指向实例化对象
// 通过这种方式,click时能执行plus方法,否则会在oBtn下面找plus方法
},
plus: function(){
console.log(this.a + this.b);
}
}
window.Test = Test; // 将Test抛到全局
})()
new Test(1, 2)
</script>
</body>
</html>
补充call/apply
function Test1(a, b){
this.a = a;
this.b = b;
console.log(this.a, this.b)
}
function Test2(a, b, c, d){
this.a = a;
this.b = b;
Test1.call(this, a, b); // call把Test1的this改成Test2的this
this.c = c;
this.d = d;
}
var test2 = new Test2(1, 2, 3, 4)