В этой статье рассказывается об использовании call, apply, bind и соответствующих принципах их реализации.
call
Метод call() вызывает функцию с указанным значением this и одним или несколькими аргументами, заданными отдельно. То есть: вы можете изменить точку this текущей функции, это также позволит выполнить текущую функцию.
Применение
function fun() {
console.log(this.name, arguments)
}
let obj = { name: 'clying' }
fun.call(obj, 'deng', 'deng')
// clying [Arguments] { '0': 'deng', '1': 'deng' }
выполнить
Реализация вызова и применения состоит в том, чтобы поместить функцию в свойство литерала obj, чтобы this в функции указывало на литеральный объект obj.
Простая версия реализации:
Function.prototype.mycall = function (context) {
context = (context == null || context == undefined) ? window : new Object(context)
context.fn = this
context.fn()
delete context.fn
}
Добавьте метод mycall в прототип функции, создайте контекст объекта context, если переданный объект не существует, он будет указывать на глобальное окно. Добавляя атрибут fn к контексту, fn контекста ссылается на функцию fun, которая вызывает метод и выполняет fun. Удалите атрибут fn после завершения выполнения.
Среди них сначала необходимо получить входящие параметры, затем он становится строковым массивом. Метод выполнения использует функцию eval, а затем вычисляет строку через eval, выполняет в ней код и возвращает результат вычисления.
Обновленная версия:
Передайте параметры для вызова.
Function.prototype.mycall = function (context) {
context = (context == null || context == undefined) ? window : new Object(context)
context.fn = this
let arr = []
for (let i = 1; i < arguments.length; i++) {
arr.push('argument[' + i + ']') // ["arguments[1]", "arguments[2]"]
}
let r = eval('context.fn(' + arr + ')') // 执行函数fun,并传入参数
delete context.fn
return r
}
Кроме того, вызов также может быть реализован с помощью синтаксиса деструктурирования.
Function.prototype.mycall = function (context, ...args) {
context = (context == null || context == undefined) ? window : new Object(context)
context.fn = this
context.fn(...args)
delete context.fn
}
Если вы хотите иметь возможность вызывать метод вызова несколько раз, вы можетеcontext.fn(...args)
Сохраните его в переменной и верните ее в конце.
Function.prototype.mycall = function (context, ...args) {
context = (context == null || context == undefined) ? window : new Object(context)
context.fn = this
let r = context.fn(...args)
delete context.fn
return r
}
apply
Подобно методу вызова, метод вызова принимает список параметров, а метод применения принимает массив из нескольких параметров.
Применение
Укажите это в функции на первый переданный параметр, а второй параметр является массивом.
function fun() {
console.log(this.name, arguments);
}
let obj = {
name: 'clying'
}
fun.apply(obj, [22, 1])
// clying Arguments(2) [22, 1]
выполнить
Реализуйте метод применения myapply самостоятельно. Метод реализации аналогичен вызову, но при получении параметров вы можете использовать args в качестве второго передаваемого параметра. Непосредственно оцените, не передан ли второй параметр, выполните функцию напрямую; в противном случае используйте eval для выполнения функции.
Function.prototype.myapply = function (context, args) {
context = (context == null || context == undefined) ? window : new Object(context)
context.fn = this
if(!args) return context.fn()
let r = eval('context.fn('+args+')')
delete context.fn
return r
}
bind
Метод bind() создает новую функцию, которая не выполняется автоматически, вам нужно вызвать bind() вручную. this этой новой функции указывается как первый параметр bind(), а остальные параметры будут использоваться как параметры новой функции для использования при вызове.
Применение
Привяжите obj к this функции fun, и функция fun сможет использовать свойства внутри obj и переданные переменные.
function fun() {
console.log(this.name, arguments);
}
let obj = {
name: 'clying'
}
let b = fun.bind(obj,2)
b(3)
// clying Arguments(2) [2, 3]
Кроме того, функция, связанная методом связывания, также может создать новый экземпляр, но в настоящее время это изменится.
Обновленная версия — Использование свойств прототипа:
function fun() {
console.log(this.name, arguments);
}
let obj = {
name: 'clying'
}
fun.prototype.age = 23
let b = fun.bind(obj, 3)
let instance = new b(4)
console.log(instance.age);
//undefined Arguments(2) [3, 4]
// 23
выполнить
Базовая версия:
Реализация привязки может быть основана на вызове и применении.
Поскольку привязка не выполняется немедленно, пользователь может сделать это вручную, вернув функцию. Используйте call или apply для передачи указанного объекта this и параметров в возвращаемую функцию.
применить привязку инструментов
Function.prototype.mybind = function (context) {
let that = this
let bindargs = Array.prototype.slice.call(arguments, 1)
return function () {
let args = Array.prototype.slice.call(arguments)
return that.apply(context, bindargs.concat(args))
}
}
Использование метода применения в основном предназначено для получения и обработки параметров, переданных привязкой, и параметров, переданных пользовательской исполнительной функцией. Используйте метод slice метода прототипа Array для перехвата необходимых параметров.
При получении параметров, переданных с помощью привязки, вам нужно начать перехват со второго параметра, поэтому начальная позиция равна 1.
вызов реализует привязку
Function.prototype.mybind = function (context, ...args1) {
let that = this
return function (...args2) {
return that.call(context, ...args1, ...args2)
}
}
Реализация вызова может напрямую объединять параметры метода вызова.
Обновленная версия:
Помимо того, что привязка может изменить эту точку, пользователь может передавать параметры после привязки или передавать параметры при выполнении пользователем. Вы также можете заставить функцию выполнения выполнить новую операцию.
Когда связанная функция используется для создания значения, изначально предоставленное this игнорируется. Однако предоставленный список параметров по-прежнему вставляется перед списком параметров при вызове конструктора.
apply
Function.prototype.mybind = function (context) {
let that = this
let bindargs = Array.prototype.slice.call(arguments, 1)
function fBind() {
let args = Array.prototype.slice.call(arguments)
// 如果使用的是new,那么this会指向fBind实例,this作为当前实例传入 不是的话,使用context上下文对象
return that.apply(this instanceof fBind ? this : context, bindargs.concat(args))
}
return fBind
}
При использовании оператора new обратите внимание на необходимость изменения указателя this.Если он новый, то this указывает на экземпляр.Если new не используется, он указывает на первый параметр, переданный в данный момент привязкой.
Кроме того, это также включает в себя исходную функцию, которая может добавлять свои собственные атрибуты метода. Если вы хотите иметь возможность использовать собственные методы прототипа fun, вам также необходимо использоватьfBind.prototype = this.prototype
, чтобы добиться совместного использования прототипа. Но для совместного использования значения свойства ссылочного типа его нельзя изменить без изменения других экземпляров (изменяется метод прототипа или свойство, все ссылки будут изменены).
Function.prototype.mybind = function (context) {
let that = this
let args = Array.prototype.slice.call(arguments, 1)
function fBind() { // 执行bind函数
let bindargs = Array.prototype.slice.call(arguments)
return that.apply(this instanceof fBind ? this : context, args.concat(bindargs))
}
function Fn(){} // 两个类的原型并未公用,而是通过原型链的方式找到该原型方法
Fn.prototype = this.prototype
fBind.prototype = new Fn()
return fBind
}
В приведенной выше ситуации вы можете использовать форму промежуточного программного обеспечения функции, чтобы использовать цепочку прототипов для поиска исходного метода или свойства прототипа функции.
call
Разница между call и apply только в параметрах обработки, а остальные аналогичны.
Function.prototype.mybind = function (context, ...args1) {
let that = this
function fBind(...args2) {
return that.call(this instanceof fBind ? this : context, ...args1, ...args2)
}
function Fn() { }
Fn.prototype = this.prototype
fBind.prototype = new Fn()
return fBind
}