阅读 352

call、apply、bind应用的介绍

call、apply、bind应用的介绍


call,apply,bind三者都是改变当前this的指向,但具体的使用方式,都存在着一定的区别,下面将从使用方式源码两个角度来简单的阐述一下之间的区别。

使用简介

call

call() 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。

注意:该方法的作用和 `apply()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。

语法:

fun.call(thisArg[, arg1[, arg2[, ...]]])
复制代码

参数:

  • thisArg

    • 在 fun 函数运行时指定的 this 值
    • 如果指定了 null 或者 undefined 则内部 this 指向 window
  • arg1, arg2, ...

    • 指定的参数列表

apply

apply() 方法调用一个函数, 其具有一个指定的 this 值,以及作为一个数组(或类似数组的对象)提供的参数。

注意:该方法的作用和 `call()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。

语法:

fun.apply(thisArg, [argsArray])
复制代码

参数:

  • thisArg
  • argsArray

apply()call() 非常相似,不同之处在于提供参数的方式。 apply() 使用参数数组而不是一组参数列表。例如:

fun.apply(this, ['eat', 'bananas'])
复制代码

**call 与apply的区别:** apply、call 的区别

对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:

var func = function(arg1, arg2) {
     
};
复制代码

就可以通过如下方式来调用:

func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
复制代码

其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。  

bind

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。 当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。 一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

语法:

fun.bind(thisArg[, arg1[, arg2[, ...]]])
复制代码

参数:

  • thisArg

    • 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
  • arg1, arg2, ...

    • 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

返回值:

返回由指定的this值和初始化参数改造的原函数拷贝。

示例:

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用'declare'方法
复制代码

小结

  • call 和 apply 特性一样

    • 都是用来调用函数,而且是立即调用
    • 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向
    • call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
    • apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
    • 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window
  • bind

    • 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
    • 它和 call、apply 最大的区别是:bind 不会调用
    • bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
        1. 在 bind 的同时,以参数列表的形式进行传递
        1. 在调用的时候,以参数列表的形式进行传递
      • 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
      • 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部

源码介绍

call源码

call的使用

  1. call可以改变前面函数中的this指向,指向call方法中的第一个参数
  2. 给方法传递参数,可以依次进行传值
  3. call的第1个参数是基本数据类型,this指向创建这个基本数据类型的构造器
  4. call的第1个参数可以是函数

简单call原理

没有参数传递的原理, x.cal(y) :在y 上挂载一个 x , 在y内部调用 x ,最终x内的x的指向y,

//定义一个调用者
function f(a, b, c) {
    return a + b + c
}

// 自定义call方法
Function.prototype.call = function (context) {
    // 将当前的调用者挂到第一个参数上
    context.fn = this;
    console.log(arguments);

    // 将传递的参数存放到一个数组里面
    let args = []
    for (let i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']')
    }

    // eval:可以执行里面的js代码
    let r = eval('context.fn(' + args + ')')

    // 删挂载上的那个函数
    delete context.fn;
    return r;
}

// 测试
let obj = { name: 'wangcai' };
let result = f.call(obj, 1, 2, 3)
console.log(result);
复制代码

call没有参数的问题,防止出错

当call没有参数的时候,程序可能会报错

function f(){
    .... 
}

Function.prototype.call = function(context){
    context = context ? Object(context) : window;
    ....
}

let result = f.call()
....

复制代码

注意:

特别提醒:运行环境是在浏览器 在node中不支持

apply源码

apply

apply与call的原理一直,就是传递参数的方式不一样,此处不过多的进行阐述

function f(a,b,c,d) {
    console.log("f.....");
    console.log(this);
    return a+b+c+d
}

Function.prototype.apply = function (context) {
    //将调用者挂载到参数上
    context.fn = this;

    // 获取到传递的参数
    let args =arguments[1]

    // eval:内的js代码会直接执行
    let r = eval("context.fn(" + args + ')')
    delete context.fn;
    return r;
}

// 测试
let obj = { name: 'wangcai' }
let res = f.apply(obj,[1,2,3,4])
console.log(res);
复制代码

bind源码

bind原理

  1. bind可以改变前面函数中的this指向,指向call方法中的第一个参数
  2. 这点与call一样,但是bind返回一个绑定后的函数
  3. bind需要自己进行调用,不会自己调用
  4. bind中绑定时传递的参数的优先级比调用时传递的参数的优先级高
  5. 优先使用绑定的参数,后续使用调用传递的参数

简单的bind原理

bind主要是借助于call或者bind进行数据的绑定,且进行对应的数据参数的传递


// 定义一个普通的函数
function f() {
    console.log("f..");
    console.log(this);
}

// 重写bind的这个方法
Function.prototype.bind = function (context) {
    let that = this;
    // 获取绑定时的参数
    let newArr = Array.prototype.slice.call(arguments, 1);

    return function () {

        // 获取调用时的参数 将arguments伪数组转成真实的数组
        let newArr2 = Array.prototype.slice.call(arguments)
        return that.apply(context, newArr.concat(newArr2))
    }

}

// 测试
let obj = { name: 'wangcai' }
let newF = f.bind(obj, 55, 66)

newF(1, 2, 3);

复制代码

注意:

当绑定的时候进行传值与新函数调用时传值同时有的时候,绑定的值优先与新函数的值, 整个的参数个数是:绑定参数+新函数参数

文章分类
前端
文章标签