每天一个小技巧js篇-深入理解call、apply、bind

440 阅读3分钟

前言

回想起之前的一些面试,几乎每次都会问到一个js中关于call、apply、bind的问题,比如…

  • 怎么利用call、apply来求一个数组中最大或者最小值
  • 如何利用call、apply来做继承
  • apply、call、bind的区别和主要应用场景

虽然网上有很多关于这方面的博客和文章,但还是决定写一篇自己对这方面知识的理解。

作用

首先问个问题,这三个函数的存在意义是什么?答案是改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向.

举个例子

function Person(name){
    this.name = name;
}

Person.prototype = {
    constructor:Person,
    showName:function(){
        console.log(`name`,this.name);
    }
}

var person = new Person('mengmiandaxia');
person.showName();// mengmiandaxia

上面的代码中person调用showName方法后会在浏览器的控制台输出mengmiandaxia 接下来

//游泳运动员
var swimmer = {
    name:'robert'
}

上面代码中有一个对象字面量,他没有所谓的showName方法,但是我还是想用?怎么办?(坑爹了,这好像在让巧媳妇去做无米之炊),不过没关系,call、apply、bind可以帮我们干这件事。

// call 
person.showName.call(swimmer);

// apply
person.showName.apply(swimmer);

// bind
person.showName.bind(swimmer)();

有木有很神奇,控制台输出了3次robert

从而可以得知,我们拿了别人的showName方法,并动态改变其上下文帮助自己输出了信息,说到底就是实现了复用

区别

上面看起来三个函数的作用差不多,干的事几乎是一样的,那为什么要存在3个家伙呢,留一个不就可以。所以其实他们干的事从本质上讲都是一样的动态的改变this上下文,但是多少还是有一些差别的.

call、apply与bind的差别

call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。

call、apply的区别

他们俩之间的差别在于参数的区别,call和aplly的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。

fn.call(obj,arg1,arg2,arg3...);
fn.apply(obj,[arg1,arg2,arg3...]);

应用

知道了怎么使用和他们之间的区别,接下来我们来了解一下通过call、apply、bind的常见应用场景。

  • 求数组中的最大和最小值
var array = [12,5,27,54,3,7,13,-5];
//求最大值
Math.max.apply(Math,array);// 54
Math.max.call(Math,12,5,27,54,3,7,13,-5);//54
//求最小值
Math.min.apply(Math,array);//-5
Math.min.call(Math,12,5,27,54,3,7,13,-5);//-5
  • 将伪数组转化为数组

js中的伪数组(例如通过document.getElementsByTagName获取的元素)具有length属性,并且可以通过0、1、2…下标来访问其中的元素,但是没有Array中的push、pop等方法。我们可以利用call、apply来将其转化为真正的数组这样便可以方便地使用数组方法了。

var books = {
    0:'css3',
    1:'react.js',
    2:'vue.js',
    length:3
}

上面就是一个普通的对象字面量,怎么把它变成一个数组呢?最简单的方法就是

var array = Array.prototype.slice.call(books);
console.log(array);//["css3", "react.js", "vue.js"]

上面array便是一个包含books元素的真正数组(注意数据结构必须是已数字为下标而且一定要有length属性

  • 数组追加
var arr1 = [1,2,3];
var arr2 = [4,5,6];

[].push.apply(arr1,arr2);

// arr1 [1, 2, 3, 4, 5, 6]
// arr2 [4,5,6]
  • 判断变量类型

对于对象型的数据类型,我们可以借助call来得知他的具体类型,例如数组

function isArray(obj){
    return Object.prototype.toString.call(obj) == '[object Array]';
}
isArray([]);// true
isArray('mengmiandaxia');// false
  • call和apply继承
var Person = function(name,age){
    this.name = name;
    this.age = age;
}

var Girl = function(name){
    Person.call(this,name);
}

var Boy = function(name,age){
    Person.apply(this,arguments);
}

var girl = new Girl('mengmian');
var boy = new Boy('daxia',20);

【p