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的同时立即把方法执行)