this的指向问题
一、非箭头函数
关于 this
的指向,记住最核心的一句话: 哪个对象调用函数,函数里面的this指向哪个对象。
(一)全局环境
全局环境中(在任何函数体外部)this都指向全局对象
//浏览器环境 Window对象即是全局对象
console.log(this === window); // true
//node环境 由于模块化node中this指向module.exports node的全局对象为global
console.log(this === module.exports); // true
(二)普通函数
在函数内部,this
的值取决于函数被调用的方式。此外,在严格模式和非严格模式之间也会有一些差别
非严格模式
——非严格模式下,this指向window
var name = "xiaoyu"
function fn() {
var name = "小鱼"
console.log(this.name)
}
fn() //xiaoyu
严格模式
- 严格模式下,
this
将保持进入执行环境时的值 - 没有被执行环境(execution context)定义,this保持为
undefined
"use strict"
var name = "xiaoyu"
function fn() {
var name = "小鱼"
console.log(this)
console.log(this.name)
}
fn() //Cannot read property 'name' of undefined
(三)对象方法
——对象方法 this指向该方法所属对象
var name = "xiaoyu"
var object = {
name: "小鱼",
sayHi() {
console.log(this.name)
}
}
object.sayHi() //小鱼
(四)构造函数
——构造函数的this 指向创建出来的实例对象
——new实例对象的过程改变this指向
var name = "xiaoyu"
function Person() {
this.name = "小鱼"
console.log(this); //Person {name: "小鱼"}
}
var person = new Person()
(五)绑定事件函数
——事件绑定的处理函数 this指向函数的调用者 (绑定该函数的元素)
<button>点击</button>
<script>
var btn = document.querySelector('button')
btn.onclick = function() {
console.log('事件处理函数的this:' + this)
//事件处理函数的this:[object HTMLButtonElement]
console.log(this) //<button>点击</button>
}
</script>
(六)定时器函数
——定时器函数 this指向window
window.setTimeout(function(){
console.log('定时器函数中this:' + this)
//定时器函数中this:[object Window]
console.log(this) //window
}, 1000)
(七)立即指向函数
——立即执行函数 this指向window
(function() {
console.log('立即执行函数中this' + this)
//立即执行函数中this[object Window]
console.log(this) //window
})()
(八)匿名函数
——匿名函数的执行环境是全局性的
var name = 'xiaoyu'
var person = {
name :'小鱼',
sayName:function () {
return function () {
console.log(this.name)
}
}
}
person.sayName()() //xiaoyu
小结
函数调用方式 | 函数内部this的指向 |
---|---|
普通函数调用 | window |
对象方法调用 | 该方法所属的对象 |
构造函数调用 | 创建的实例对象 |
事件绑定方法 | 绑定事件的的对象 |
定时器函数 | window |
立即执行函数 | window |
匿名函数 | window |
二、箭头函数
箭头函数出现的意义:让函数表达更简洁,增强可读性;解决匿名函数、setTimeout和setInterval的回调函数 this 指向的问题
箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”
var name = "xiaoyu"
var object = {
name: "小鱼",
sayHi: () => { console.log(this.name) }
}
object.sayHi() //xiaoyu this继承自父执行上下文
三、 改变this的指向
var name = "xiaoyu"
var obj = {
name: "小鱼",
fn: function() {
setTimeout( function() {
console.log(this.name)
}, 0)
}
}
obj.fn() //xiaoyu
(一)使用that保存this变量
var name = "xiaoyu"
var obj = {
name: "小鱼",
fn: function() {
var that = this;
setTimeout( function() {
console.log(that.name)
}, 0)
}
}
obj.fn() //小鱼
(二)使用 call apply bind
bind()
方法创建一个新的函数,在bind()
被调用时,这个新函数的this
被指定为bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用返回值:改变this指向的原函数拷贝
var name = "xiaoyu"
var obj = {
name: "小鱼",
fn: function() {
setTimeout( function() {
console.log(this.name)
}.bind(obj), 0)
}
}
obj.fn() //小鱼
(三)new 实例
使用new创建新对象的过程
- 创建一个空对象
- 将构造函数的作用域赋值给该对象 (将this绑定在刚创建的对象上)
- 执行构造函数的代码,为该对象添加属性
- 返回一个新的的对象
var Person = function(name, age) {
this.name = name;
this.age = age;
};
Person.prototype.show = function() {
console.log(this.name, this.age);
};
var p = new Person("xiaoyu", 18);
console.log(p);
(四)箭头函数
var name = "xiaoyu"
var obj = {
name: "小鱼",
fn: function() {
setTimeout( () => console.log(this.name), 0)
}
}
obj.fn() //小鱼
四、面试题
var baz = 0;
let foo = {
bar:function() {
console.log(this,this.baz);
return this.baz;
},
baz:1
};
let foo2 = {
baz:2
};
let a = foo.bar();
//作为对象的方法调用,this指向调用函数的对象,即foo
let b = foo.bar.call(foo2);
//使用call方法将this绑定到第一个参数对象向,此时,this指向foo2
let fn = foo.bar;
let c = fn();
//let fn创建的对象,此时,fn = function(){...},此时函数执行时,默认指向全局window对象
let d;
(function test(){
d = arguments[0]()
})(foo.bar);
// arguments.callee包括了一个函数的引用去创建一个arguments对象,它能让一个匿名函数很方便的指向本身,即此时this指向arguments类数组对象
console.log(a,b,c,d) //1 2 0 undefined
参考文章