参考:
[译] JavaScript 中至关重要的 Apply, Call 和 Bind
JavaScript 中 call()、apply()、bind() 的用法
一、个人对apply,call,bind的认知
- 目的:常常用来改变原调用方法中,this的指向;
- 可以用apply,call,bind来优雅地处理很多生产使用中的问题,也是很多底层代码中经常被使用的,这篇文章只是基础理解,更深入的使用和介绍可以阅读参考文章,或者其他;
二、基础使用[vscode 中 this指向与浏览器跑的时候不一样]
1. 以下代码中,因为this指向调用方法getAge
的obj
对象,所以输出为小明,undefined
,参考图片中,运行时堆栈信息里this变量的指向;
var name="小王",age="22"
var obj={
name:"小明",
age:this.age, //对象声明过程中,这个this指向window
getAge:function(){
// 处于函数块中,此时this,指向函数所属的obj这个对象
console.log(this.name,this.age)
},
doFun:function(city,job){
console.log(this.name,this.age,city,job)
}
}
obj.getAge()
// output: 小明,undefined
2. 创建一个新对象newObj
,如果想让newObj
也能调用原obj
的方法,那么此时就可以通过apply/call/bind来轻巧的解决;
// 为了让newObj调用obj的方法,参数1都是this重新指向的对象
obj.getAge.apply(newObj) //typeof obj.getAge.apply(newObj) =>undefined,输出 执行结果,相当于执行了一次函数
obj.getAge.call(newObj) //typeof obj.getAge.call(newObj) =>undefined,输出 执行结果,相当于执行了一次函数
obj.getAge.bind(newObj)() //typeof obj.getAge.call(bind) =>function,说明仅返回函数声明
// output: 小林,26
三、参数传递
- 仅apply的第二参数开始以数组形式传递形参,call和bind传递参数,用逗号隔开。
obj.doFun.apply(newObj,["北京","developer"])
obj.doFun.call(newObj,"北京","developer")
obj.doFun.bind(newObj,"北京","developer")()
// output: 小林 26 北京 developer
四、部分简单使用扩展
- 比如改变函数声明中this(window)的指向(非node环境)。
var colorObj={
color:"red"
}
var animal="cat"
function getName(){
console.log(this.animal,animal,this.color)
}
getName() //output: cat cat undefined,这里的this指向window
getName.call(colorObj) //output: undefined cat red 这里的this指向colorObj
- 一些绑定在元素上的事件,比如click等,此时this和全局指向;
<body>
<div id="domId" onclick="setColor()">domId</div>
<script>
var colorObj={
color:"red",
getColor:function(){
return this.color
}
}
function setColor(){
console.log(this)
this.style.color="red"
}
var ele=document.getElementById("domId")
// setColor() // =>error
setColor.call(ele) //=>success
</script>
</body>
- 利用apply第二参数为数组的特性,轻巧地实现一些功能。
- 数组比较大小,常规比较是排序后,取其中的最大数,这里可以借用
Math.max
等方法; - 通过
push
和apply
,来拼接数组;
var nums=[1,240,33,42]
// 这里因为没有用到this,所以可以使用null
// 因为apply接收的第二个参数为数组,当做一系列的实参,实际上相当于Math.max(1,240,33,42)
console.log(Math.max.apply(null,nums)) //output: 240
var alps=["A","B","C"]
Array.prototype.push.apply(nums,alps)
console.log(nums) //output: [1, 240, 33, 42, "A", "B", "C"]
- 对于类数组借用Array的原型对象上(
Array.prototype
)的方法。
- 我理解的类数组,一般指具备数组特征的类似对象 :通过角标调用,如arryLike[0];具有长度属性length;可以通过for循环遍历;
//类数组
var arryLike = {
0: 1,
1: 2,
2: 3,
length: 3
}
- 常见的类数组,包括但不限于,函数形参中的arguments,HTML节点结合HTMLCollection等。
function fn(a, b) {
// arguments.pop() //=>error,arguments这种类数组无法使用全部Array的方法
var last = Array.prototype.pop.call(arguments) //通过这种方式来实现调用Array的一些方法
console.log(last) //output: 2
}
fn(1,2)