los bind()
El método crea una nueva función que, cuando se llama, tiene su this
palabra clave establecida en el valor proporcionado, con una secuencia dada de argumentos que preceden a los proporcionados cuando se llama a la nueva función.
Sintaxis
bind(thisArg) bind(thisArg, arg1) bind(thisArg, arg1, arg2) bind(thisArg, arg1, ... , argN)
Parámetros
thisArg
- El valor que se pasará como
this
parámetro a la función de destinofunc
cuando se llama a la función vinculada. El valor se ignora si la función vinculada se construye utilizando elnew
operador. Cuando usasbind
para crear una función (suministrada como devolución de llamada) dentro de unsetTimeout
, cualquier valor primitivo pasado comothisArg
se convierte en objeto. Si no se proporcionan argumentos parabind
, o si elthisArg
esnull
oundefined
, losthis
del alcance de ejecución se trata como elthisArg
para la nueva función. -
arg1, arg2, ...argN
Opcional - Argumentos para anteponer a los argumentos proporcionados a la función vinculada al invocar
func
.
Valor devuelto
Una copia de la función dada con el especificado this
valor y argumentos iniciales (si se proporcionan).
Descripción
los bind()
función crea un nuevo función vinculada, que es un objeto de función exótica (un término de ECMAScript 2015) que envuelve el objeto de función original. Llamar a la función enlazada generalmente da como resultado la ejecución de su función envuelta.
Una función vinculada tiene las siguientes propiedades internas:
[[BoundTargetFunction]]
- El objeto de función envuelto
[[BoundThis]]
- El valor que siempre se pasa como
this
valor al llamar a la función envuelta. [[BoundArguments]]
- Una lista de valores cuyos elementos se utilizan como los primeros argumentos para cualquier llamada a la función envuelta.
[[Call]]
- Ejecuta código asociado con este objeto. Se invoca mediante una expresión de llamada a función. Los argumentos del método interno son un
this
value y una lista que contiene los argumentos pasados a la función por una expresión de llamada.
Cuando se llama a una función vinculada, llama al método interno [[Call]]
sobre [[BoundTargetFunction]]
, con los siguientes argumentos Call(boundThis, ...args)
. Dónde boundThis
es [[BoundThis]]
, args
es [[BoundArguments]]
, seguido de los argumentos pasados por la llamada a la función.
Una función ligada también se puede construir usando el new
operador. Hacerlo actúa como si la función de destino se hubiera construido. El proporcionado this
value se ignora, mientras que los argumentos antepuestos se proporcionan a la función emulada.
Ejemplos de
Creando una función enlazada
El uso más simple de bind()
es hacer una función que, no importa cómo se llame, se llame con un particular this
valor.
Un error común para los nuevos programadores de JavaScript es extraer un método de un objeto, luego llamar a esa función y esperar que use el objeto original como su this
(por ejemplo, utilizando el método en código basado en devolución de llamada).
Sin embargo, sin un cuidado especial, el objeto original suele perderse. La creación de una función vinculada a partir de la función, utilizando el objeto original, resuelve perfectamente este problema:
this.x = 9; // 'this' refers to global 'window' object here in a browser const module = { x: 81, getX: function() { return this.x; } }; module.getX(); // returns 81 const retrieveX = module.getX; retrieveX(); // returns 9; the function gets invoked at the global scope // Create a new function with 'this' bound to module // New programmers might confuse the // global variable 'x' with module's property 'x' const boundGetX = retrieveX.bind(module); boundGetX(); // returns 81
Funciones parcialmente aplicadas
El siguiente uso más simple de bind()
es hacer una función con argumentos iniciales preespecificados.
Estos argumentos (si los hay) siguen los this
value y luego se insertan al comienzo de los argumentos que se pasan a la función de destino, seguidos de los argumentos que se pasan a la función vinculada en el momento en que se llama.
function list() { return Array.prototype.slice.call(arguments); } function addArguments(arg1, arg2) { return arg1 + arg2 } const list1 = list(1, 2, 3); // [1, 2, 3] const result1 = addArguments(1, 2); // 3 // Create a function with a preset leading argument const leadingThirtysevenList = list.bind(null, 37); // Create a function with a preset first argument. const addThirtySeven = addArguments.bind(null, 37); const list2 = leadingThirtysevenList(); // [37] const list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3] const result2 = addThirtySeven(5); // 37 + 5 = 42 const result3 = addThirtySeven(5, 10); // 37 + 5 = 42 // (the second argument is ignored)
Con setTimeout()
Por defecto dentro de window.setTimeout()
, los this
la palabra clave se establecerá en el window
(o global
) objeto. Cuando se trabaja con métodos de clase que requieren this
para hacer referencia a instancias de clase, puede vincular explícitamente this
a la función de devolución de llamada, para mantener la instancia.
function LateBloomer() { this.petalCount = Math.floor(Math.random() * 12) + 1; } // Declare bloom after a delay of 1 second 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!`); }; const flower = new LateBloomer(); flower.bloom(); // after 1 second, calls 'flower.declare()'
Funciones enlazadas utilizadas como constructores
Advertencia: Esta sección demuestra las capacidades de JavaScript y documenta algunos casos extremos de la bind()
método.
Los métodos que se muestran a continuación no son la mejor manera de hacer las cosas y probablemente no deberían usarse en ningún entorno de producción.
Las funciones enlazadas son automáticamente adecuadas para su uso con new
operador para construir nuevas instancias creadas por la función de destino. Cuando se usa una función vinculada para construir un valor, el this
se ignora.
Sin embargo, los argumentos proporcionados todavía se anteponen a la llamada al constructor:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function() { return `${this.x},${this.y}`; }; const p = new Point(1, 2); p.toString(); // '1,2' // not supported in the polyfill below, // works fine with native bind: const YAxisPoint = Point.bind(null, 0/*x*/); const emptyObj = {}; const YAxisPoint = Point.bind(emptyObj, 0/*x*/); const axisPoint = new YAxisPoint(5); axisPoint.toString(); // '0,5' axisPoint instanceof Point; // true axisPoint instanceof YAxisPoint; // true new YAxisPoint(17, 42) instanceof Point; // true
Tenga en cuenta que no necesita hacer nada especial para crear una función vinculada para usar con new
.
El corolario es que no necesita hacer nada especial para crear una función enlazada que se llamará claramente, incluso si prefiere requerir que la función enlazada solo se llame usando new
.
// Example can be run directly in your JavaScript console // ...continued from above // Can still be called as a normal function // (although usually this is undesired) YAxisPoint(13); `${emptyObj.x},${emptyObj.y}`; // > '0,13'
Si desea admitir el uso de una función vinculada solo usando new
, o solo llamándolo, la función de destino debe hacer cumplir esa restricción.
Creando atajos
bind()
También es útil en los casos en los que desee crear un acceso directo a una función que requiera una this
valor.
Llevar Array.prototype.slice()
, por ejemplo, que desea utilizar para convertir un objeto similar a una matriz en una matriz real. Podrías crear un atajo como este:
const slice = Array.prototype.slice; // ... slice.apply(arguments);
Con bind()
, esto se puede simplificar.
En el siguiente fragmento de código, slice()
es una función ligada a la apply()
funcion de Function
, con el this
valor establecido en el slice()
funcion de Array.prototype
. Esto significa que adicional apply()
las llamadas se pueden eliminar:
// same as "slice" in the previous example const unboundSlice = Array.prototype.slice; const slice = Function.prototype.apply.bind(unboundSlice); // ... slice(arguments);
Polyfill
Debido a que los navegadores más antiguos generalmente también son navegadores más lentos, es mucho más crítico de lo que la mayoría de la gente reconoce para crear polyfills de rendimiento para hacer que la experiencia de navegación en navegadores obsoletos sea un poco menos horrible.
Por lo tanto, a continuación se presentan dos opciones para Function.prototype.bind()
polyfills:
- El primero es mucho más pequeño y de mayor rendimiento, pero no funciona cuando se usa el
new
operador. - El segundo es más grande y de menor rendimiento, pero permite cierto uso del
new
operador en funciones vinculadas.
Generalmente, en la mayoría de los códigos es muy raro ver new
se usa en una función enlazada, por lo que generalmente es mejor optar por la primera opción.
// 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); }; }; })();
Puede solucionar parcialmente esto insertando el siguiente código al comienzo de sus scripts, lo que permite el uso de gran parte de la funcionalidad de bind()
en implementaciones que no lo soportan de forma nativa.
// 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; }; })();
Algunas de las muchas diferencias (bien puede haber otras, ya que esta lista no intenta seriamente ser exhaustiva) entre este algoritmo y el algoritmo especificado son:
- La implementación parcial se basa en
Array.prototype.slice()
,Array.prototype.concat()
,Function.prototype.call()
yFunction.prototype.apply()
, métodos incorporados para tener sus valores originales. - La implementación parcial crea funciones que no tienen una “píldora venenosa” inmutable
caller
yarguments
propiedades que arrojan unTypeError
al obtener, configurar o eliminar. (Esto podría agregarse si la implementación admiteObject.defineProperty
, o implementado parcialmente [without throw-on-delete behavior] si la implementación apoya el__defineGetter__
y__defineSetter__
extensiones.) - La implementación parcial crea funciones que tienen un
prototype
propiedad. (Las funciones enlazadas adecuadas no tienen ninguna). - La implementación parcial crea funciones vinculadas cuyas
length
la propiedad no está de acuerdo con lo ordenado por ECMA-262: crea funciones conlength
de0
. Una implementación completa, según la longitud de la función de destino y el número de argumentos preespecificados, puede devolver una longitud distinta de cero. - La implementación parcial crea funciones vinculadas cuyas
name
La propiedad no se deriva del nombre de la función original. Según ECMA-262, el nombre de la función enlazada devuelta debe ser “enlazada” + nombre de la función de destino (observe el carácter de espacio).
Si elige utilizar esta implementación parcial, no debe confiar en aquellos casos en los que el comportamiento se desvíe de ECMA-262, 5th ¡edición! Afortunadamente, estas desviaciones de la especificación rara vez (si alguna vez) surgen en la mayoría de las situaciones de codificación. Si no comprende alguna de las desviaciones de la especificación anterior, entonces es seguro en este caso particular no preocuparse por estos detalles de desviación que no cumplen.
Si es absolutamente necesario y el rendimiento no es una preocupación., se puede encontrar una solución mucho más lenta (pero más compatible con las especificaciones) en https://github.com/Raynos/function-bind.
Especificaciones
Especificación |
---|
Especificación del lenguaje ECMAScript (ECMAScript) # sec-function.prototype.bind |
Compatibilidad del navegador
Escritorio | Móvil | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Cromo | Borde | Firefox | explorador de Internet | Ópera | Safari | WebView Android | Chrome Android | Firefox para Android | Opera Android | Safari en IOS | Internet de Samsung | |
bind |
7 | 12 | 4 | 9 | 11,6 | 5.1 | 4 | 18 | 4 | 12 | 6 | 1.0 |
Ver también
Function.prototype.apply()
Function.prototype.call()
- Funciones