js call,apply和bind区别和使用场景

在JavaScript中,所有的函数再被调用的时候都会默认传入两个参数,一个是this,还有一个是arguments。在默认情况下this都是指当前的调用函数的对象。但是有时候我们需要改变this的指向,也就是说使函数可以被其他对象来调用,那么我们应该怎样做呢?这时候我们就可以使用call,apply和bind方法了。
this指向 = 谁调用,指向谁(这是错误的!!!)
this永远指向最后一个调用它的那个对象(正解)
如何解决this指向问题?
1.使用ES6中箭头函数
2.函数内部使用_this = this
3.使用apply,call,bind方法
4.new实例化一个对象

1 首先搞清楚call,apply和bind方法的来历

在js中所有的函数都是Function的实例,而且对于Function来说,它的原型即Function.prototype中含有很多东西,其中call,apply和bind方法就是Function原型中的方法,所以根据原型的规则,所有的函数都可以使用原型中属性和方法,所以来说,对于所有的函数都可以使用call,apply和bind方法。
简单一句话:call,apply和bind都是Function原型中的方法,而所有的函数都是Function的实例。

2 抛出一个概念,就是call,apply和bind方法到底有什么作用?

我认为它们的作用可以用一句话来描述:就是改变this的指向。对于这句话的解释,我们可以结合代码来理解。

3 结合代码理解改变this的指向的含义 

	<script type="text/javascript">
		function show(sex){
			console.log("普通函数"+sex);
		}
		var person={
			name:"aa",
			age:14
		};
		show.call(person,"男");
	</script>

在 上面的代码块中,我们可以看到person对象并没有show方法,但是我们可以通过call方法来实现person对象来调用show方法。所以这种情况我认为就是改变了this的指向。
同样的,apply和bind方法也可以实现上述的功能,那么它们三个有什么区别呢?

4 call,apply和bind的区别

​​它们在功能上是没有区别的,都是改变this的指向,它们的区别主要是在于方法的实现形式和参数传递上的不同
①:函数.call(对象,arg1,arg2....)
②:函数.apply(对象,[arg1,arg2,...])
③:var ss=函数.bind(对象,arg1,arg2,....)
我们通过代码来更加明显的区别一下: 

<script>
 let obj = {
    name : "小明",
    func1: function () {
        console.log(this.name,'-')
    },
    func2: function () {
        setTimeout(  function () {
            this.func1()
        }.apply(obj),1000);
    },
    func3: function () {
        setTimeout(  function () {
            console.log(this,'---')
        } )
    }
};
obj.func1()   
obj.func2()   
obj.func3()   
</script>

apply() 方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或者类似数组的对象)提供的参数,
fun.apply(thisArg, [argsArray])
thisArg:在fun函数运行时指定的this值。指定this的值并不一定是函数执行时真正的this值,如果是原始值的this会指向该原始值的自动包装对象。
argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun函数。参数为null或者undefined,则表示不需要传入任何参数。

<script>
let obj2 = {
    name : "小红",
    func1: function () {
        console.log(this.name)
    },
    func2: function () {
        setTimeout(  function () {
            this.func1()
        }.call(obj2),1000);
    }
};
obj2.func2()            // 小红
</script>

call() 调用一个函数,其具有一个指定的this值,以及若干个参数列表,
fun.call(thisArg, arg1, arg2, …)
thisArg:在fun函数运行时指定的this值。指定this的值并不一定是函数执行时真正的this值,如果是原始值的this会指向该原始值的自动包装对象。
arg1, arg2, …:若干个参数列表
let obj3 = {
    name : "小猪",
    func1: function () {
        console.log(this.name)
    },
    func2: function () {
        setTimeout(  function () {
            this.func1()
        }.bind(obj3)(),1000);
    }
};
obj3.func2()   // 小猪

bind() 创建一个新的函数,当被调用时,将其this的关键字设置为提供的值,在调用新函数时,在任何提供一个给定的参数序列。
bind创建了一个新函数,必须手动去调用。

   <script type="text/javascript">
    function show(sex) {
      console.log("普通函数" + sex);
      console.log(this, '--');
    }
    var person = {
      name: "aa",
      age: 14
    };
    show.call(person, "man");
    show.apply(person, ["woman"]);
    //对于bind来说,用法更加的灵活
    var ss = show.bind(person, "不明");
    ss();//手动去调用
	</script>
//通过观察上面的代码,很明显的就可以得出它们三者的区别,
// 仅仅是函数传递的不同以及bind方法可以更加的方便的使用

  • apply和call基本类似,他们的区别只是传入的参数不同。
  • apply传入的参数是包含多个参数的数组
  • call传入的参数是若干个参数列表
  • bind方法会创建一个新的函数,当被调用的时候,将其this关键字设置为提供的值,我们必须手动去调用

