javaScript 中的 call 、 apply 和 bind 的区别

其实博客对于我来说,更多的是记录我学习的心得,没有特别想说,我的博客写完了是想给谁看,或是通过这个提高知名度。我的想法很简单,就是自己学到了的东西,做个记录,方便以后的查看和复习。

今天就说说javaScript 中的 call 、 apply 和 bind 的区别,之前我自己也不是很懂。今天的目标就是把这个弄懂了。

先看看菜鸟教程中的解释:

在 JavaScript 中, 函数是对象。JavaScript 函数有它的属性和方法。call() 和 apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。

1
2
3
4
5
6
7
8
9
10
11
function myFunction(a, b) {
return a * b;
}
var myObject;
myObject = myFunction.call(myObject, 10, 2);
console.log(myObject); // 20
var myArr = [40,3];
var myObject1;
myObject1 = myFunction.apply(myObject1, myArr);
console.log(myObject1); //120

两个方法都使用了对象本身作为第一个参数。 两者的区别在于第二个参数: apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。
在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。
在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。

1
2
3
4
5
6
7
8
9
var a = "call";
function t(){
console.log(this.a);
}
t(); //控制台打印 call
var obj = {"a":1};
t.apply(obj); //控制台打印 1 // 传入obj的对象,this指向obj。
t.apply(); //控制台打印 call // 第一个参数的值是 null 或 undefined, 它将使用全局对象替代,this指向window。

实例测试

真是不看不知道,一看吓一跳,妙用无穷呀!!!。。。。
上代码,来体验体验这碗汤有多好喝。

1
2
3
4
5
6
7
8
9
10
11
function Person(){
}
Person.prototype = {
langulage : "汉语",
speak : function(){
console.log("我会说" + this.langulage);
}
}
var p = new Person();
p.speak(); // 我会说汉语

p 是个人,人具有会说话的能力,那么就打印出“我会说汉语”了。问题来了,这时候有一颗千年古树,她吸食日月之精华,日积月累成精啦,幻化成了一个大美女。重点来了,这时候她可以说话了。那要怎样才能行驶只有人才具有的这个功能呢。

1
2
3
4
5
6
//接着上面的代码,我们让这个大美女树精也能说话
var tree = new Object();
p.speak.call(tree); // 我会说undefined // 是让树精美女说话了,可是没告诉人家能说什么话呀。 这里可以说树精拥有了人说话的这个功能
//代码换一下,再瞧
var tree = {langulage : "English"};
p.speak.call(tree); // 我会说English // 看吧,树精厉害了,还是个外国美女

其实这么调用的时候就是改变了 this 的指向,此时 this 的指向就是 tree 这个对象了。
这么做的优点就是你不需要再重写一个 tree 的 speak 的方法了。

bind

上面对 call 和 apply 做了一些解释,其实两个作用是一样的,其区别也就在传参上的不同。而 bind 与这两个的区别在于:call 和 apply 是即时调用,而 bind 是函数调用。

1
2
3
//还是上面树精美女的例子,看用bind实现的效果
p.speak.bind(tree)(); //我会说English
//这样与 call 和 apply 做对比,看出来bind是返回的函数,还需要();调用才行。

再看一些常用的:

1. 伪数组的调用Array的sort方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
function sortA(){
console.log(arguments);
arguments.sort(); //arguments.sort is not a function
}
sortA(2,56,34,4,0,67,8);
//arguments 是个伪数组,并不具有Array的sort方法,换种方式看看:
function sortA(){
console.log(arguments);
var a = Array.prototype.sort.apply(arguments);
console.log(a); // [0, 2, 34, 4, 56, 67, 8, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
sortA(2,56,34,4,0,67,8);
2. 数组间的追加
1
2
3
4
5
var arr1 = [1,2,3,"aa","bb"];
var arr2 = [{"a":"aaa"},"vv",4,5,6];
Array.prototype.push.apply(arr1, arr2);
console.log(arr1); //[1, 2, 3, "aa", "bb", {…}, "vv", 4, 5, 6]
//注意一下,这里是向 arr1 的数组中追加 arr2 数组的内容。 arr2 的内容不变
3. 使用tostring()检测对象类

Object.prototype.toString():
"apply"
上面这段英文的意思就是,Object.prototype.toString() 方法在没有重写的情况下,返回的是”[object type]”这种字符串,因此可以根据这个来判断对象类。

1
2
3
4
5
6
7
8
9
10
11
12
var toString = Object.prototype.toString;
console.log(toString.call(new Object)); // [object Object]
console.log(toString.call(new Date)); // [object Date]
console.log(toString.call(new String)); // [object String]
console.log(toString.call(Math)); // [object Math]
console.log(toString.call(undefined)); // [object Undefined]
console.log(toString.call(null)); // [object Null]
var arr1 = [1,2,3,"aa","bb"];//判断arr1是否是一个数组
console.log(Object.prototype.toString.apply(arr1) === "[object Array]"); //true

看过一篇关于前端面试题的文章[!https://segmentfault.com/a/1190000000375138?page=1],感觉这里面的知识点挺不错的,故此处做一下记录。

  1. 定义一个log的方法替代console.log(),并且支持传参个数是不定的

    1
    2
    3
    4
    function log(){
    console.log.apply(console,arguments);
    }
    log('hello', 'world', "ffff"); // hello world ffff
  2. 在上面例子的基础上,在每个 log 信息前面加上“(app)”

    1
    2
    3
    4
    5
    6
    function log(){
    var args = Array.prototype.slice.call(arguments);
    args.unshift('(app)');
    console.log.apply(console, args);
    }
    log('hello', 'world', "ffff"); //(app) hello world ffff
个人觉得比较好的文章链接:
  1. https://segmentfault.com/a/1190000007342882
  2. https://www.cnblogs.com/libin-1/p/5823025.html
  3. https://segmentfault.com/a/1190000003963461