8JS-call、apply、bind

166 阅读3分钟

一:call、apply、bind

call是一个方法,函数的方法,可以调用函数,可以改变函数的指向

function.call(thisArg, arg1, arg2, ...)

dog.eat.call(cat,'鱼','肉') dog.eat.apply(cat,['鱼','肉']) // 参数不同,带数组的 dog.eat.bind(cat,'鱼','肉')() // call会执行,bind不会执行,需要调用一下

call() 方法接受的是一个参数列表

apply() 方法接受的是一个包含多个参数的数组

bind()方法需要调用才会执行

call、apply、bind 应用

继承 子类继承父类的方法,可以实现多重继承

function Animal(name, price) {
  this.eat = function () {
      console.log('eating')
  }
}

function Bird(){
  fly()
}

function Cat(name, price) {
  Animal.call(this)
  Bird.call(this)
}

let c = new Cat
c.eat()
c.fly() // 多重继承

手写实现

思考,call从哪里来的

js函数其实都是Function对象,Function对象是构造函数,构造函数是有原型对象的Function.prototype,里面有call属性,同时也是一个方法,其实就是个函数而已,

模仿自己写的话,就业再原型对象里面新加newCall属性


function person(a,b,c){
    // console.log(this.name)
    // console.log(a,b,c)
    return {
        name:this.name,
        a:a,
        b:b,
        c:c
    }
}

let egg = {name :"xixixi"}

Function.prototype.newCall = function(obj){
    console.log('obj',obj) //{name :"xixixi"}
   
    var obj = obj||window // 如果第一个参数没有传,或者是null的话,就指向window,要不然会报错
    console.log(this) // 这里的this指向的是函数person,谁调用就指向谁
    obj.p = this  //Obj是第一个参数 所以这块改变this
    let newArguments = []; 
    for(let i=1;i<arguments.length;i++){  //i=1 从索引是1开始,因为索引=0的第一个参数是this
        // newArguments.push(arguments[i])  
        newArguments.push('arguments['+i+']')  // 用eval实现,需要字符串拼接
    }
    console.log('newArguments',newArguments)
    
    // obj.p(newArguments) 
    //这里数组相当于第一个参数了,所以 console.log(a,b,c)输出的值是  
    //['aaa', 'bbb', 'cccc']  undefined   undefined  
    //不要for循环,因为不确定argument的长度,会重复调用,
    //导致其他的比如console.log(this.name)重复输出
    // 想用eval的来实现obj.p(newArguments)   
    //要改成eval('obj.p('newArguments[0]','newArguments[1]','[newArguments[2]]')') 
    //所以push的时候要字符串拼接;  
    
    let res = eval('obj.p('+newArguments+')')
    delete obj.p //是因为不能改变原有的数据,所以加了之后 还得删掉
    return res
}
var xili = person.newCall(egg,'aaa','bbb','cccc')
console.log('xili--',xili)



这个是按照上面修改的apply,差别就是参数的处理,这个没有加返回值,加上之后就是上面那种,带return的



Function.prototype.newApply = function(obj,arr){
    var obj = obj||window 
    obj.p = this
    if(!arr){
        obj.p()
    } else {
        let newArguments = []; 
        for(let i=0;i<arr.length;i++){  //i=0 从索引是0开始,因为是全部元素
            newArguments.push('arr['+i+']')  // 用eval实现,需要字符串拼接
        }
        eval('obj.p('+newArguments+')')
    }
    delete obj.p
}
person.newApply(egg,['aaa','bbb','cccc'])



Bind


// bind
function person(a,b,c){
    console.log('name==',this.name)
    console.log('a====',a)
    console.log('b====',b)
    console.log('c====',c)
}
let egg = {name :"xixixi"}
Function.prototype.newBind = function(obj){
    let that = this
    arr = Array.prototype.slice.call(arguments,1) //切割第一个参数
    console.log('arr==',arr) // ['aaa', 'bbb', 'cccc']
    console.log('arguments111',arguments) //[ {name :"xixixi"},['aaa','bbb','cccc']]   是newBind第一个括号的参数

    return  function(){
        console.log('arguments222',arguments) // "ddd"  是newBind第二个括号的参数
        let arr2 = Array.prototype.slice.call(arguments) 
        console.log('arr2===',arr2)// ['ddd']
        arrSum = arr.concat(arr2)
        console.log('arrSum===',arrSum) // ['aaa', 'bbb', 'cccc', 'ddd']
        that.apply(obj, arrSum)
    }
}
person.newBind(egg,'aaa','bbb','cccc')('ddd')


Bind进阶 配合new使用

参考学习视频 技术蛋老师15:00 实在听不懂,等进步了,再回来


// bind 进阶 new
function person(a,b,c){
    console.log('name==',this.name)
    console.log('a====',a)
    console.log('b====',b)
    console.log('c====',c)
}
person.prototype.pppp = 'pppp'

let egg = {name :"xixixi"}
Function.prototype.newBind = function(obj){
    let that = this
    arr = Array.prototype.slice.call(arguments,1) //切割第一个参数

    return  function(){
        let arr2 = Array.prototype.slice.call(arguments) 
        arrSum = arr.concat(arr2)
        that.apply(obj, arrSum)
    }
}

let cici = person.newBind(egg,'aaa','bbb','cccc')

let ci = new cici('ci');

console.log('ci.pppp',ci.pppp)