5 call()和apply()的区别:

相同点:都是调用一个对象的一个方法,用另一个对象替换当前对象(功能相同)
例如: B.call(A, args1,args2);即A对象调用B对象的方法
F.apply(G, arguments);即G对象应用F对象的方法

不同点:参数书写方式不同
call()的第一个参数是this要指向的对象,后面传入的是参数列表,参数可以是任意类型,当第一个参数为null、undefined的时候,默认指向window;
apply():第一个参数是this要指向的对象,第二个参数是数组

//例如:
var obj = {}//定义一个空的对象
function f(x,y){
console.log(x,y)
console.log(this) //this是指obj
}
f.apply(obj,[1,2]) //后面的值需要用[]括起来
f.call(obj,1,2) //直接写

6 call()和bind()的区别:

相同点:都是用来改变this的指向
不同点:call()改过this的指向后,会再执行函数,bind()改过this后,不执行函数,会返回一个绑定新this的函数 

//例如:
function f(){
console.log("看我怎么被调用");
console.log(this) //指向this
}
var obj = {name:'g'};
f.call(obj) //直接调用函数
var g = f.bind(obj); //bind()不能调用函数
g();  //此时才调用函数

 

7 call()的应用

7.1 利用call()判断数据类型

在判断数据类形式使用typeof,一般不是太准确的,我们可以使用Object.prototype.toString.call()方法来判断一个数据的数据类型

console.log(Object.prototype.toString.call("qq"))            // [Object String] 返回值都是字符串类型
console.log(Object.prototype.toString.call(12))              // [object Number]
console.log(Object.prototype.toString.call(false))           // [object Boolean]
console.log(Object.prototype.toString.call(undefined))       // [object Undefined]
console.log(Object.prototype.toString.call(null))            // [object Null]
console.log(Object.prototype.toString.call(function(){}))    // [object Function]
console.log(Object.prototype.toString.call([]))              // [object Array]
console.log(Object.prototype.toString.call({}))              // [object Object]

//
function a(){   
  console.log(this);   //输出函数a中的this对象
}       

function b(){}       

var c={name:"call"};    //定义对象c  

a.call();   //window
a.call(null);   //window
a.call(undefined);   //window
a.call(1);   //Number
a.call('');   //String
a.call(true);   //Boolean
a.call(b);   //function b(){}
a.call(c);   //Object
//
var obj={aa:99};
var a=obj.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf");//true

 我们可以利用上面的输出的内容进行封装一个函数,以达到判断输入数据的基本类型

  function getType(a){
       var obj = Object.prototype.toString.call(a); //区分对象类型  确定当前的数据的类型
       var sub = obj.substr(8); 
       // stringObject.substr(start,length)  start 要抽取的子符串的起始下标,
       // length 截取的长度,如果不写则表示从start开始截取到最后 ,stringObject表示某一字符串
      var len = sub.length;
      var sub = sub.substr(0,len-1)
      var rs =  sub.toLowerCase(sub) //转换成小写
      return rs ;
    }
     console.log(getType("a")); //string
7.2利用call()翻转字符串
//思路:将字符串转化为数组,借用数组中的reverse,将字符串翻转过来
 var str = "abcdefg";
 console.log(Array.prototype.reverse.call(str)); //此时会报错误,即引用类型错误,就是说只有数组才能使用reverse这个方法;(错误写法)
    //方法一:这种方法内有使用call()
 var arr =  Array.from(str).reverse().join("") //将字符串转化为数组,在进行翻转,然后在进行拼接
 console.log(arr) //gfedcba
 console.log(typeof arr) //string
     //方法二:
var rs = Array.prototype.reverse.call(str.split("")).join(""); 
    //splice(start,length)方法用于把一个字符串分割成字符串数组,start 表示从指定的地方分割字符串    length表示分割的长度。
    //返回一个一个字符串数组 如果把空字符串 ("") 用为参数那么字符串中的每个字符之间都会被分割
console.log(rs); //gfedcba
console.log(typeof arr) //string
function eat(x,y){   
  console.log(x+y);   
}   
function drink(x,y){   
  console.log(x-y);   
}   
eat.call(drink,3,2);

//输出:5
//这个例子中的意思就是用 eat 来替换 drink,eat.call(drink,3,2) == eat(3,2) ,所以运行结果为:console.log(5);
//注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。
//继承
function Animal(name){   
  this.name=name;   
  this.showName=function(){   
    console.log(this.name);   
  }   
}   
function Dog(name){   
  Animal.call(this,name);   
}   
var dog=new Dog("Crazy dog");   
dog.showName();

