从经典案例中认识call(),apply(),bind()

目录

  • function基础
  • apply(context,[arg1,arg2])
    • 获取数组中最大的值
    • 使用push合并数组
    • 模拟construct构造器方法
  • call(context,arg1,arg2)
    • 类数组对象的数组方法操作
    • uncurrying 反柯里化
  • bind(context)

function基础

在ECMAScript中函数是对象,因此函数也有属性和方法。[1]

1.属性

  • length
    参数数量
  • prototype
    指向函数的所有实例方法,换句话说,诸如toString()valueOf()(返回函数的代码) 都保存在prototype名下

2.方法

每个函数都包含两个非继承而来(即不在prototype名下)的方法apply()call()

apply(context,[arg1,arg2,...])

apply()方法接收两个参数:一个是在其中运行的函数作用域,另一个可以是arguments也可以是自定义Array的实例。
经典案例
- 获取数组中最大的值

var arr = [1,2,3,4,9,8,7,5]
var result = Math.max.apply(null,a rr)

- 使用push合并数组

var arr1 = [1,2,3]
var arr2 = [4,5,6]
Array.prototype.push.apply(arr2,arr1);
console.log(arr2) //[4,5,6,1,2,3]

- 模拟construct构造器方法

Function.prototype.construct = function(){
  var oNew = Object.create(this.prototype);
  this.apply(oNew, arguments);
  return oNew;
}

call(context,arg1,arg2,arg3,...)

call()apply()用法相同,只是除了第一个都是作用域之外,其余参数必须逐个列举出来,即 使用call()方法时必须明确要传多少参数,同样也可以先创建函数之后再利用call确定参数。
经典案例
- 对类数组对象进行一系列数组的方法操作
类数组对象{0:'asd',1:123,2:{},length:3},最经典的就是arguments对象

[].shift.call(arguments)  //删除arguments中的第一个参数并返回这个参数

//详情解释
var a = {0:1,1:2,2:3,length:3};
console.log([].shift.call(a)) // 1
console.log( a )  // {0: 2, 1: 3, length: 2}

- 使用call调用父构造函数实现继承

function Parent(name, age){
  this.name = name;
  this.age = age;
}

function Son(name, age, weight){
  Parent.call(name, age) //继承Parent函数的name和age属性
  this.weight = weight //私有属性
}

var son = new Son('Niko', 24, 140)
console.log(son)  // Son {name:'Niko', age:24, weight:140}

- 使用call调用匿名函数

const arr = [0,1,2,3,4]
for(let i=0; i

- 修改上下文this指向

var name = 'Niko'
function func(){ console.log(this.name) }
var obj = {
  name : 'Bellic'
}
func.call(obj) // Bellic
func();  // Niko

- uncurrying反柯里化 [2]

我们常常让类数组对象去借用Array.prototype的方法,这是call和apply最常用的应用场景之一:

(function(){
  Array.prototype.push.call( arguments, 4 ); // arguments借用Array.prototype.push方法
  console.log( arguments ); // [1, 2, 3, 4]
})(1, 2, 3)

在我们的预期中,Array.prototype上的方法原本只能用来操作array对象。但用call和apply可以把任意对象当作this传入某个方法,这样一来,方法中用到this的地方就不再局限于原来规定的对象,而是加以泛化并得到更广的适用性。

那么如何将泛化this过程提取出来呢?
uncurrying()函数实现

Function.prototype.uncurrying = function(){
  var self = this;
  return function(){
    var obj = Array.prototype.shift.call( arguments );
    return self.apply( obj, arguments )
  }
}

bind(context)

bind()方法会创建一个函数的实例,其this值会被绑定传递给bind()函数的值。

Function.prototype.bind = function(context){
  var self = this;        //保存原函数
  return function(){    // 返回一个新函数
    return self.apply( context, arguments )
  }
}

总结

它们真正强大的地方是能够扩充函数赖以运行的作用域

参考

- [1]《JavaScript高级程序设计》(第3版) p.116 函数属性和方法
- [2] 《JavaScript设计模式与开发实践》 p.51 高阶函数
- 上述部分案例参考自https://developer.mozilla.org/

你可能感兴趣的:(从经典案例中认识call(),apply(),bind())