JavaScript 中 call()、apply()、bind() 的用法

1、call()

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

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

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 方法调用父构造函数

在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承,类似于 Java 中的写法。下例中,使用 Food 和 Toy 构造函数创建的对象实例都会拥有在 Product 构造函数中添加的 name 属性和 price 属性,但 category 属性是在各自的构造函数中定义的。

function Product(name, price) {
  this.name = name;
  this.price = price;
}

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

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

var cheese = new Food('feta', 5);
console.log(cheese);
// 输出: Food {name: "feta", price: 5, category: "food"}
var fun = new Toy('robot', 40);
console.log(fun);
// 输出: Toy {name: "robot", price: 40, category: "toy"}

使用 call 方法调用匿名函数

在下例中的 for 循环体内,我们创建了一个匿名函数,然后通过调用该函数的 call 方法,将每个数组元素作为指定的 this 值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个 print 方法,这个 print 方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为 this 值传入那个匿名函数(普通参数就可以),目的是为了演示 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);
}

// 输出: #0 Lion: King
// 输出: #1 Whale: Fail

使用 call 方法调用函数并且指定上下文的 ‘this’

在下面的例子中,当调用 greet 方法的时候,该方法的this值会绑定到 obj 对象。

function greet() {
  var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
  console.log(reply);
}

var obj = {
  animal: 'cats', sleepDuration: '12 and 16 hours'
};

greet.call(obj);  
// 输出: cats typically sleep between 12 and 16 hours

使用 call 方法调用函数并且不指定第一个参数(argument)

在下面的例子中,我们调用了 display 方法,但并没有传递它的第一个参数。如果没有传递第一个参数,this 的值将会被绑定为全局对象。

var sData = 'Wisen';

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

display.call();
// 输出: sData value is Wisen

2、apply()

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

注意:call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。

语法

func.apply(thisArg, [argsArray])

thisArg
必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。 浏览器兼容性 请参阅本文底部内容。
返回值
调用有指定this值和参数的函数的结果。

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

我们可以使用push将元素追加到数组中。并且,因为push接受可变数量的参数,我们也可以一次推送多个元素。但是,如果我们传递一个数组来推送,它实际上会将该数组作为单个元素添加,而不是单独添加元素,因此我们最终得到一个数组内的数组。如果那不是我们想要的怎么办?在这种情况下,concat确实具有我们想要的行为,但它实际上并不附加到现有数组,而是创建并返回一个新数组。 但是我们想要附加到我们现有的阵列…那么现在呢? 写一个循环?当然不是吗?

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

使用apply和内置函数

聪明的apply用法允许你在某些本来需要写成遍历数组变量的任务中使用内建的函数。在接下里的例子中我们会使用Math.max/Math.min来找出一个数组中的最大/最小值。

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

/* 应用(apply) Math.min/Math.max 内置函数完成 */
var max = Math.max.apply(null, numbers); /* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */
var min = Math.min.apply(null, numbers);

/* 代码对比: 用简单循环完成 */
max = -Infinity, min = +Infinity;

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] > max)
    max = numbers[i];
  if (numbers[i] < min) 
    min = numbers[i];
}

标题

3、bind()

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

语法

function.bind(thisArg[, arg1[, arg2[, …]]])

thisArg
调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,执行作用域的 this 将被视为新函数的 thisArg。
arg1, arg2, …
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
返回值
返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

创建绑定函数

bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 值。JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题:

this.x = 9;    // 在浏览器中,this 指向全局的 "window" 对象
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();   
// 返回 9 - 因为函数是在全局作用域中调用的

// 创建一个新函数,把 'this' 绑定到 module 对象
// 新手可能会将全局变量 x 与 module 的属性 x 混淆
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

偏函数

bind() 的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。

function list() {
  return Array.prototype.slice.call(arguments);
}

function addArguments(arg1, arg2) {
    return arg1 + arg2
}

var list1 = list(1, 2, 3); // [1, 2, 3]

var result1 = addArguments(1, 2); // 3

// 创建一个函数,它拥有预设参数列表。
var leadingThirtysevenList = list.bind(null, 37);

// 创建一个函数,它拥有预设的第一个参数
var addThirtySeven = addArguments.bind(null, 37); 

var list2 = leadingThirtysevenList(); 
// [37]

var list3 = leadingThirtysevenList(1, 2, 3); 
// [37, 1, 2, 3]

var result2 = addThirtySeven(5); 
// 37 + 5 = 42 

var result3 = addThirtySeven(5, 10);
// 37 + 5 = 42 ,第二个参数被忽略

作为构造函数使用的绑定函数

绑定函数自动适应于使用 new 操作符去构造一个由目标函数创建的新实例。当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。不过提供的参数列表仍然会插入到构造函数调用时的参数列表之前。

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

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

var p = new Point(1, 2);
p.toString(); // '1,2'

var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);

// 本页下方的 polyfill 不支持运行这行代码,
// 但使用原生的 bind 方法运行是没问题的:

var YAxisPoint = Point.bind(null, 0/*x*/);

/*(译注:polyfill 的 bind 方法中,如果把 bind 的第一个参数加上,
即对新绑定的 this 执行 Object(this),包装为对象,
因为 Object(null) 是 {},所以也可以支持)*/

var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new YAxisPoint(17, 42) instanceof Point; // true

文章引用: Function.prototype.apply()Function.prototype.bind()Function.prototype.call()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值