//输出:Crazy dog
//Animal.call(this) 的意思就是使用 Animal对象代替this(Dog)对象,那么Dog就能直接调用Animal的所有属性和方法。
function Animal(){   
  this.name="animal";   
  this.showName=function(){   
    console.log(this.name);   
  }   
}   
function Dog(){   
  this.name="dog";   
}   
var animal=new Animal();   
var dog=new Dog();       

animal.showName.call(dog);

//输出:dog
//在上面的代码中,我们可以看到Dog里并没有showName方法,那为什么(this.name)的值是dog呢?

//关键就在于最后一段代码(animal.showName.call(dog)),意思是把animal的方法放到dog上执行,也可以说,把animal 的showName()方法放到 dog上来执行,所以this.name 应该是 dog。
7.3 apply()的应用

 利用apply()求最大值

var arr =[2,6,8,3,4,9,7,23,56,889]; 
console.log(Math.max.apply(arr,arr)) //第一个arr表示让arr借用max这个方法,第二个arr表示传给max的数据

//apply()所执行的操作:1.执行Math.max(1,2,3,5,4) 2.把内部的this改成arr
//注意:Math是一个对象,并不是构造器

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: bind、call和apply都是用来改变函数中this的指向的方法。 bind方法会返回一个新的函数,这个新函数的this指向被绑定的对象,但不会立即执行。 call和apply方法都是立即执行函数,并且都会改变函数中this的指向。它们的区别在于传入参数的方式不同,call方法是将参数一个一个传入,而apply方法是将参数放在一个数组中传入。 举个例子: ``` var obj = { name: 'Tom' }; function sayHello(age) { console.log('Hello, my name is ' + this.name + ', I am ' + age + ' years old.'); } var newSayHello = sayHello.bind(obj); newSayHello(18); // Hello, my name is Tom, I am 18 years old. sayHello.call(obj, 18); // Hello, my name is Tom, I am 18 years old. sayHello.apply(obj, [18]); // Hello, my name is Tom, I am 18 years old. ``` 以上代码中,我们定义了一个对象obj和一个函数sayHello。使用bind方法将sayHello函数中的this指向obj,并返回一个新的函数newSayHello。使用call和apply方法直接调用sayHello函数,并将this指向obj,同时传入参数18。 ### 回答2: bind和call apply都是JavaScript中用来改变函数执行上下文的方法,但它们有些许区别bind方法会创建一个新函数,其this值会被绑定到指定的对象上。它接收一个参数,即想要将函数绑定到的对象。如果绑定的函数参数存在,则会将绑定函数的参数传递给原始函数,同时返回绑定后的新函数。如果绑定的函数没有参数,则直接返回一个绑定后的新函数。bind方法并不会立即调用函数,而是返回一个函数引用,需要手动调用函数才会执行。 call和apply则是直接调用函数,并且可以指定函数执行上下文。它们最主要的区别在于传递参数的方式不同。call方法接收的参数是一个参数列表,即需要传递给函数的参数直接列出来一个一个传递。apply方法接收的参数是一个数组,即需要传递给函数的参数通过数组的方式传递。另外,如果函数没有参数,那么使用call和apply的方式没有区别。 因此,如果我们想要在不同的上下文中调用函数,可以使用bind方法,在其他时候可以使用call和apply方法。同时,由于bind方法会创建新的函数引用,可能会占用一些内存空间,因此可考虑使用call和apply方法。 ### 回答3: bind、call和applyJavaScript中三个非常重要的函数方法,它们都用于改变函数的this指向。虽然它们的作用类似,但是它们具体的使用方式有很大的不同。 bind()、call()和apply()都能够把一个函数的this指向指定为第一个参数中的对象,并且都可以传递多个参数给函数。具体差别如下: 1. bind():bind()方法常常用于多次调用同一函数,且该函数的this值不变。bind()方法会返回一个新函数,并且它的this值总是被指定为第一个参数。bind()方法的传参方式,与原函数相同,在调用时将参数以一个个单独的参数传入。 2. call():将具体的值,作为指定的this,传入并执行该函数。call()方法会在调用时执行指定函数,并且呈现出函数的this值为第一个参数的对象。call()方法的传参方式,将参数以一个个单独的参数传入。 3. apply():apply()的作用与call()类似,主要区别在于参数的传递方式,apply()会将参数直接传入数组。就是说,在调用apply()方法时,需要将需要绑定到this的对象作为第一个参数传入,第二个参数是一个数组、列表或类似数组的对象,该数组中的所有元素都将作为单独的参数传递给被调用的函数。 总之,bind()、call()和apply()都是用于改变函数的this指向,它们之间的区别主要在于传递参数的方式不同。在实际开发中,我们需要根据场景的需要选择不同的方法来使用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值