一步让你区分理解call,apply,bind

594 阅读3分钟

作用: call,apply,bind 都是改变this指向的

语法:

 func.call(thisagr,param1,param1,...)
 func.apply(thisagr,[param1,param1,...])
 func.bind(thisagr,param1,param1,...)

1.call:

1.1 call第一个参数,就是要变成的this的对象

 var obj = { name: 'psg' };
     function fn(num1, num2) {
       console.log(num1 + num2);
       console.log("this指向:" + this, "num1:" + num1, "num2:" + num2)
   }
   fn(100, 200);//this指向window, num1=100, num2=200
   fn.call(100, 200);////this指向100, num1=200, num2=undefined
   fn.call(obj, 100, 200);//this指向obj,  num1=100, num2=200

1.2严格模式,非严格模式下的this指向

在非严格模式下:this为null,undefined时,this指向window 在严格模式下:传谁this就是谁,不传this就是undefined

  // 严格模式
  fn.call(); //this指向undefined
  fn.call(null); //this指向null
  fn.call(undefined); //this指向undefined

2.apply:

apply 于call 类似,只是不用于的语法

call传参数是逗号分割,一个一个传入:fn.call(obj,arg1,agr2)

apply传参数是用一个数组传:fn.apply(obj,[agr1,agr2])

var obj1 = { name: 'psg' };
    function fn1(num1, num2) {
      console.log(num1 + num2);
      console.log("this指向:" + this, "num1:" + num1, "num2:" + num2)
   }
   
    fn1.call(obj1, 100, 200);
    fn1.apply(obj1, [100, 200]);

3.bind:

bind 于call类似,语法一致,但是bind体现了js的预处理

bind 不会执行函数,会有一个返回值(返回值是函数的拷贝)

预处理:实现把fn的this改变成我们想要的结果,并且把对象的参数也准备了,要用的时候,直接执行就行了

var obj1 = { name: 'psg' };
    function fn1(num1, num2) {
      console.log(num1 + num2);
      console.log("this指向:" + this, "num1:" + num1, "num2:" + num2)
    }
    fn1.call(obj1, 100, 200);
    fn1.bind(obj1, 100, 200);
    
    //bind只是改变了fn中的this为obj,并且给fn传递了两个参数值,但是此时并没有给fn这个函数执行。
   // 但是,执行bind会有一个返回值,这个返回值myFn就是我们把fn的this改变后的那个结果!!!


    var myFn = fn1.bind(obj1, 100, 200);
    myFn();  //执行函数

3.1手动实现bind

4.小结区别:

call于apply区别:

语法不同,传给函数的参数写法不同

call于bind区别:

1.执行:

call,apply改变了this执行,立马执行函数

bind 返回了this指向后的函数,没有执行函数

 

5.手动模拟实现call:

主要思路如上,不一样的是要通过arguments取出除第一位之外的所有参数放到一个数组里,然后通过展开运算符给要执行的函数或方法传参

1.改变this指向,给传递过来的对象添加属性,把this指向这个属性,然后指向方法,删除属性

2.用arguments接受传递过来的参数

Function.prototype.myCall = function (con) {
 con = new Object(con) || window || global;//1.没有参数时,指向window
   con.fun = this;   //2.给目标对象新建一个属性,绑定这个函数
   let arr =[];  //3.新建一个空数组
   for (let i = 1; i < arguments.length; i++) {
        arr.push(arguments[i]);
   }
   con.fun(...arr);  //4.运行一下
   delete con.fun;   //5.删除目标对象上的fun属性,不删除会越来越多
 }
  var obj = { name: 12 }
  function fn(num1, num2) {
    console.log("this:" + this);
    console.log("num1:" + num1);
    console.log("num2:" + num2);
  }
  fn.myCall(100, 200);
  fn.myCall(obj, 100, 200);

 

方法二:eval 将原始函数转换为字符串,再将其中的this替换为目标对象

1.arguments代指函数接收的所有参数,它是一个类数组,不能用Array的方法

第一位是this指向,后面的才是参数

  1. eavl() 可以接受一个字符串str作为参数,并把这个参数作为脚本代码来执行
  var name = 'cao1';
  var obj = {name: 'cao2'};
  Function.prototype.call1_ = function (obj) {
      var arr = []
      for (var i = 1, len = arguments.length; i < len; i++) {
        arr.push("arguments[" + i + "]")
      }
      obj.fn = this;
      eval("obj.fn(" + arr + ")");//2.执行方法  eval(string)会做运算  eval("var a=1");
      delete obj.fn;
    }
    function fn2(a, b, c) {
      console.log(a + b + c + this.name);
    };
    fn2.call1_(obj, 1, 2, 3);
    fn2.call1_(obj, "我的", "名字", "是");
    // 如果是args.push(arguments[i])报错,因为传递的是字符串这一步我们提前将字符串进行了解析 等于 eval("obj.fn(我的,名字,是)")

6.开发使用

6.1 改变this指向

6.2 数据类型检测

function type(obj) {
return  Object.prototype.toString.call(obj)[1];
};
 
type([123]);//Array
type('123');//String
type(123);//Number
type(null);//Null
type(undefined);//Undefined

6.2数组取最大值,最小值

var arr = [11, 1, 0, 2, 3, 5];
var max1 = Math.max.call(null, ...arr);
var max2 = Math.max.apply(null, arr);


var min1 = Math.min.call(null, ...arr);
var min2 = Math.min.apply(null, arr);

6.3函数arguments类数组操作

var fn = function () {
    var arr = Array.prototype.slice.call(arguments);
    console.log(arr); //[1, 2, 3, 4]
};
fn(1, 2, 3, 4);