this
就是js
里的关键字,有特殊意义,代表函数执行主体。
一、定义
- 函数执行的主体(不是上下文):意思是谁把函数执行的,那么执行主体就是谁
二、使用情况
-
1、全局作用域里的
this
是window
,全局作用域下相当于是window.fn()
执行只是把window.
省略了(严格模式下是undefined
)。console.log(this === window) // true window.a = 13; console.log(this.a) // 13
-
2、函数里的
this
,看执行主体前有没有点
,如果有点
,那点
前面是谁,函数里的this
就是谁,如果没有点
,那函数里的this
就是window
,严格模式下是undefined
。function fn(){ console.log(this) } fn();//window let obj = { fn:function(){ console.log(this) } } obj.fn();//obj var f = obj.fn; f();//window
-
3、自执行函数里的
this
是window
或undefined
(严格模式下)(function(){ console.log(this);//==>window })(); ~function(){}();//==>window +function(){}();//==>window -function(){}();//==>window !function(){}();//==>window
-
4、回调函数里的
this
一般情况下是window
let ary = [1,2,3]; ary.forEach(function(item,index){ console.log(this) }) //================================// setTimeout(function(num){ console.log(num) console.log(this) },1000,1) //================================// function fn(m){ m() } fn(function(){ console.log(this) })
-
5、箭头函数没有
this
:但是要是在箭头函数里使用
this
,他就会往上一级作用域查找,如果上一级作用域也没有,那就继续往上找,直到找到全局的window
为止let obj = { num: 2, fn: function () { // this-->obj let m = () => { // this-->obj console.log(this.num) // obj(向上一级作用域查找) } m() } } obj.fn()
-
6、构造函数里的
this
是当前实例 -
7、实例原型上的公有方法里的
this
一般是当前实例(下面改变this
的方法中有体现) -
8、给元素绑定事件行为,那事件里的
this
就是当前被绑定的元素本身btn.onclick = function(){ console.log(this) // btn }
三、面向对象中有关私有/公有方法中的THIS问题
总结下来
this
在面向对象中,主要还是看是谁执行的,也就是执行函数点前面是谁
- 1、方法执行,看前面是否有点,点前面是谁
THIS
就是谁- 2、把方法总的
THIS
进行替换- 3、再基于原型链查找的方法确定结果即可
四、改变this
指向:call
/apply
/bind
每一个函数(普通函数/构造函数/内置类)都是
Function
这个内置类的实例,所以:函数.__proto__===Function.prototype
,函数
可以直接调取Function
原型上的方法
call / apply / bind
- 原型上提供的三个公有属性方法
- 每一个函数都可以调用这个方法执行
- 这些方法都是用来改变函数中的
THIS
指向的
1、call
语法:
- 函数.call(context,params1,params2....)
定义:
- 函数基于原型链找到
Function.prototype.cal
l这个方法,并且把它执行,在call
方法执行的时候改变里面的this关键字
作用:
- 让当前函数执行
- 把函数中的
THIS
指向改为第一个传递给CALL
的实参 - 把传递给
CALL
其余的实参,当做参数信息传递给当前函数
注意:
- 如果执行
CALL
一个实参都没有传递,非严格模式下是让函数中的THIS
指向WINDOW
,严格模式下指向的是UNDEFINED
fn.call(null)
;- //=>
this:window
- 严格下是
null
(第一个参数传递的是null
/undefined
/不传
,非严格模式下this
指向window
,严格模式下传递的是谁this
就是谁,不传this
是undefined
)
- //=>
使用方法:

function fn(){}
fn.call(); //=>fn函数基于原型链找到Function.prototype上的call方法,并且让其执行(执行的是call方法:方法中的this是fn)
fn.call.call(); //=>fn.call就是Function.prototype上的call方法,也是一个函数,只要是函数就能用原型上的方法,所以可以继续调用call来执行
Function.prototype.call = function $1(){
//...
}
fn.call => $1
fn.call() => $1() this:fn
fn.call.call() => $1.call() => 继续让call执行,this:$1
实例.方法():都是找到原型上的内置方法,让内置方法先执行(只不过执行的时候做了一些事情会对实例产生改变,而这也是这些内置方法的作用),内置方法中的THIS一般都是当前操作的实例
call简单实现原理
```javascript
//=>我们的需求是想让FN执行的时候,方法中的THIS指向OBJ
obj.fn(); //=>Uncaught TypeError: obj.fn is not a function
//因为此时obj中并没有fn这个属性
-------解决办法---------
obj.fn = fn;
obj.fn(); //=>this:obj //=>'OBJ'
delete obj.fn;//=>对象中原本没有,所以使用后要删掉
```
自己基于原生JS简单的实现内置的call方法
-
详细梳理:
~ function () { /* * myCall:改变函数中的THIS指向 * @params * context 可以不传递,传递必须是引用类型值(因为后面要给它加$fn的属性) */ function myCall(context) { //this:sum 也就是当前要操作的这个函数实例 context = context || window; let args = [], //=>除第一个参数外剩余传递的信息值 result; for (let i = 1; i < arguments.length; i++) { args.push(arguments[i]); } context.$fn = this; result = context.$fn(...args); //=>args=[10,20] ...是ES6中的展开运算符,把数组中的每一项分别的展开传递给函数 //=>context.$fn(10,20) delete context.$fn; return result; } /* 扩展到内置类的原型上 */ Function.prototype.myCall = myCall; }();
-
可简写为
function myCall(context,...arg){ //context-->obj this-->fn context = context || window; // 如果你不传参、或者传null、传undefined,那context的值都是window let res = null; // 创建一个变量,准备接收函数执行结果 context.fn = this; // 把fn增加到obj里 res = context.fn(...arg); // 让fn指向 delete context.fn; // 把fn从obj里删除 return res; // 把this的返回值return出去 } Function.prototype.myCall = myCall; console.log(fn.myCall(obj,1,2)) // 'ss'
-
以题为例分析

最终我们可以得出:
- 一个
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,并且传递10/20
fn.call(obj,10,20);
fn.apply(obj,[10,20]);
3、bind
和
call
/apply
一样,也是用来改变函数中的this
关键字的,只不过基于bind
改变this
,当前方法并没有被执行,类似于预先改变this
语法:
- 函数.bind(context,params1,....)
区别:
bind
是预处理this
,他并不会让函数执行bind
方法的返回值是一个改变this
之后的新函数
作用:
- 把函数中的
THIS
指向通过预处理的方式改为第一个传递给BIND
的实参 - 一般使用在绑定点击事件,不让函数立即执行时
let obj={name:'OBJ'}; function fn(){ console.log(this.name); } document.body.onclick=fn; //=>当事件触发,fn中的this:BODY //=>点击BODY,让FN中的THIS指向OBJ //document.body.onclick=fn.call(obj); //=>基于call/apply这样处理,不是把fn绑定给事件,而是把fn执行后的结果绑定给事件 document.body.onclick=function(){ //this:BODY fn.call(obj); } document.body.onclick=fn.bind(obj);
注意:
- 在
IE6~8
中不支持bind
方法 - 预先做啥事情的思想被称为“柯理化函数”
优点:
bind
的好处是:通过bind
方法只是预先把fn
中的this
修改为obj
,此时fn
并没有执行呢,当点击事件触发才会执行fn
(call
/apply
都是改变this
的同时立即把方法执行)
