this指向详解

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(在非严格模式情况下), 如果函数的调用者不存在,那么它的调用者将会是全局window对象

1. 全局环境中

1
console.log(this) // window

2. 事件处理函数

DOM 事件处理函数

1
2
3
4
5
6
7
box.onclick = function () {
// 事件处理函数中的this指向触发该事件的元素
console.log(this); // div
(function(){ // 函数自调用 this指向window
console.log(this) // window
})()
}

内联事件处理函数

1
2
3
4
<!--当代码被内联处理函数调用时,它的this指向所在的DOM元素(即div) -->
<div id="box" onclick="console.log(this)">div</div>
<!--当代码被包括在函数内部或自执行函数内时指向window -->
<div id="box" onclick="(function(){ console.log( this ) }) ()">div</div>

3. 函数内部

  • 函数直接执行

非严格模式下 this默认指向全局对象window

1
2
3
4
5
6
// 非严格模式下 函数执行, 如果没有明确指定执行的主体, this统一指向window
function test () {
console.log(this)
}
test() // window
window.test() // window

严格模式下 this为undefined

1
2
3
4
5
6
function test () {
"use strict" // 开启严格模式
console.log(this)
}
test() // undefiend
window.test() // window

  • 自执行函数(自调用函数表达式),this默认指向window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
!function(){
console.log(this + ' !') //window !
}();
(function(){
console.log(this + ' ()') //window ()
})();
-function(){
console.log(this + ' -') //window -
}();
+function(){
console.log(this + ' +') //window +
}();
~function(){
console.log(this + ' ~') //window ~
}();
(function(){
console.log(this + ' ()') //window ()
}());
  • call bind apply 改变函数体内部this指向
    apply call 都会使该函数立即执行,bind不会立即执行,而是返回一个新函数
  • call 函数.call(obj,arg1,arg2) 会直接调用该函数且第一个参数就是函数执行时的this,后面的参数可有多个
1
2
3
4
5
function test (m, n) {
console.log(this, m, n);
}
test(2, 3) // window 2 3
test.call({name: 'bob'}, 2, 3) // {name: 'bob'} 2 3
  • apply 函数.apply(obj,[arg1,arg2]) 会直接调用该函数且第一个参数是函数执行的this,后面是一个数组,执行时会将数组变成参数列表
1
2
3
4
function test (m, n) {
console.log(this, m, n);
}
test.apply({name: 'bob'}, [2, 3]) // {name: 'bob'} 2 3
  • 借助call apply 去使用MathArray 的方法案例
1
2
3
console.log(Math.max.apply(null, [2, 9, 5])); // 9
//console.log(document.getElementsByTagName("*").slice()); // 报错,不能直接使用slice
console.log(Array.prototype.slice.call(document.getElementsByTagName("*"))) // 正确用法

对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样

  • bind 函数.bind(obj) 创建一个新函数,并不会立即调用,但只能绑定一次
1
2
3
4
5
6
7
function test () {
console.log(this)
}
var fn = test.bind({name: 'bob'})
fn() // {name: 'bob'}
var fun = fn.bind({name: 'tom'})
fun() // {name: 'bob'}
  • 箭头函数 => 与定义时环境上下文中this一致 ,不能用bind,call,apply修改其内部指向,没有arguments对象
1
2
3
4
var fn = () => {
console.log(this); //此时定义时上下文中的this即window
}
fn() // window
1
2
3
4
5
6
7
8
9
10
11
box.onclick = function () {
//setTimeout setInterval 延时函数内部函数的this指向window
setTimeout(function(){
console.log(this) // window
//this.style.backgroundColor = "red"; 报错
}, 2000)
setTimeout(() => {
console.log(this) // div
this.style.backgroundColor = "red";
}, 1000)
}
  • apply、call、bind比较
    1
    2
    3
    4
    // apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
    // apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
    // apply 、 call 、bind 三者都可以利用后续参数传参;
    // bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

4. 对象内部

  • 函数作为对象的方法调用
1
2
3
4
5
6
7
8
// 作为对象的方法调用 this指向调用该函数的对象
var obj = {
a: 1,
fn: function (){
console.log(this.a)
}
}
obj.fn() // 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a = 1
var obj = {
a: 2,
// 作为对象方法的箭头函数this指向全局window对象
fn1: () => {
console.log('arrow',this.a)
},
// 作为对象方法的普通函数this指向调用它的对象
fn2: function () {
console.log('fun',this.a)
}
}
obj.fn1() // arrow 1
obj.fn2() // fun 2
  • 多层嵌套的对象 this 就近绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var a = 1
var obj = {
a: 2,
b: {
a: 3,
fn1: function () {
console.log(this.a)
},
fn2: () => {
console.log(this.a)
}
}
}
obj.b.fn1() // 3
obj.b.fn2() // 1
var temp = obj.b.fn1;
temp() // 1 函数直接调用this默认指向window
  • 函数作为返回值使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var a = 1
var obj = {
a: 2,
fn1: function () {
return function () {
console.log(this.a)
}
},
fn2: function () {
return () => {
console.log(this.a)
}
}
}
obj.fn1()() // 1
// obj.fn1() 执行返回 function () { console.log(this.a) }
// obj.fn1()() 相当于 (function () { console.log(this.a) })()
// fn() 直接执行 this即window
obj.fn2()() // 2
// obj.fn2() 执行返回 function () { console.log(this.a) }
// obj.fn2()() 相当于 (() => { console.log(this.a) })()
// (() => {})() this指向上下文中的this即obj
  • 函数或方法被赋值于变量时 this指向这个变量的拥有者
1
2
3
4
5
6
7
8
9
10
11
var tew = '111'
var it = {
tew: '222',
getTew() {
console.log(this.tew)
}
}
it.getTew() // 222
fn1 = it.getTew
console.log(fn1) // getTew() { console.log(this.tew) }
fn1() // 111

5. 构造函数中

1
2
3
4
5
6
7
8
9
10
function Person () {
console.log(this) // this指向实例化出来的对象
this.name = "bob"
}
Person() // window
var p = new Person(); // Person {name: "tew"}
console.log(p) // Person {name: "bob"}
p.name = 'tew'
console.log(p) // Person {name: "tew"}
console.log(p.name) // tew

6. 原型链中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 原型链中的this指向调用它的对象
// 构造函数
function Person (name) {
this.name = name // this指向调用该函数的对象
}
// 原型扩展绑定 showName方法
Person.prototype.showName = function () {
console.log(this.name)
}
// 实例化对象
var p = new Person('bob')
// 实例化对象的__proto__ 指向其构造函数的prototype
console.log(p.__proto__ === Person.prototype); // true
console.log(p.__proto__); // showName() {}
console.log(Person.prototype); // showName() {}
p.showName() // bob
-------------本文结束感谢您的阅读-------------
0%