前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JavaScript中call,apply,bind方法的使用及原理

JavaScript中call,apply,bind方法的使用及原理

原创
作者头像
伯爵
修改2019-10-18 17:25:10
1.1K0
修改2019-10-18 17:25:10
举报
文章被收录于专栏:前端小菜鸟前端小菜鸟

在JavaScript里,call(),apply(),bind()都是Function内置的三个方法, 它们的作用都是显示的绑定this的指向,三个方法的第一个参数都是this指向的对象,也就是函数在运行时执行的上下文。

当我们定义一个新的对象,需要使用其他对象的方法的时候,我们不需要重新开发重复的方法逻辑,借助apply,apply,bind三个方法可以实现对这些的方法的调用。

我们定义三者的概念:

apply:调用一个对象(obj)的方法(func),并使用新的对象(thisArg)代替该对象,参数是数组

代码语言:txt
复制
obj.func.apply(thisArg, [argsArray])

call:调用一个对象(obj)的方法(func),并使用新的对象(thisArg)代替该对象,参数是列表

代码语言:txt
复制
obj.func.call(thisArg, arg1, arg2, ...)

bind:bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用,第一个thisArg在setTimeout中创建一个函数时传递的原始值都会转化成object,如果bind参数列表为空,thisArg赋值当前当前运行环境this

代码语言:txt
复制
function.bind(thisArg[,arg1[,arg2[, ...]]])

特点:

  • apply,call,bind三个方法第一个参数都是函数在调用时this指向的对象,也就是运行时的上下文(this显示绑定的原理)
  • apply,call第一个参数为空,null,undefined,this指向的是window
  • apply,call两个方法只是参数形式有所不同,apply参数是一个数组,call则是参数列表版本
  • apply,call 则是立即调用,bind 是则返回对应函数

常见的一些应用:

数组合并

我们创建arr和other两个数组,当我们需要合并两个数组的时候,可以使用concat方法进行操作,但是concat需要创建新的数组对象,我们可以借助apply方法不需要创建新的对象,不需要遍历数组,也可以实现需求:

代码语言:txt
复制
let arr = [2,3,4,5,6];
let other = [10,89];
arr.push.apply(arr, other); // or Array.prototype.apply(arr, other)

console.log(arr); // [2, 3, 4, 5, 6, 10, 89]

我们借助了apply方法,push方法在调用的时候,this指向的是arr对象,这里的参数列表other,我们将other数组合并到arr数组对象上。

下面的是两种比较常见的写法,其实是一样的

继承
代码语言:txt
复制
function Animal(name) {
    this.name = name;
    this.run = function() {
        console.log(`${this.name} is running!`)
    }
}

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

let tom = new Cat("TOM");
tom.run(); // TOM is running!
创建绑定函数
代码语言:txt
复制
this.name = 'global';
let cacheFun = {
    name : 'cache',
    getCacheName : function() {
        console.log("Name: ",this.name)
    }
}
cacheFun.getCacheName(); // Name is cache

let cacheName = cacheFun.getCacheName;
cacheName(); // Name:  global

在创建绑定函数的实例中,我们分析一下一部分调用过程:

  • 我们cacheFun对象作为缓存name字段的对象
  • 我们声明变量cacheName存储getCacheName方法的引用
  • 当我们调用cacheName方法时this绑定的是window对象,所以this.name获取的是global

我们需要借助bind方法,在函数调用的时候,绑定this的执行到cacheFun对象上:

代码语言:txt
复制
let cacheNameByBind = cacheName.bind(cacheFun);
cacheNameByBind();  // Name is cache
实现原理
  • apply
代码语言:txt
复制
Function.prototype.applyFun = function(content) {
    content = content || window;
    content.fun = this;
    var argsArr = arguments[1];

    if(!argsArr) {
       return content.fun();
    }
    if(!Array.isArray(argsArr)) {
        throw Error("参数列表必须是数组的格式!")
    }else {
        var result ;
        var len = argsArr.length;
        for(var i=0;i < len; i++) {
            result = content.fun(argsArr[i]);
        }
        delete content.fun;
        return result;
    }
}

// 测试
var arr = [1,5];
var other = [34,5,5];
Array.prototype.push.applyFun(arr, other);
console.log(arr) //  [1, 5, 34, 5, 5]
  • call
代码语言:txt
复制
Function.prototype.callFun = function(content) {
    content = content || window;
    content.fun = this;

    var args = [];

   for(var i=1;i<arguments.length;i++) {
        args.push(arguments[i]);
    }
    eval(`content.fun(${args})`); //字符串拼接
    delete content.fun;
}

let arr = [1,2,3]
Array.prototype.push.callFun(arr, 4,5,6,6);
console.log(arr); // [1,2,3,4,5,6,6]
  • bind

基础版

代码语言:txt
复制
Function.prototype.bindFun = function(content) {
  let that = this;
  return function() {
    return that.apply(content)
  }
}

let Person = {
  getName : function() {
    console.log(`Name is ${this.name}`)
  }
}
let Liming = {
  name: 'ming'
}


let getName = Person.getName;
let getNmaeByming = getName.bindFun(Liming);

getNmaeByming(); // Name is ming

最终版本,处理通过new创建绑定是的this绑定,将that对象传入闭包缓存

代码语言:txt
复制
Function.prototype.bindFun = function(content) {
  if(typeof this !== 'function') {
    return;
  }
  var that = this;
  var args = Array.prototype.slice.call(arguments, 1);
  var _bindFun = function() {
    var _content = this instanceof that ? this: content;
    var _args = [].slice.call(arguments, 0);
    that.apply(_content , args.concat(_args));
  }
  var BindFun = function() {};

  if(this.prototype) {
    BindFun.prototype = this.prototype;  
    _bindFun.prototype = new BindFun();
  }
  return _bindFun;
}

// 测试
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() { 
  return this.x + ',' + this.y; 
};

var emptyObj = {};
var YAxisPoint = Point.bindFun(emptyObj, 0);

var axisPoint = new YAxisPoint(5);
var str = axisPoint.toString(); 

console.log(str) // '0,5'
参考

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数组合并
  • 继承
  • 创建绑定函数
  • 实现原理
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档