call,apply,bind

223 阅读2分钟

每个function都是Function的实例,都可以调用其原型上的三个方法

  • Funtion.prototype(apply,call,bind)

call

call方法执行的作用是:把fn执行,并把fn中的this指向变为我们传递的第一个实参obj

    function fn(x,y){
        console.log(this.name)
    }
    let obj = {
        name:"obj"
    }
    fn.call(obj)  
    //  fn首先基于__proto__找到Function.prototype上的call方法,执行call方法
        - 传递的实参obj
        - call方法中的this -> fn

        - fn.call(obj,10,20,30,...) call第二个第三个参数传给fn作为参数
        - fn.call(10,20,30) this->10 this指向10 其余成为实参
        - fn.call() this->window 指向window

apply

    与call只有一个区别:传递给执行函数的实参方式不一样,参数越多,call性能越好
    fn.call(obj,10,20,30,...)
    fn.apply(obj,[10,20,30,...])
    最后结果一样
    let arr = [10,20]
    fn.apply(obj,arr)
    fn.apply(obj,...arr)

call、apply 实际应用

1.  **数组排序 取最大值**
    let arr = [1,7,89,45,67,3,2]
    
    arr.sort((a,b)=>b-a) arr[0]
    
    console.log(Math.max(...arr))
    
    console.log(Math.max.apply(Math,arr))
    
    let str = 'Math.max('+ arr + ')'
    console.log(eval(str))
2.  **鸭子类型(把长得像鸭子的,称它为鸭子,最主要的是让其具有鸭子的特点)**
    function fn(){
        console.log(arguments) 
        // 类数组对象,不能直接使用数组的方 arguments.__proto = Object.prototype
        // 想让类数组拥有数组的方法(Array.prototype)
        
        // 方案1:把类数组变成数组
          let arr = [...arguments]
           
           let arr = Array.from(arguments)
           
           ler arr=[]
           for(let i = 0 ; i < arguments.length;i++){
               arr.push(arguments[i])
           }
           
           // Array.prototype.slice = function slice(){ //数组浅克隆
           //    for(let i =0;i < this.length ; i++){
           //        arr.push(this[i])
           //    }
           //   return arr
           //}
           //上面的方法等同于 let arr =Array.prototype.slice.call(arguments,0)
                           let arr =[].slice.call(arguments,0)
           //参考下方图片 大多数数组方法都可以参照此方式给类数组
           //[].prototype.foreach.call(arguments,item=>{
           //  console.log(item)
           // })
           
           方案2:直接借用
           return [].reduce.call(arguments,(total,item)=>total+item)
           
           方案3:改变原型指向
           arguments.__proto__ = Array.prototype
           return arguments.reduce((total,item)=>total+item)
           
           console.log(arr)
           
           let obj = {
               a:1,
               b:2,
               4:3,
               length:2,
               push:Array.prototype.push
           }
           obj.push(1)
    }
    fn(10,20,30,40)

image.png

bind

** call/apply都是立即把函数执行「改变this和传入参数」,
** bind没有把函数立即执行,而是预先把后期要改变的this和参数存储起来「柯理化」
** 
    function fn(x,y,ev){
        console.log(this,x,y,ev)
        return x+y
    }
    let obj = {
        name:"obj"
    }
   //执行bind会返回一个新函数「预先把fn/obj/10/20存储起来」,fn没有立即执行
   let proxy = fn.bind(obj,10,20)
   //执行proxy,内部会帮我们执行fn,「this和obj都已经处理过了」,
   proxy()
   
   document.onclick = fn;//点击文档才执行fn this->document x->MouseEvent事件对象
   y->undefined
   document.onclick.call(obj,10,20)// 立即执行 达不到onclick的效果
   document.onclick = function(ev){  // 用call也可以实现,过于繁琐
       // this->document
       fn.call(obj,10,20,ev)
   }
   document.onclick = fn.bind(obj,10,20) // 可以实现 简单