改变this的几种方式

深入理解call,apply,bind,new

js中this的指向一直是困扰人的一点,一般来说,谁调用这个方法,this就指向谁。

new

new过程发生了什么?
1.创建一个空对象
2. obj 的__proto__ 指向构造函数的原型对象 prototype
3.改变this指向,得到返回值
4.返回值是对象,则返回这个对象,不是则返回上面的空对象
看完下面的伪代码再看一遍上面的过程可能更清楚:

1
2
3
4
5
6
new Animal('cat') = {
var obj = {};//创建空对象
obj.__proto__ = Animal.prototype;//链接原型链
var result = Animal.call(obj,"cat");//改变this指向
return typeofresult ==='object'? result : obj;//返回值是
}

了解new的内部工作原理,看看下面几种情况:

1
2
3
4
5
6
7
function fn()  
{
this.user = '追梦子';
return {}; //这里返回一个空对象
}
var a = new fn; // a时空对象
console.log(a.user); //undefined

1
2
3
4
5
6
function fn()  
{
this.user = '追梦子'; //无返回值
}
var a = new fn; // fn无返回值
console.log(a.user); //追梦子

call

使用方法:function.call(obj,a1,a2...)
作用:改变this指向,function的调用者改变为obj

1
2
3
4
5
6
7
8
const myCall = function (context) {
context.fn = this
let args = Array.from(arguments).slice(1) //Array.from 把伪数组对象转为数组
let result = context.fn(...args)
delete context.fn
return result
}

apply

s使用方法:function.apply(obj,[a1,a2...])
作用:改变this指向,function的调用者改变为obj
省略了调用者是否是函数,context是否存在的判断

1
2
3
4
5
6
7
const myApply = function (context) {
// 保存this,调用apply的函数
context.fn = this
const result = arguments[1] ? context.fn(...arguments[1]) : context.fn()
delete context.fn
return result
}

bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const myBind = function(context){
// 保存调用bind的函数
const _this = this
// 保存参数
const args = Array.prototype.slice.call(arguments,1)
// 返回一个函数
return function F () {
// 判断是不是new出来的
if(this instanceof F) {
// 如果是new出来的
// 返回一个空对象,且使创建出来的实例的__proto__指向_this的prototype,且完成函数柯里化
return new _this(...args,...arguments)
}else{
// 如果不是new出来的改变this指向,且完成函数柯里化
return _this.apply(context,args.concat(...arguments))
}
}
}

注意箭头函数的this无法通过bind、call、apply来直接修改,及时加了也会无效。
深入了解this

上一篇

需要掌握的算法