了解.apply与.call以及.bind

1.apply:foo.apply(obj,args)方法能接收两个参数:

     (1)obj:这个对象将代替Function类里this对象;

     (2)args:这个是数组,它将作为参数传给Function(args-->arguments);

  关于apply,我所知道的用法中,最经典的莫过于巧妙地实现提取数值(Number)数组最值的方法:

Array.prototype.getMin = function() {
  return Math.min.apply(null, this);
};
var arr = [1,2,3,4,5,213212,-99];
arr.getMin();//-99

2.call: foo.call(obj, args);

对于apply与call不同点的说明:A for array and C for comma;call的参数只能一个一个地传递;

3.bind()方法会创建一个新函数,当这个新函数被调用时,它的this值是传递给bind()的第一个参数, 它的参数是bind()的其他参数和其原本的参数.

  绑定函数示例:参考网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

this.x = 9; 
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX(); // 9, because in this case, "this" refers to the global object

// Create a new function with 'this' bound to module
//New programmers (like myself) might confuse the global var getX with module's property getX
var boundGetX = retrieveX.bind(module);//<<--------bind的第一个参数为this指向的对象
boundGetX(); // 81

  复制某个对象下的方法之后想要this仍然指向module对象,获取它的属性X值,如上文代码中的:var retrieveX = module.getX;

但是在直接调用复制出来的函数retrieveX时,注意它是在全局作用域下直接内调用的,那么此时的this指向window,所有无法像我们“想当然地”认为的那样取出module对象下的属性值;

  而bind拥有改变函数执行时的作用域的作用(apply与call同样拥有这样的作用),我们可以在调用时使用bind方法,并将bind的第一个参数设置为module对象,那么我们就改变了this的指向(指向module),此时我们就可以达到预期的目的了。

  关于.call、.bind的基本知识看完了?懂了吗?来个思考题检验一下你是否理解了这些方法.

问:下面的代码输出的是什么?

分析:

1.首先test函数输出的是操作对象(this指向)的a属性的值;所以接下来的思路就是判断this的指向;

2.三个var语句,定义了两个对象,一个绑定函数.

  对象

    var obj1= {a:1};

    var obj2 = {a:2};

  绑定函数

    var fn = test.bind(obj2);//绑定函数作用域内this指向obj2;

最后关键的执行语句test.bind(obj2).call(obj1);//call的第一个参数传递的也是this的指向那么问题来了,this到底指向哪个对象?

结果:如果你回答输出的a1那就错了,因为通过bind取得的函数是个闭包,在调用时,只会通过闭包作用域找到之前存在的值(不是this),所以用call(其只改变this)是无法改变它的结果。输出为2.

 


 

进一步进行如下测试:

let aa = function() {
   console.log(this.n);
   console.log(arguments);
};
let testObj = {
   n: 1 
};
let bb = aa.bind(testObj, "预置的参数");
bb.call({n:2}, "后来加入的参数一", "后来加入的参数二");

//控制台输出的结果是:
//1
//["预置的参数", "后来加入的参数一", "后来加入的参数二"]

得出的结论是:

1. bind函数生成的新函数在生成的时候它的上下文就已经固定了,后续通过.call或者.apply等方法都无法改变它。

2. 由预置的参数(n个) 与 后续加入的参数(n个)组成新函数执行时的arguments数组。

editTime:2017/02/08


 

转载于:https://www.cnblogs.com/foxNike/p/5656059.html

