人生就像一列开往坟墓的列车,路途上会有很多站,很难有人至始至终陪你走完全程,当陪你的人要下车时,即便不舍,也要心存感激,然后挥手告别。---sunnyhuang
在es5很多新手可能对于this感觉到很迷惑,看见有些大神对this灵活运用,心里很是羡慕,不要紧,这篇文章让你彻底明白什么是this
函数的四种调用
- func(p1,p2) 直接调用
- obj.child.method(p1,p2) 对象的方法调用
- func.call(null,p1,p2) 通过call调用
- func.apply(null,[p1,p2]) 通过apply调用
我们一般都使用的是第一种和第二种调用,但是正规的函数调用是第三种和第四种。
func.call(context,p1,p2) this就是这个context的值,后面的是传入的参数,在不严格模式下,context等于null或undefined的时候会自动指向为window对象,同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
面试题运用
你可能遇到过这样的题目
var obj={
sum: function(){
console.log(this)
}
}
var bar=obj.sum;
obj.sum() //输出的是obj
bar() //输出的确实window
我们不通过第一种或者第二种调用函数,通过第三种来调用函数上述代码的变换
obj.sum() --->obj.sum.call(obj) //obj就是context
bar() ---->bar.call(undefined) 可以简写bar.call() //window就是context
数组的运用
猜猜如果数组中的元素调用函数,this指向谁呢?
function fn(){console.log(this)}
function fn1(){console.log(this+"fn2")}
var arr=[fn,fn1]
arr[0]() >---你可以意淫成arr.0.call(arr)
小试牛刀
var a = {
name:"zhang",
sayName:function(){
console.log("this.name="+this.name);
}
};
var name = "ling";
function sayName(){
var sss = a.sayName;
sss(); //this.name = ? >--sss.call(undefined) window.name
a.sayName(); //this.name = ? >--a.sayname.call(a) a.name
(a.sayName)(); //this.name = ? >--只是对于a进行了一层包装,a.sayname.call(a) a.name
(b = a.sayName)();//this.name = ? >--先进行b的复制,然后再执行 b() b.call() window.name
}
sayName();
// ling
// zhang
// zhang
// ling
变形
var name = "ling";
function sayName(){
var a = {
name:"zhang",
sayName:getName
};
function getName(){
console.log(this.name);
}
getName(); //this.name = ? >--getName.call() window.name
a.sayName(); //this.name = ? >--a.sayName.call(a) a.name
getName.call(a);//this.name = ? 它直接转换了,所以是a.name
}
sayName();
//ling
// zhang
//zhang
继续变形,我看你能翻出天
var name = "ling";
var obj = {
name:"zhang",
sayName:function(){
console.log("this.name="+this.name);
},
callback:function(){
var that = this; //2. 所以这里的this值是obj that=this
return function(){
var sayName = that.sayName; //3. 这里就相当于是obj.sayName
that.sayName(); //this.name = ? //4. obj.sayName.call(obj) obj.name
sayName();//this.name = ? // sayName.call() window.name
}
}
}
obj.callback()() //1. 首先里面有一个this被复制给that 先转换这个obj.callback.call(obj)()
// zhang
//ling
让我们更加加深转换代码
- fun()
- obj.child.call(obj)
- arr.push(1)
- arr.push.call(arr,3)
- obj1.obj2.sum()
仰天抬头看看世界,思考1分钟人生,然后看题
1. fun.call()
2. obj.child()
3. arr.push.call(arr,1) //记住1是参数
4. arr.push(3)
5. obj1.obj2.sum.call(obj2)
挑战升级
理解Array的原型调用
如果你是一个看mdn的小baby,就会在数组上面经常看到Array函数的原型上面调用函数实现某种效果
1. 在es5的时候实现一个数组push另一个数组
var arr1=[1,2,3],arr2=[4,5,6]
Array.prototype.push.apply(arr1,arr2) //arr1=[1,2,3,4,5,6]
刚开始,由于自己没有开发过api,我也不是怎么理解这个的原理,下面我来分解一下
- 如果我想给arr1后面添加一个数据
arr1.push(1)
- 用我们刚刚的进行转换
arr1.push.call(arr,1)
>--变成applyarr1.push.apply(arr1,[1])
- 我们把[1]变成arr2
arr1.push.apply(arr1,arr2)
- 由于数组的提供的api,不可能每一个都是arr1,所以统一调用
Array.prototype
。我们把arr1换成Array.prototype
试试理解 Math.max.apply(null,[1,2,3])
ES6的扩展语句
arr1.push(...arr2)
Math.max(...arr1)