From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../global_objects/function/bind/index.html | 324 +++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 files/zh-cn/web/javascript/reference/global_objects/function/bind/index.html (limited to 'files/zh-cn/web/javascript/reference/global_objects/function/bind') diff --git a/files/zh-cn/web/javascript/reference/global_objects/function/bind/index.html b/files/zh-cn/web/javascript/reference/global_objects/function/bind/index.html new file mode 100644 index 0000000000..fbabcdfc13 --- /dev/null +++ b/files/zh-cn/web/javascript/reference/global_objects/function/bind/index.html @@ -0,0 +1,324 @@ +--- +title: Function.prototype.bind() +slug: Web/JavaScript/Reference/Global_Objects/Function/bind +tags: + - ECMAScript 2015 + - ECMAScript 5 + - Function + - JavaScript + - Method + - polyfill +translation_of: Web/JavaScript/Reference/Global_Objects/Function/bind +--- +
{{JSRef}}
+ +

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

+ +
{{EmbedInteractiveExample("pages/js/function-bind.html", "taller")}}
+ + + +

语法

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

参数

+ +
+
thisArg
+
调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用{{jsxref("Operators/new", "new")}}运算符构造绑定函数,则忽略该值。当使用 bindsetTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者thisArgnullundefined,执行作用域的 this 将被视为新函数的 thisArg
+
arg1, arg2, ...
+
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
+
+ +

返回值

+ +

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

+ +

描述

+ +
bind() 函数会创建一个新的绑定函数bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数
+绑定函数具有以下内部属性:
+ +
+ +
+ + +

当调用绑定函数时,它调用 [[BoundTargetFunction]] 上的内部方法 [[Call]],就像这样 Call(boundThis, args)。其中,boundThis[[BoundThis]]args[[BoundArguments]] 加上通过函数调用传入的参数列表。

+
+ +
+

绑定函数也可以使用 {{jsxref("Operators/new", "new")}} 运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的 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 ,第二个参数被忽略
+
+ +

配合 setTimeout

+ +

在默认情况下,使用 {{ domxref("window.setTimeout()") }} 时,this 关键字会指向 {{ domxref("window") }} (或 global)对象。当类的方法中需要 this 指向类的实例时,你可能需要显式地把 this 绑定到回调函数,就不会丢失该实例的引用。

+ +
function LateBloomer() {
+  this.petalCount = Math.ceil(Math.random() * 12) + 1;
+}
+
+// 在 1 秒钟后声明 bloom
+LateBloomer.prototype.bloom = function() {
+  window.setTimeout(this.declare.bind(this), 1000);
+};
+
+LateBloomer.prototype.declare = function() {
+  console.log('I am a beautiful flower with ' +
+    this.petalCount + ' petals!');
+};
+
+var flower = new LateBloomer();
+flower.bloom();  // 一秒钟后, 调用 'declare' 方法
+ +

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

+ +
+

警告 :这部分演示了 JavaScript 的能力并且记录了 bind() 的超前用法。以下展示的方法并不是最佳的解决方案,且可能不应该用在任何生产环境中。

+
+ +

