◆

JavaScript apply、call 和 bind 用法详解

2023-02-02 01:370

apply

apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

语法:func.apply(thisArg, [argsArray])

  • thisArg:在 func 函数运行时使用的 this 值
  • argsArray: 一个数组或者类数组对象

返回值:调用有指定 this 值和参数的函数的结果。

实用方法

通过 apply,push 整个数组元素

直接使用 push 会把整个数组 push 成一个元素,用 apply 可以避免这种情况

var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
console.log(max); // 7
console.log(min); // 2

call

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

  • 该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

bind

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

  • bind 与 apply 和 call 都是用来绑定 this, bind 与 其他两个的区别是,bind 是创建一个函数,但不会直接调用,其他两个绑定 this 指向后会直接调用函数。

返回值 返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

回调函数中使用 bind

var obj = {
  name: 'fx',
};

setTimeout(
  function () {
    console.log(this.name); // fx
  }.bind(obj),
  1000,
);

var name = 'fnn';

不用 bind 绑定

var obj = {
  name: 'fx',
};

setTimeout(function () {
  console.log(this.name); // fnn
}, 1000);

var name = 'fnn';

模拟实现 call 方法

  • 思路是在 context(在函数运行时使用的 this 值)对象上增加一个函数属性,其属性值就是调用的函数,执行时,使用对象中的函数属性执行,执行完毕后删除此对象的属性。
  • Object(context):为了避免 context 传入的不是一个对象,如传入了一个基本类型的字符串,字符串.属性会产生语法错误,所以使用 Object 包裹一下,将基本类型转为其对应的包装类型
  • const symbolKey = Symbol(): 使用 Symbol 的作用,如果不使用 Symbol,就需要随机定义一个属性名,这个属性名在 context 对象上可能存在,所以使用 Symbol 保持其唯一性
  • eval('context.fn(' + args +')') 这种写法,可以将 args 中的数组,变成一个一个的单个参数,如 [1, 2, 3] 变成 1, 2, 3,虽然下面的示例也可以通过 ES6 的扩展运算符去解决
Function.prototype.copyCall = function (context) {
  context = context ? Object(context) : window;
  const symbolKey = Symbol();
  context[symbolKey] = this;

  var args = [];
  for (var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
  }

  var result = eval('context[symbolKey](' + args + ')');

  delete context.fn;
  return result;
};

测试代码

var fnn = 2;

var obj = {
  fnn: 888,
};

function bar(name, age) {
  console.log(this.fnn, name, age);
}

bar.copyCall(null, 'fnn', '18.5'); // 2 'fnn' '18.5'
bar.copyCall(obj, 'fx', '18'); // 888 'fx' '18'

eval 测试

const arr = [12, 3123, 213];

function test(a, b, c) {
  console.log(a, b, c);
}

eval('test(' + arr + ')'); // 输出 12 3123 213
eval('test(arr)'); // 输出 [12, 3123, 213] undefined undefined

模拟实现 apply 方法

思路和 call 一样

Function.prototype.copyApply = function (context, arr) {
  context = context ? Object(context) : window;
  const symbolKey = Symbol();
  context[symbolKey] = this;

  var result;
  if (!arr) {
    result = context[symbolKey]();
  } else {
    var args = [];
    for (var i = 0, len = arr.length; i < len; i++) {
      args.push('arr[' + i + ']');
    }
    result = eval('context[symbolKey](' + args + ')');
  }

  delete context[symbolKey];
  return result;
};

测试代码

var fnn = 2;

var obj = {
  fnn: 888,
};

function bar(name, age) {
  console.log(this.fnn, name, age);
}

bar.copyApply(null, ['fnn', '18.5']); // 2 'fnn' '18.5'
bar.copyApply(obj, ['fx', '18']); // 888 'fx' '18'
💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEJavaScript apply、call 和 bind 用法详解加载中