JS中this相关问题梳理
this
就是js
里的关键字,有特殊意义,代表函数执行主体
一、定义
- 函数执行主体(不是作用域):意思是谁把函数执行了,那么执行主体就是谁
二、使用情况
- 全局作用域里的
this
是window
, 全局作用域下相当于是window.fn()
执行只是把window
省略了(严格模式下是undefined
)。
console.log(this === window); // truewindow.a = 13;
console.log(this.a); // 13
- 函数里的
this
, 看执行主体前有没有点
,如果有点
,那点
前面是谁,函数里的this
就是谁,如果没有点
,那函数里的this
就是window
,严格模式下是undefined
。
function fn() {console.log(this);
}
fn(); // windowlet obj = {fn: function() {console.log(this);}
}
obj.fn(); // obj{...}var f = obj.fn;
f(); // window
- 立即执行函数里的
this
是window
或undefined
(严格模式下)
(function() {console.log(this); //==> window
})();
~function(){}(); //==> window
+function(){}(); //==> window
-function(){}(); //==> window
!function(){}(); //==> window
- 回调函数里的
this
一般情况下是window
let arr = [1, 2, 3];
arr.forEach(function(item, index) {console.log(this); // 打印了三个window
})setTimeout(function(num) {console.log(num); // 1console.log(this); // window
}, 1000, 1)function fn(m) {m();
}fn(function() {console.log(this); // window
})
- 箭头函数没有
this
:
但是要是在箭头函数里使用
this
,它就会往上一级作用域查找,如果上一级作用域也没有,那就继续查找,直到找到全局作用域window
为止
let obj = {num: 2,fn: function() {// this --> objlet m = () => {// this --> objconsole.log(this.num); // obj (要向上一级查找)}m();}
}
obj.fn();
-
构造函数里的
this
是当前的实例 -
实例原型上的公有方法里的
this
一般是当前实例(下面改变this
的方法中有所体现) -
给元素绑定事件行为,那事件里的
this
就是当前被绑定的元素本身
btn.onclick = function() {console.log(this); // btn
}
三、面向对象中有关私有/公有方法中的THIS问题
总结下来
this
在面向对象中,主要还是看谁执行的,也就是执行函数点前是谁
- 方法执行,看前面是否有点,点前面是谁
this
就是谁- 把方法总的
this
进行替换- 在基于原型链查找的方法确定结果即可
四、改变this
指向:call
/apply
/bind
每一个函数(普通函数/构造函数/内置类)都是
Function
这个内置类的实例,所以函数.__proto__ === Function.prototype
,函数
可以直接调取Function
原型上的方法
call / apply / bind
- 原型上提供的三个公有属性方法
- 每一个函数都可以调用这个方法执行
- 这些方法都是用来改变函数中的
this
指向的
- call
函数基于原型链扎找到Function.prototype.call
这个方法,并且把它执行,在call
方法执行的时候改变里面的this
关键字
- 让当前的函数执行
- 把函数中的
this
指向改为第一个传递给call
的实参 - 把传递给
call
其余的实参,当做参数信息传递给当前函数
注意:
- 如果执行
call
一个实参都没有传递,非严格模式下是让函数中的this
指向window
,严格模式下指向的是undefined
fn.call(null)
;- //=>
this.window
- 严格下是
this
(第一个参数传递的是null
/undefined
/不传
,非严格模式下this
指向window
,严格模式下传递的是谁this
就是谁,不传this
是undefined
)
使用方法:
window.name = 'WINDOW';
let obj = {name: 'OBJ'};
function fn(n, m) {console.log(this.name);
}
fn(10, 20); // WINDOW --> 因为函数刚开始指向window
fn.call(obj); // OBJ --> 改变函数this指向,此时函数fn指向obj
fn.call(obj, 10, 20); // OBJ
fn.call(10, 20); // undefined
可以得出:
- 一个 call 是让左边函数之后执行(
this
是传递的参数)- 多个 call 是让最后传参的函数执行(
this
是window
/undefined
)
2、apply
和
call
方法一样,都是把函数执行,并且改变里面的this
关键字,唯一的区别就是传递给函数参数的方式不一样
call
是一个个传参apply
是按照数组传参
let obj = {name: 'OBJ'};
let fn = function(n, m) {
console.log(this.name); //
}
//=>让fn方法执行,让方法中的this变为obj,并传递1, 2给函数
fn.call(obj, 1, 2);
fn.apply(obj, [1, 2]);
3、bind
和
call
/apply
一样,也是用来改变函数中的this
关键字,只不过基于bind
改变this
,当前方法并没有被执行,类似于预先改变this
bind
是预处理this
,他并不会让函数执行bind
方法的返回值是一个改变this
之后的新函数
作用:
-
把函数中的
this
指向通过预处理的方式改为第一个传递给bind
的实参 -
一般使用在绑定点击事件,不让函数立即执行
let obj = {name: 'mary'};
function fn() {console.log(this.name);
}
btn.onclick = fn; // this --> btn// 想让this指向obj???
btn.onclick = fn.call(obj); // 这样也不行,call/apply只是把执行的结果返回给点击事件
btn.onclick = fn.bind(obj); // this --> obj// 如果非要用call/apply
btn.onclick = function() {return fn.call(obj); // 把函数返回给点击事件才可以
}
注意:
- 在IE6~8中不支持
bind
方法 - 预先做啥事情的思想被称为“
柯理化函数
”
优点:
bind
的好处是:通过bind
方法只是预先把fn
中的this
修改为obj
,此时fn
并没有执行,当点击事件触发才会执行fn
(call
/apply
都是通过改变this
的同时立即把方法执行)