<think>我们正在分析一个复杂的函数创建表达式:`Array.apply.bind(Number.bind)`这个表达式可以分为几个部分来理解,我们需要逐步拆解。首先,我们回顾一下涉及的几个核心概念:1.`Function.prototype.bind`:该方法创建一个新的函数,在调用时,其`this`值会被绑定到提供的值,以及一个参数序列(如果有的话)。2.`Function.prototype.apply`:该方法调用一个函数,其`this`值被指定为提供的`thisArg`,以及一个参数数组(或类数组对象)。3.`Number`:这是一个内置的JavaScript构造函数,用于创建数字对象或将其他值转换为数字。表达式:`Array.apply.bind(Number.bind)`分解步骤:1.最内层是`Number.bind`:这里`Number`是一个函数(构造函数),所以`Number.bind`是`Function.prototype.bind`方法在`Number`函数上的引用。因此,`Number.bind`是一个函数,它可以用来创建一个新的函数,这个新函数在调用时会将`this`绑定到`Number`函数(或者绑定到`bind`的第一个参数指定的值?注意:这里需要澄清)。注意:`bind`方法本身是一个函数,它需要两个参数:第一个参数是调用`bind`时绑定的`this`值,第二个及以后的参数是预先填充的参数。但是,当我们说`Number.bind`时,它还没有被调用,它只是`bind`方法在`Number`函数上的引用。因此,`Number.bind`等价于:Function.prototype.bind.call(Number)或者更直接地说,就是`Number.bind`,它和任何函数的`bind`方法一样,需要被调用才能创建一个绑定函数。2.现在,我们看外层:`Array.apply.bind(...)``Array.apply`:`Array`是一个构造函数,它有一个`apply`方法(从`Function.prototype`继承而来)。所以`Array.apply`就是`Function.prototype.apply`方法在`Array`构造函数上的引用。然后,我们对`Array.apply`调用`bind`方法:`Array.apply.bind(Number.bind)`。这意味着,我们将`Array.apply`函数的`this`值绑定到`Number.bind`这个函数上。所以,整个表达式的结果是一个新的函数,我们暂且叫它`ff`:ff=Array.apply.bind(Number.bind)这个`ff`函数的行为是怎样的呢?首先,我们知道`Array.apply`原本的调用方式应该是:Array.apply(thisArg,[argsArray])它的作用是:以`thisArg`作为`this`,以`argsArray`作为参数数组来调用`Array`构造函数。但是,现在我们通过`bind`将`Array.apply`的`this`值固定为`Number.bind`(注意:这里的`this`值指的是`apply`方法执行时内部的`this`,即`Array.apply`中的`Array`被绑定为`Number.bind`?实际上,这里需要重新理解)。实际上,`apply`方法内部会使用它的`this`值作为要调用的函数。所以,原本的`Array.apply`相当于:apply方法内部:this(即Array)被调用,参数为thisArg和argsArray。而我们通过`bind`将`Array.apply`的`this`(即`apply`方法被调用时,其内部的`this`)绑定为`Number.bind`。也就是说,现在`ff`函数(即绑定后的函数)的调用相当于:ff(thisArg,[argsArray])会转换为:Array.apply.call(Number.bind,thisArg,argsArray)或者更直接地说,就是:Number.bind.apply(thisArg,argsArray)因为:`ff`=`Array.apply.bind(Number.bind)`,所以当我们调用`ff(arg1,arg2)`时,实际上是在调用`Array.apply`方法,但是`Array.apply`方法执行时的`this`被绑定为`Number.bind`。所以:ff(thisArg,argsArray)=>Number.bind.apply(thisArg,argsArray)而`apply`方法的作用是:调用`this`指向的函数(这里`this`指向`Number.bind`),并以第一个参数作为`thisArg`,第二个参数数组作为参数列表。所以:Number.bind.apply(thisArg,argsArray)等价于Number.bind.call(thisArg,...argsArray)但是,这里我们使用了`apply`来调用`Number.bind`,所以它相当于:Number.bind.call(thisArg,argsArray[0],argsArray[1],...)即:将`Number.bind`函数作为普通函数调用,并将`thisArg`作为`call`方法的第一个参数(即绑定`Number.bind`内部的`this`),然后将参数数组展开作为后续参数。而`Number.bind.call(thisArg,...args)`实际上是在设置`Number.bind`执行时的`this`(注意:`Number.bind`本身也是一个函数,它内部也有`this`,这里通过`call`将`Number.bind`的`this`设置为`thisArg`),然后传入的参数是`...args`。但是,`Number.bind`函数的作用是:创建一个新的函数,这个新函数是`Number`函数(构造函数)的绑定版本,其`this`值会被绑定到`Number.bind`的第一个参数,并且可以预先填充一些参数。所以,`Number.bind.call(thisArg,...args)`的返回值是一个新的函数,这个新函数是`Number`的绑定版本,其中:-`Number`函数内部的`this`被绑定到`thisArg`(因为`bind`的第一个参数是`thisArg`)?-并且预先填充了参数`...args`(即`bind`的后续参数)。但是,这里有一个关键点:`Number`函数作为构造函数,通常不会使用`this`绑定,因为构造函数通常`new`一起使用。当我们使用`new`调用构造函数时,`this`会被绑定到新创建的对象上,而预先绑定的`this`会被忽略(除非不使用`new`调用)。因此,我们创建的`ff`函数实际上是一个工厂函数,它返回一个绑定了`this`和部分参数的`Number`函数(构造函数)的绑定版本。然而,上面的分析可能过于复杂,我们可以通过一个具体的例子来演示:我们定义:ff=Array.apply.bind(Number.bind)调用ff:varboundFunc=ff(context,arg1,arg2,...);这相当于:boundFunc=Number.bind.apply(context,[arg1,arg2,...]);而`Number.bind.apply(context,[arg1,arg2,...])`又等价于:boundFunc=Number.bind.call(context,arg1,arg2,...);但是,注意:`Number.bind`是一个函数,它需要两个参数:第一个是`thisArg`(用于绑定给`Number`函数),第二个及以后是参数(用于部分应用)。所以,上面的调用相当于:boundFunc=Number.bind(arg1,arg2,...);但是这里将`context`作为`Number.bind`执行时的`this`值?这通常没有意义,因为`Number.bind`内部可能不会用到`this`。实际上,`Number.bind`作为`Function.prototype.bind`在`Number`上的引用,它本身就是一个函数,这个函数并不依赖`this`(除了它被调用时作为方法所属的对象,但这里我们通过`call`改变了它所属的对象,但`bind`方法本身是通用的,它只关心第一个参数是待绑定的函数,而这里我们通过`call`将`Number.bind`内部的`this`设置为`context`,但`bind`方法执行时,它会将`Number`函数(因为`Number.bind`是从`Number`上取到的)参数绑定。所以,实际上,无论我们怎么改变`Number.bind`的`this`,它都会绑定`Number`函数,因为`Number.bind`就是`Number`的`bind`方法。所以,`Number.bind.call(context,arg1,arg2,...)`其实等价于:context.Number=Number;//假设我们在context上添加一个Number属性,但实际上没有context.Number.bind(arg1,arg2,...)//然后在context上调用bind方法,但这并不是我们想要的这显然不是我们想要的,因为`context`可能没有`bind`方法。因此,我们需要重新审视:当我们使用`call`来调用`Number.bind`时,`call`的第一个参数`context`只是指定了`Number.bind`函数执行时的`this`值,但是`Number.bind`函数内部是如何工作的呢?`Function.prototype.bind`方法的内部实现大致如下(简化):Function.prototype.bind=function(thisArg,...presetArgs){varoriginalFunc=this;//这里的this就是调用bind方法的函数returnfunction(...args){returnoriginalFunc.apply(thisArg,presetArgs.concat(args));};};所以,当我们在`Number.bind`上使用`call`时,`call`的第一个参数(`context`)会成为`bind`方法内部的`this`值。在`bind`方法内部,它使用`this`(即`context`)作为原始函数(即要绑定的函数)。但是我们期望原始函数是`Number`,而这里却变成了`context`,这显然不是我们想要的。因此,我们之前的分解可能出现了问题。重新考虑:表达式`Array.apply.bind(Number.bind)`,它返回的函数`ff`在调用时:ff(thisArg,argsArray)实际上执行的是:Array.apply.call(Number.bind,thisArg,argsArray)而`Array.apply`的原本功能是:调用`Array`构造函数,并以`thisArg`作为`this`值,以`argsArray`作为参数数组。但是,这里我们通过`call`改变了`apply`方法执行时的`this`,将其设置为`Number.bind`。所以,实际上我们是在用`Number.bind`作为要调用的函数,然后以`thisArg`作为`this`值,`argsArray`作为参数数组来调用它。即:Number.bind.apply(thisArg,argsArray)注意:这里`apply`是`Function.prototype.apply`,所以它执行的效果就是:调用`Number.bind`函数,并且将`thisArg`作为`Number.bind`内部的`this`值,`argsArray`作为参数数组。所以,`ff`函数的作用就是:以给定的`thisArg`和参数数组`argsArray`来调用`Number.bind`函数。那么,`Number.bind`函数在被调用时,它的参数是什么?它需要两个参数:第一个参数是`thisArg_bind`(用于绑定给`Number`函数),第二个及以后的参数是预设参数。所以,`argsArray`应该是一个参数数组,其中第一个元素是`thisArg_bind`,后面的元素是预设参数。因此,`ff`函数可以这样使用:varboundNumberFunc=ff(thisArg_bind,presetArgs);其中:thisArg_bind:将作为绑定后的`Number`函数的`this`值(在调用绑定的`Number`函数时,`this`被固定为thisArg_bind)presetArgs:一个数组,包含预设的参数。但是,注意:`ff`的第二个参数`argsArray`应该是一个数组,它会被展开作为`Number.bind`的参数。所以,我们也可以这样写:varboundNumberFunc=ff(null,[1]);//这里thisArg_bind为null,预设参数为1这样,`boundNumberFunc`就是一个绑定的函数,调用它:`boundNumberFunc()`相当于`Number.call(null,1)`。然而,`Number`函数通常作为转换函数使用,它返回一个数字原始值,而不是对象(除非用`new`)。当我们用`new`调用时,它会创建一个数字对象。在绑定版本中,如果我们不使用`new`调用`boundNumberFunc`,那么`Number`函数的`this`绑定就不重要(因为`Number`函数在作为普通函数调用时,`this`可以是任意值,它返回转换后的数字)。所以,通常我们不会绑定`Number`函数的`this`,因为作为转换函数,它不依赖于`this`。那么,`ff`函数实际上就是用来创建一个预设了部分参数的`Number`函数。例如:varboundFunc=ff(null,[10]);//相当于boundFunc=Number.bind(null,10)boundFunc();//返回数字10所以,`ff`函数的作用可以简化为:创建一个新的函数,该函数调用`Number.bind`,并允许我们传入绑定所需的`thisArg`和预设参数数组。但通常我们不会关心`Number.bind`的`this`绑定,所以我们可以将`ff`的第一个参数(`thisArg`)设为`null`或`undefined`,而第二个参数是预设参数数组。但是,我们也可以这样使用:varboundFunc=ff(undefined,[10]);//和上面一样另外,如果我们想预设多个参数,可以这样:varboundFunc=ff(null,[10,20]);//相当于Number.bind(null,10,20)boundFunc();//相当于Number(10,20)->返回10,因为第二个参数被忽略因为`Number`函数只接受一个参数,所以多余的参数会被忽略。因此,`Array.apply.bind(Number.bind)`实际上创建了一个辅助函数,用于生成预设了参数的`Number`函数的绑定版本。但是,这个表达式在实际开发中非常罕见,因为它的效果可以直接用`Number.bind`来实现,而且更清晰:varboundFunc=Number.bind(null,10);所以,这个表达式可能只是为了演示函数式编程的技巧,或者在某些特定的函数组合场景中使用。总结:函数`ff=Array.apply.bind(Number.bind)`等价于:functionff(thisArg,argsArray){returnNumber.bind.apply(thisArg,argsArray);}更精确地说,它返回一个函数,该函数调用`Number.bind`,并指定`thisArg`作为`bind`内部的`this`值(通常不重要),以及`argsArray`作为参数列表(预设参数)。通常,我们可以忽略`thisArg`(设为`null`),然后`argsArray`是一个数组,包含我们想预设给`Number`函数的参数。调用`ff`返回的函数就是预设了参数的`Number`函数。示例:varff=Array.apply.bind(Number.bind);vartoNumber=ff(null,[]);//相当于Number.bind(null)->返回Number函数本身,但绑定了this为null(作为普通函数调用,this为null)console.log(toNumber("123"));//123(数字)varalways10=ff(null,[10]);//相当于Number.bind(null,10)console.log(always10());//10注意:由于`Number`函数不依赖于`this`,所以绑定`this`为`null`没有影响。因此,这个函数`ff`是一个通用的创建`Number`函数绑定版本的工厂函数,但通常我们不会这样使用。引用说明:以上分析基于对JavaScript中`bind`和`apply`方法的理解,没有直接引用外部资料,因此不添加引用标识。现在,根据要求生成相关问题。</think>在JavaScript中,表达式`Array.apply.bind(Number.bind)`创建的函数`ff`是一个高阶工具函数,用于生成预设参数的`Number`构造函数绑定版本。其核心行为如下: ### 函数作用解析 1. **结构分解**: - `Number.bind`:指向`Function.prototype.bind`方法在`Number`构造函数上的引用。 - `Array.apply`:指向`Function.prototype.apply`方法在`Array`构造函数上的引用。 - `Array.apply.bind(Number.bind)`:将`Array.apply`的调用上下文**永久绑定**到`Number.bind`函数上。 2. **等效逻辑**: ```javascript const ff = function(thisArg, argsArray) { return Number.bind.apply(thisArg, argsArray); }; ``` 即`ff`接收两个参数: - `thisArg`:指定`Number`函数中`this`的绑定值(通常无实际意义,因`Number`作为转换函数不依赖`this`)。 - `argsArray`:数组,包含预设给`Number`的参数。 3. **实际效果**: - `ff(null, [x])` 等价于 `Number.bind(null, x)` - 返回一个新函数,该函数调用时会忽略自身的`this`和参数,直接以预设参数执行`Number`转换。 ### 示例演示 ```javascript const ff = Array.apply.bind(Number.bind); // 场景1:创建始终返回10的函数 const getTen = ff(null, [10]); getTen(); // 10(数字类型) getTen("abc"); // 10(预设参数覆盖后续输入) // 场景2:创建字符串转数字的函数 const toNumber = ff(null, []); toNumber("123"); // 123(数字类型) toNumber(true); // 1 // 场景3:预设多参数(多余参数被忽略) const getFive = ff(null, [5, "foo"]); getFive(); // 5(仅首个预设参数生效) ``` ### 关键特性 1. **`this`绑定无效** `Number`作为原始值转换函数,不依赖`this`上下文。因此`ff`的第一个参数(如`null`)无实际影响[^1][^4]。 2. **参数预设** 通过`argsArray`预设参数,新函数调用时直接使用这些参数,忽略后续传入的参数[^4][^5]。 3. **直接`bind`的关系** `ff(null, [x])` 完全等价于 `Number.bind(null, x)`,但通过`apply`实现参数动态化。 ### 应用场景 - **参数固化**:快速创建固定返回值的工具函数(如`getTen`)。 - **函数式编程**:在组合函数时动态生成预设参数的转换器。 - **减少重复**:避免多次书写`Number.bind(null, ...)`。 ### 注意事项 - 若需保留调用时的动态参数,应直接使用`Number`而非`ff`。 - 该模式是`bind`和`apply`的链式应用,实际开发中更推荐显式使用`Number.bind`以保证可读性。 --- ### 相关问题 1. **`ff`和`Function.prototype.bind`有何本质区别?** `ff`通过`apply`动态绑定预设参数,而`bind`直接生成固定参数的函数。前者在参数动态化时更灵活。 2. **为什么`Number`中的`this`绑定在此场景无效?** `Number`作为原始值转换函数,内部不依赖`this`,其行为仅由参数决定[^1][^4]。 3. **如何用`ff`实现一个始终返回当前时间戳的函数?** 由于`Number`无法直接生成时间戳,此场景不适用。应改用`Date.now.bind(Date)`。 4. **`ff(null, [])`直接调用`Number`有何差异?** 两者功能相同,但`ff(null, [])`返回的是绑定后的函数,可延迟调用。 [^1]: `bind`方法创建的新函数会永久绑定`this`和预设参数。 [^4]: 绑定后的函数忽略后续传入的`this`和参数。 [^5]: `bind`返回新函数,其行为由预设参数决定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值