JS 里的 this
- 1、在 function 内部被创建
- 2、指向调用时所在函数所绑定的对象
- 3、this 不能被赋值,但可以被 call/apply/bind 改变
this 的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定 this 到底指向谁,实际上 this 的最终指向的是最近调用该 this 所在的函数的对象
( this 所在的函数由哪个最近的对象调用,this 就会指向谁),当函数执行时,没有明确的调用对象时,则 this 指向 window
例子1:
function a(){
var user = "简书";
console.log(this); // this --> Window
console.log(this.user); // undefined
}
a();
`按照我们上面说的 this 最终指向的是最近调用该函数的对象,这里的函数 a 实际是被 Window 对象所点出来的,下面的代码就可以证明`
function a(){
var user = "简书";
console.log(this); // this --> Window
console.log(this.user); // undefined
}
window.a();
例子2:
var o = {
user: "简书",
fn: function(){
console.log(this); // this --> o
console.log(this.user); // 简书
}
}
o.fn();
`这里的 this 指向的是对象 o,因为你调用这个 fn 是通过 o.fn() 执行的,那自然指向就是对象 o,
这里再次强调一点,this 的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个`
var o = {
user: "简书",
fn: function(){
console.log(this); // this --> o
console.log(this.user); // 简书
}
}
window.o.fn();
`这段代码和上面的那段代码几乎是一样的,但是这里的 this 为什么不是指向 window,如果按照上面的理论,最终 this 指向的是调用该函数的对象,
这里先说个题外话,window 是 js 中的全局对象,我们创建的变量实际上是给 window 添加属性,所以这里可以用 window 点 o 对象`
这里先不解释为什么上面的那段代码 this 为什么没有指向 window,我们再来看一段代码
例子3:
var o = {
a: 10,
b: {
a: 12,
fn: function(){
console.log(this); // this --> b
console.log(this.a); // 12
}
}
}
o.b.fn()
`这里同样也是对象 o 点出来的,但是同样 this 并没有指向它,那你肯定会说我一开始说的那些不就都是错误的吗?
其实也不是,只是一开始说的不准确,接下来我将补充一句话,我相信你就可以彻底的理解 this 的指向的问题`
情况1:如果一个函数中有 this,但是它没有被上一级的对象所调用,那么 this 指向的就是 window,这里需要说明的是在 js 的严格版中 this 指向的不是 window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找
情况2:如果一个函数中有 this,这个函数有被上一级的对象所调用,那么 this 指向的就是上一级的对象
情况3:如果一个函数中有 this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this 指向的也只是它上一级的对象,例子3可以证明,如果不相信,那么接下来看例子4
例子4:
var o = {
a: 10,
b: {
// a: 12,
fn: function(){
console.log(this); // this --> b
console.log(this.a); // undefined
}
}
}
o.b.fn()
`尽管对象 b 中没有属性 a,这个 this 指向的也是对象 b,因为 this 只会指向它的上一级对象,不管这个对象中有没有 this 要的东西`
例子5:
var o = {
a: 10,
b: {
a: 12,
fn: function(){
console.log(this); // this --> window
console.log(this.a); // undefined
}
}
}
var j = o.b.fn;
j();
这里 this 指向的是 window,是不是有些蒙了?其实是因为你没有理解一句话,这句话同样至关重要
this 永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子5中虽然函数 fn 是被对象 b 所引用,但是在将 fn 赋值给变量 j 的时候并没有执行,所以最终指向的是 window,这和例子4是不一样的,例子4是直接执行了 fn
this 讲来讲去其实就是那么一回事,只不过在不同的情况下指向的会有些不同,上面的总结每个地方都有些小错误,也不能说是错误,而是在不同环境下情况就会有不同,所以我也没有办法一次解释清楚,只能你慢慢地的去体会
再举一个常见的例子,关于事件绑定
例子6:
btn.onclick = function(){
console.log(this); // this --> btn
}
btn.onclick();
function fn(){
console.log(this); // this --> obj
}
var obj = {
show: fn
}
btn.onclick = function(){
window.setTimeout(function(){
obj.show();
}, 100);
}
`自行体会一下,为什么这个 this 指向的是 obj`
科学是严谨的,得出结论之前,我们还是要反复验证,再看一个例子
例子7:
btn.onclick = function(){
setTimeout(function(){
console.log(this); // this --> Window
}, 0);
}
btn.onclick();
`当函数执行时,没有明确的调用对象时,则 this 指向 Window`
从例子7代码中看的出来, this 不再函数 btn 的内部,而是在函数 setTimeout 的内部,所以结果没有打印出 btn, 现在我们也不感到奇怪了
你可能还要问,为什么函数 setTimeout 里的 this 指向 window 呢?
这里其实算是一个特例,传入定时器的函数,是由哪个对象调用的?我们不得而知,这种情况,this 就指向 window,以下例子8、例子9同样
例子8:
function m1(){
function m2(){
console.log(this); // this --> Window
}
m2();
}
m1();
例子9:
var name = "the window";
var object = {
name: "my object",
getNameFunc: function(){
return function(){
return this.name; // this --> Window
}
}
}
console.log(object.getNameFunc()()); // the window
由 this 衍生出的问题
刚才遗留了一个问题没有解决
btn.onclick = function(){
setTimeout(function(){
console.log(this); // this --> Window
}, 0);
}
btn.onclick();
我们期待 this 指向 btn,而 this 现在却指向了 window,这个问题该怎么修复呢? 有很多办法
如果你不知道call、apply、bind,那么恐怕你只能看得懂例子10的方法
例子10:
var name = "the window";
var object = {
name: "my object",
getNameFunc: function(){
var that = this;
return function(){
return that.name; // that --> object
}
}
}
console.log(object.getNameFunc()()); // my object
btn.onclick = function(){
var that = this; // 使用变量保存 this,that 变量的值是不会随着环境改变的
setTimeout(function(){
console.log(that); // that --> btn
},0);
}
btn.onclick();
例子11:
btn.onclick = function(){
var that = this; // 使用变量保存 this
function fn(){ // 将代码写在一个函数 fn 中
console.log(this); // this --> btn
}
setTimeout(function(){
fn.call(that); // 强行指定 this 为 that 对象
}, 0);
}
btn.onclick();
/*
call 方法的作用,是调用函数,同时指定 this 可以代表谁
例如 fn.call(obj)
意思就是 调用函数 fn,并且 this 指向 obj 对象
*/
例子12:
btn.onclick = function(){
var that = this; // 使用变量保存 this
function fn(){ // 将代码写在一个函数 fn 中
console.log(this); // this --> btn
}
setTimeout(function(){
fn.apply(that); // 使用 apply 方法调用函数,强行指定 this 为 that 对象
}, 0);
}
btn.onclick();
/*
apply 方法的作用,是调用函数,同时指定 this 可以代表谁
例如 fn.apply(obj)
意思就是 调用函数 fn,并且 this 指向 obj 对象
*/
例子13:
btn.onclick = function(){
setTimeout(function(){
console.log(this); // this --> btn
}.bind(this), 0);
// 使用 bind 方法,将定时器函数的 this 强行绑定为事件函数的 this
}
btn.onclick();
/*
bind 方法的作用,是绑定函数的 this,同时返回绑定后的新函数
例如
var fb = fn.bind(obj);
window.fb();
无论谁调用 fb 函数, 函数的 this 都会指向 obj
*/
关于自行改变 this 的指向请看JavaScript中call, apply, bind方法的总结,详细的说明了我们如何手动更改 this 的指向
箭头函数版 this
1.如何判断箭头函数的 this
因为箭头函数不具备自己的 this,所以非常简单,假装它不存在,就像这样
这下 this 的指向非常清晰了吧
2. 箭头函数可以用 call 来改变 this 指向吗?
不能!! 试图改变箭头函数的 this 是徒劳的
构造函数版 this
function Fn(){
this.user = "简书";
}
var a = new Fn();
console.log(a.user); // 简书
这里之所以对象 a 可以点出函数 Fn 里面的 user 是因为 new 关键字可以改变 this 的指向,将这个 this 指向对象 a,为什么我说 a 是对象,因为用了 new 关键字就是创建一个对象实例,我们这里用变量 a 创建了一个 Fn 的实例(相当于复制了一份 Fn 到对象 a 里面),此时仅仅只是创建,并没有执行,而调用这个函数 Fn 的是对象 a,那么 this 指向的自然是对象 a,那么为什么对象 a 中会有 user,因为你已经复制了一份 Fn 函数到对象 a 中,用了 new 关键字就等同于复制了一份
更新一个小问题当 this 碰到 return 时
function fn() {
this.user = '简书';
return {};
}
var a = new fn;
console.log(a.user); // undefined
function fn() {
this.user = '简书';
return function(){};
}
var a = new fn;
console.log(a.user); // undefined
function fn() {
this.user = '简书';
return 1;
}
var a = new fn;
console.log(a.user); // 简书
function fn() {
this.user = '简书';
return undefined;
}
var a = new fn;
console.log(a.user); // 简书
如果返回值是一个对象,那么 this 指向的就是那个返回的对象,如果返回值不是一个对象那么 this 还是指向函数的实例
function fn() {
this.user = '简书';
return undefined;
}
var a = new fn;
console.log(a); // fn {user: "简书"}
还有一点就是虽然 null 也是对象,但是在这里 this 还是指向那个函数的实例,因为 null 比较特殊
function fn() {
this.user = '简书';
return null;
}
var a = new fn;
console.log(a.user); // 简书
知识点补充:
1.在严格版中的默认的 this 不再是 window,而是 undefined。
2.new 操作符会改变函数 this 的指向问题,虽然我们上面讲解过了,但是并没有深入的讨论这个问题,网上也很少说,所以在这里有必要说一下
function fn(){
this.num = 1;
}
var a = new fn();
console.log(a.num); // 1
为什么 this 会指向 a?首先 new 关键字会创建一个空的对象,然后会自动调用一个函数 apply 方法,将 this 指向这个空对象,这样的话函数内部的 this 就会被这个空的对象替代
通过 call() 和 apply() 改变函数执行环境的情况下,this 就会指向其他对象