Los métodos call, bind y apply son funcionalidades del prototipo Function en JavaScript, diseñadas para mdoificar el contexto de ejecución (el valor de this) de una función.
Consideraciones Generales
Si bien las implementaciones nativas de estos métodos están escritas en C++, podemos simular su comportamiento utilizando JavaScript puro. Es importante tener en cuenta algunas particularidades:
- Si se proporciona un valor de tipo primitivo (como un número, cadena o booleano) como argumento para
this, JavaScript lo envolverá automáticamente en su correspondiente tipo objeto (Number,String,Boolean) para permitir la asignación de propiedades. La funciónObject()puede usarse para realizar esta conversión explícitamente. - En modo no estrcito, si no se especifica un contexto de
thiso se pasanulloundefined, el valor dethisdentro de la función se referirá al objeto global (windowen navegadores).
Implementación Personalizada de call
El método call permite invocar una función con un this específico y argumentos pasados individualmente.
Function.prototype.iCall = function (thisContext, ...args) {
// 1. Guardar la función original a la que se llama
const originalFunction = this;
// 2. Determinar el contexto de 'this'. Si es nulo/indefinido, usar window (en modo no estricto).
// De lo contrario, convertir el contexto proporcionado a un objeto.
const context = (thisContext === null || thisContext === undefined)
? window
: Object(thisContext);
// 3. Asignar la función original como una propiedad temporal del objeto de contexto.
context.tempFunction = originalFunction;
// 4. Invocar la función a través del objeto de contexto, pasando los argumentos.
const result = context.tempFunction(...args);
// 5. Eliminar la propiedad temporal del objeto de contexto.
delete context.tempFunction;
// 6. Devolver el resultado de la ejecución de la función.
return result;
};
// Ejemplo de uso:
function displayUserInfo(city, country) {
console.log(`Contexto 'this':`, this);
console.log(`Ciudad: ${city}, País: ${country}`);
}
const userDetails = { name: 'Alice' };
console.log("--- Ejemplo iCall ---");
displayUserInfo.iCall(userDetails, 'New York', 'USA');
// Salida esperada:
// Contexto 'this': { name: 'Alice' }
// Ciudad: New York, País: USA
displayUserInfo.iCall(null, 'London', 'UK');
// Salida esperada (en modo no estricto):
// Contexto 'this': Window {...}
// Ciudad: London, País: UK
Implementación Personalizada de apply
Similar a call, apply invoca una función con un this específico, pero los argumentos se pasan como un array.
Function.prototype.iApply = function (thisContext, argsArray) {
// 1. Guardar la función original
const originalFunction = this;
// 2. Determinar el contexto de 'this', similar a iCall
const context = (thisContext === null || thisContext === undefined)
? window
: Object(thisContext);
// 3. Asignar la función como propiedad temporal
context.tempFunction = originalFunction;
// 4. Invocar la función usando el operador spread para desempaquetar el array de argumentos
const result = context.tempFunction(...argsArray);
// 5. Eliminar la propiedad temporal
delete context.tempFunction;
// 6. Devolver el resultado
return result;
};
// Ejemplo de uso:
function displayProductDetails(price, currency) {
console.log(`Contexto 'this':`, this);
console.log(`Precio: ${price} ${currency}`);
}
const productInfo = { id: 'XYZ123' };
const details = [50, 'USD'];
console.log("\n--- Ejemplo iApply ---");
displayProductDetails.iApply(productInfo, details);
// Salida esperada:
// Contexto 'this': { id: 'XYZ123' }
// Precio: 50 USD
const dataString = "Sample Data";
const formatArgs = ['PDF', 'A4'];
displayProductDetails.iApply(dataString, formatArgs);
// Salida esperada (en modo no estricto):
// Contexto 'this': String { "Sample Data" }
// Precio: PDF A4
Implementación Personalizada de bind
El método bind crea una nueva función que, cuando es llamada, tiene su palabra clave this establecida con el valor proporcionado, junto con una secuencia de argumentos iniciales. No invoca la función inmediatamente.
Function.prototype.iBind = function (thisContext, ...initialArgs) {
// 1. Guardar la función original
const originalFunction = this;
// 2. Determinar el contexto de 'this'
const context = (thisContext === null || thisContext === undefined)
? window
: Object(thisContext);
// 3. Devolver una nueva función. Esta nueva función contendrá el contexto y los argumentos iniciales.
return function (...laterArgs) {
// Dentro de esta nueva función, asignamos temporalmente la función original al contexto
// y la ejecutamos con los argumentos iniciales y los argumentos pasados posteriormente.
context.tempFunction = originalFunction;
const result = context.tempFunction(...initialArgs, ...laterArgs);
// Es importante eliminar la propiedad temporal después de la ejecución
// para evitar efectos secundarios en el objeto de contexto.
delete context.tempFunction;
// Devolver el resultado de la ejecución.
return result;
};
};
// Ejemplo de uso:
function greet(greeting, punctuation) {
console.log(`Contexto 'this':`, this);
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: 'Bob' };
console.log("\n--- Ejemplo iBind ---");
// Creamos una nueva función 'boundGreet' con 'person' como contexto y 'Hello' como saludo inicial.
const boundGreet = greet.iBind(person, 'Hello');
// Ahora llamamos a 'boundGreet' con argumentos adicionales ('!').
boundGreet('!');
// Salida esperada:
// Contexto 'this': { name: 'Bob' }
// Hello, Bob!
// También podemos llamar a la función devuelta por bind con más argumentos.
const anotherBoundGreet = greet.iBind({ name: 'Charlie' });
anotherBoundGreet('Hi', '. Welcome!');
// Salida esperada:
// Contexto 'this': { name: 'Charlie' }
// Hi, Charlie. Welcome!