1,
首先call()、apply()、bind() 都是用来重定义 this 这个对象的
例子1:
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <meta http-equiv="X-UA-Compatible" content="ie=edge">
? ? <title>call</title>
</head>
<body>
</body>
<script>
? ? var name="盖伦";
? ? var sex="男";
? ? var character={
? ? ? ? name: "艾希",
? ? ? ? character_sex: "女",
? ? ? ? say:function(){
? ? ? ? ? ? console.log(this.name + " "+ this.sex)
? ? ? ? }
? ? }
? ? console.log(character.name)
? ? character.say();
</script>
</html>
结果:
如果把say里面的console.log改以下
console.log(this.name + " "+ this.character_sex)
结果:
这样的结果是因为this的指向问题,say里面this的指向是character这个对象,然后character.里没有定义sex。call和apply()、bind()都可改变this指向
列子:
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <meta http-equiv="X-UA-Compatible" content="ie=edge">
? ? <title>call</title>
</head>
<body>
</body>
<script>
? ? var gailun={
? ? ? ? name:"盖伦",
? ? ? ? sex:"男"
? ? }
? ? var character={
? ? ? ? name: "艾希",
? ? ? ? character_sex: "女",
? ? ? ? say:function(){
? ? ? ? ? ? console.log(this.name + " "+ this.sex)
? ? ? ? }
? ? }
? ? character.say.call(gailun)
</script>
</html>
结果:
通过call()就将原来指向character的this变成指向gailun,bind(),apply(),通用会改变this指向。
2,call()和apply()的区别
call()方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组。
列子:
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <meta http-equiv="X-UA-Compatible" content="ie=edge">
? ? <title>call</title>
</head>
<body>
</body>
<script>
? ? var gailun={
? ? ? ? name:"盖伦",
? ? ? ? sex:"男"
? ? }
? ? var timo={
? ? ? ? name:"提莫",
? ? ? ? sex:"未知"
? ? }
? ? var character={
? ? ? ? name: "艾希",
? ? ? ? character_sex: "女",
? ? ? ? say:function(from){
? ? ? ? ? ? console.log(this.name + " "+ this.sex+" "+"来自"+from)
? ? ? ? }
? ? }
? ? character.say.call(gailun,"德玛西亚");
? ? character.say.apply(timo,["班德尔城"]);
</script>
</html>
结果:
假如apply()参数不是array类型,会报以下错误。
TypeError: second argument to Function.prototype.apply must be an array
假如:character.say.apply(null,["班德尔城"]);这样写,那么this指向了window。
我们如果在window下这样定义:
?? var name="cc"
? ? var sex ="男"
那么结果应该会这样:
cc 男 来自班德尔城
3,bind()
bind()方法和call,apply不一样的地方在于它返回的是一个函数
列子:
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <meta http-equiv="X-UA-Compatible" content="ie=edge">
? ? <title>call</title>
</head>
<body>
</body>
<script>
? ? var name="cc"
? ? var sex ="男"
? ? var gailun={
? ? ? ? name:"盖伦",
? ? ? ? sex:"男"
? ? }
? ? var timo={
? ? ? ? name:"提莫",
? ? ? ? sex:"未知"
? ? }
? ? var character={
? ? ? ? name: "艾希",
? ? ? ? sex: "女",
? ? ? ? say:function(from){
? ? ? ? ? ? console.log(this.name + " "+ this.sex+" "+"来自"+from)
? ? ? ? }
? ? }
? ? character.say.call(gailun,"德玛西亚");
? ? character.say.apply(timo,["班德尔城"]);
? ? console.log(character.say.bind(gailun,"德玛西亚"))
</script>
</html>
结果:
理解call(),apply(),bind()并不困难,但是面试中许多面试官都会问,“能自己模拟实现这三个函数吗?”,生活所迫那么自己实现一下。
参考:https://github.com/mqyqingfeng/Blog/issues/11
实践写一个call(),其他参考上面的地址
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <meta http-equiv="X-UA-Compatible" content="ie=edge">
? ? <title>myCall</title>
</head>
<body>
</body>
<script>
? ? var a=1;
? var bar={
? ? ? a:1
? }
? var bar2={
? ? ? a:2
? }
function testCall(argA,argB){
? return {
? ? ? ? argA:argA,
? ? ? ? argB:argB,
? ? ? ? value:this.a
? }
}
testCall.call(bar,"c","cheng")
Function.prototype.myCall=function(context){
? ? var context = context || window
? ? context.fn=this
? ? let args=[]
? ? for(let i =1;i<arguments.length;i++){
? ? ? ? args.push("arguments["+i+']')
? ? }
? ? var res = eval('context.fn('+args+')')
? ? delete context.fn
? ? return res
}
console.log(testCall.myCall(bar2,"c","cheng"))
console.log(testCall.myCall(null))
</script>
</html>
理解:
首先改变this指向
Function.prototype.call2=function(context) {
????? context.fn=this;context.fn();
????? delete context.fn;
}
如果context为空,则需要把context改成window对象
所以改进一下
Function.prototype.call2=function(context) {
????? var context = context || window;
???? context.fn=this;context.fn();
????? delete context.fn;
}
然后处理参数,
?? let args=[]
? ? for(let i =1;i<arguments.length;i++){
? ? ? ? args.push("arguments["+i+']')
? ? }
???? var res = eval('context.fn('+args+')')
到此完成了 call ()的模拟实现