绑定函数自动适应于使用 {{jsxref("Operators/new", "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
+ +

请注意,你不需要做特别的处理就可以用 {{jsxref("Operators/new", "new")}} 操作符创建一个绑定函数。也就是说,你不需要做特别处理就可以创建一个可以被直接调用的绑定函数,即使你更希望绑定函数是用 {{jsxref("Operators/new", "new")}} 操作符来调用。

+ +
// ...接着上面的代码继续的话,
+// 这个例子可以直接在你的 JavaScript 控制台运行
+
+// 仍然能作为一个普通函数来调用
+// (即使通常来说这个不是被期望发生的)
+YAxisPoint(13);
+
+emptyObj.x + ',' + emptyObj.y;   //  '0,13'
+
+ +

如果你希望一个绑定函数要么只能用 {{jsxref("Operators/new", "new")}} 操作符,要么只能直接调用,那你必须在目标函数上显式规定这个限制。

+ +

快捷调用

+ +

在你想要为一个需要特定的 this 值的函数创建一个捷径(shortcut)的时候,bind() 也很好用。

+ +

你可以用 {{jsxref("Array.prototype.slice")}} 来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以简单地这样写:

+ +
var slice = Array.prototype.slice;
+
+// ...
+
+slice.apply(arguments);
+ +

bind()可以使这个过程变得简单。在下面这段代码里面,slice 是 {{jsxref("Function.prototype")}} 的 {{jsxref("Function.prototype.apply()", "apply()")}} 方法的绑定函数,并且将 {{jsxref("Array.prototype")}} 的 {{jsxref("Array.prototype.slice()", "slice()")}} 方法作为 this 的值。这意味着我们压根儿用不着上面那个 apply()调用了。

+ +
// 与前一段代码的 "slice" 效果相同
+var unboundSlice = Array.prototype.slice;
+var slice = Function.prototype.apply.bind(unboundSlice);
+
+// ...
+
+slice(arguments);
+ +

Polyfill

+ +

有两种实现bind的方法,下面第一种不支持使用new调用新创建的构造函数,而第二种支持。

+ +
// Does not work with `new (funcA.bind(thisArg, args))`
+if (!Function.prototype.bind) (function(){
+  var slice = Array.prototype.slice;
+  Function.prototype.bind = function() {
+    var thatFunc = this, thatArg = arguments[0];
+    var args = slice.call(arguments, 1);
+    if (typeof thatFunc !== 'function') {
+      // closest thing possible to the ECMAScript 5
+      // internal IsCallable function
+      throw new TypeError('Function.prototype.bind - ' +
+             'what is trying to be bound is not callable');
+    }
+    return function(){
+      var funcArgs = args.concat(slice.call(arguments))
+      return thatFunc.apply(thatArg, funcArgs);
+    };
+  };
+})();
+ +

你可以将这段代码插入到你的脚本开头,从而使你的 bind() 在没有内置实现支持的环境中也可以部分地使用bind

+ +
//  Yes, it does work with `new (funcA.bind(thisArg, args))`
+if (!Function.prototype.bind) (function(){
+  var ArrayPrototypeSlice = Array.prototype.slice;
+  Function.prototype.bind = function(otherThis) {
+    if (typeof this !== 'function') {
+      // closest thing possible to the ECMAScript 5
+      // internal IsCallable function
+      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
+    }
+
+    var baseArgs= ArrayPrototypeSlice.call(arguments, 1),
+        baseArgsLength = baseArgs.length,
+        fToBind = this,
+        fNOP    = function() {},
+        fBound  = function() {
+          baseArgs.length = baseArgsLength; // reset to default base arguments
+          baseArgs.push.apply(baseArgs, arguments);
+          return fToBind.apply(
+                 fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
+          );
+        };
+
+    if (this.prototype) {
+      // Function.prototype doesn't have a prototype property
+      fNOP.prototype = this.prototype;
+    }
+    fBound.prototype = new fNOP();
+
+    return fBound;
+  };
+})();
+ +

上述算法和实际的实现算法还有许多其他的不同 (尽管可能还有其他不同之处,但已经没有必要再多列举):

+ + + +

如果你选择使用这部分实现,你不能依赖于那些与 ECMA-262, 5th edition 规定的行为偏离的例子。bind() 函数被广泛支持之前,某些情况下(或者为了兼容某些特定需求对其做一些特定修改后)可以选择用这个实现作为过渡。

+ +

请访问 https://github.com/Raynos/function-bind 以查找更完整的解决方案!

+ +

规范

+ + + + + + + + + + + + + + + + + + + + + + + + +
规范状态备注
{{SpecName('ES5.1', '#sec-15.3.4.5', 'Function.prototype.bind')}}{{Spec2('ES5.1')}}初始定义。在 JavaScript 1.8.5 中实现。
{{SpecName('ES2015', '#sec-function.prototype.bind', 'Function.prototype.bind')}}{{Spec2('ES2015')}}
{{SpecName('ESDraft', '#sec-function.prototype.bind', 'Function.prototype.bind')}}{{Spec2('ESDraft')}}
+ +

浏览器兼容性

+ + + +

{{Compat("javascript.builtins.Function.bind")}}

+ +

相关链接

+ + -- cgit v1.2.3-54-g00ecf