js深入系列六(call,apply,bind)

125 阅读2分钟

参考mdn和github文章

1. Function.prototype.call()

使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数

语法:

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

thisArg: 在 fun 函数运行时指定的 this 值,
         如果这个函数在非严格模式下运行,则指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中就是 window 对象),
         同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象
arg1, arg2, ...: 指定的参数列表

示例:

// 使用 call 方法调用父构造函数
function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name); // cheese

使用call方法调用匿名函数

var animals = [
    { species: 'Lion', name: 'King' },
    { species: 'Whale', name: 'Fail' }
  ];

  for (var i = 0; i < animals.length; i++) {
    (function(i) {
      this.print = function() {
        console.log('#' + i + ' ' + this.species
                    + ': ' + this.name);
      }
      this.print();
    }).call(animals[i], i);
  }
// call的this指向animals[i]
// 输出:
// #0 Lion: King
// #1 Whale: Fail

如果没有传递第一个参数,this 的值将会被绑定为全局对象

var sData = 'Wisen';

function display() {
  console.log('sData value is %s ', this.sData);
}

display.call();  // sData value is Wisen

在严格模式下,this 的值将会是 undefined

'use strict';

var sData = 'Wisen';

function display() {
  console.log('sData value is %s ', this.sData);
}

display.call(); // Cannot read the property of 'sData' of undefined

2. Function.prototype.apply()

作用与call方法一样,区别就是apply()方法接受的是一个参数数组

语法:

func.apply(thisArg, [argsArray])

argsArray: 可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数

代码示例:

用 apply 将数组添加到另一个数组

以下代码中,当我们将一个数组 push 进另一个数组时,整个数组被视为一个元素直接 push 进去

var array = ['a', 'b'];
var elements = [1, 2, 3];
array.push(elements);
console.log(array);  // ['a', 'b', [1, 2, 3]]

如果我们想要将数组 elemennts 中的元素分别 push 进数组 array 中,可用apply:

var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]

使用apply和内置函数, 找出数组中最大/小的数字

Math.max(1,2,3,4) // 4
Math.max([1,2,3,4]) // NaN

/* 找出数组中最大/小的数字 */
var numbers = [5, 6, 2, 3, 7];

var max = Math.max.apply(null, numbers); 
var min = Math.min.apply(null, numbers);

以下都可正常运行:

Math.max.apply(undefined,[1,2,3,4]) // 4
Math.max.apply(Math,[1,2,3,4]) // 4

3. Function.prototype.bind()

创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用

例子:

var x = 9;
var module = {
    x: 81,
    getX: function () {
        return this.x;
    }
};
console.log(module.getX()); // 81
var retrieveX = module.getX;
console.log(retrieveX()); // 9
var boundGetX = retrieveX.bind(module);
console.log(boundGetX()); // 81

call/apply的使用技巧

  1. 类数组调用数组方法:
// 在数组原型的方法上调用call,执行的this指向arrayLike
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }

// join语法: arr.join([separator])
// 相当于: arrayLike.join('&')
Array.prototype.join.call(arrayLike, '&'); // name&age&sex

// slice语法:arr.slice([begin[, end]])
Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] 
// slice可以做到类数组转数组

Array.prototype.map.call(arrayLike, function(item){
    return item.toUpperCase();
}); 
// ["NAME", "AGE", "SEX"]
  1. 类数组转数组
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
// 1. slice
Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"] 
// 2. splice
Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"] 
// 3. ES6 Array.from
Array.from(arrayLike); // ["name", "age", "sex"] 
// 4. apply
Array.prototype.concat.apply([], arrayLike)