Предисловие:
Первоначально опубликовано мнойблог, честно говоря, функция привязки появлялась n раз в вопросах интервью, которыми делились другие в крупных сообществах за последние шесть месяцев.На этот раз я планирую подробно изучить ее.
Сначала давайте посмотрим, что описывает mdn для функции связывания.
грамматика
fun.bind(thisArg[, arg1[, arg2[, ...]]])
параметр
- thisArgКогда связанная функция вызывается, параметр будет использоваться как указатель this исходной функции при ее запуске. Этот параметр не действует, когда связанная функция вызывается с оператором new.
- arg1, arg2, ...Когда связанная функция вызывается, эти параметры будут переданы связанному методу перед фактическими параметрами.
возвращаемое значение
Возвращает копию исходной функции, модифицированной указанным значением this и параметрами инициализации.
При выполнении кода new Foo(...) происходит следующее: 1. Создается новый объект, наследуемый от Foo.prototype. 2. Вызвать конструктор Foo с указанными параметрами и привязать его к вновь созданному объекту. new Foo эквивалентен new Foo(), т.е. список аргументов не указывается и Foo вызывается без каких-либо аргументов. 3. Объект, возвращаемый конструктором, является результатом нового выражения. Если конструктор явно не возвращает объект, используется объект, созданный на шаге 1. (В обычных обстоятельствах конструктор не возвращает значение, но пользователь может активно возвращать объект, чтобы покрыть обычные этапы создания объекта) Если вы не понимаете это предложение, это не имеет значения после прочтения следующего код, вам будет ясно
function Foo(){
}
下面代码就是执行new Foo()时的简单实现
let obj = {};
obj.__proto__ = Foo.prototype
return Foo.call(obj)
Для полной реализации новинки можно обратиться к этому великому богублог
выполнить
Версия Beggar, нельзя предварительно заполнить параметры, только изменить этот пункт при выполнении
let obj = {
ll: 'seve'
};
Function.prototype.bind = function(that) {
var self = this;
return function() {
return self.apply(that, arguments);
};
};
let func0 = function(a, b, c) {
console.log(this.ll);
console.log([a, b, c]);
}.bind(obj, 1, 2);
func0(3); // seve
// [ 3, undefined, undefined ] 发现1,2并没有填入
Нищенская версия слишком низкая, верно, поэтому мы продолжаем улучшать
расширенная версия es6
es6 предоставляет структурные операторы, которые можно легко использовать для реализации связывания.
Function.prototype.bind = function(that, ...argv) {
if (typeof this !== 'function') {
throw new TypeError(`${this} is not callable`);
}
// 保存原函数
let self = this;
// 获取bind后函数传入的参数
return function(...argu) {
return self.apply(that, [...argv, ...argu]);
};
};
let func1 = function(a, b, c) {
console.log(this.ll);
console.log([a, b, c]);
}.bind(obj, 1, 2);
func1(3); // seve
// [ 1, 2, 3 ]
Реализация версии es6 очень проста, верно, но интервьюер сказал, что наша операционная среда es5.В это время вы втайне счастливы, бейбл Дафа хорош, но вы не должны говорить, что есть бабель, потому что интервьюер вряд ли намерение будет спрашивать, как вы конвертируете es6 в es5, но изучите другие свои знания, например, как следующие массивы классов преобразуются в реальные массивы.
расширенная версия es5
Function.prototype.bind = function() {
if (typeof this !== 'function') {
throw new TypeError(`${this} is not callable`);
}
var self = this;
var slice = [].slice;
// 模拟es6的解构效果
var that = arguments[0];
var argv = slice.call(arguments, 1);
return function() {
// slice.call(arguments, 0)将类数组转换为数组
return self.apply(that, argv.concat(slice.call(arguments, 0)));
};
};
let func2 = function(a, b, c) {
console.log(this.ll);
console.log([a, b, c]);
}.bind(obj, 1, 2);
func2(3); // seve
// [ 1, 2, 3 ]
Конечно, на данный момент для большинства интервью этот код является хорошим ответом, но для того, чтобы произвести лучшее впечатление на интервьюера, нам нужно перейти к окончательной версии Реализуйте функцию полной привязки, чтобы вы могли поднять волну с интервьюером
Окончательный версия
Чтобы использовать новый оператор, функция после привязки не теряет этого. Нам нужно смонтировать прототип функции перед привязкой к прототипу функции после привязки
Однако для того, чтобы модифицировать прототип функции после привязки, не затрагивая прототип до привязки, во всем виноват объект. . . Прямое назначение просто назначает адрес объекта в куче Поэтому необходимо наследовать прототип функции после бинда, а не назначать его напрямую.Я видел в некоторых местах, что Object.crate может добиться такого же эффекта.Если вам интересно, вы можете узнать об этом, но я попробовал сам и обнаружил, что эффект не Нет, это указывает на неправильное во время новой операции (возможно, я использую неправильную позу)
По эффекту прямого присвоения
Function.prototype.bind = function(that, ...argv) {
if (typeof this !== 'function') {
throw new TypeError(`${this} is not callable`);
}
// 保存原函数
let self = this;
let func = function() {};
// 获取bind后函数传入的参数
let bindfunc = function(...arguments) {
return self.apply(this instanceof func ? this : that, [...argv, ...arguments]);
};
// 把this原型上的东西挂载到func原型上面
// func.prototype = self.prototype;
// 为了避免func影响到this,通过new 操作符进行复制原型上面的东西
bindfunc.prototype = self.prototype;
return bindfunc;
};
function bar() {
console.log(this.ll);
console.log([...arguments]);
}
let func3 = bar.bind(null);
func3.prototype.value = 1;
console.log(bar.prototype.value) // 1 可以看到bind后的原型对bind前的原型产生的同样的影响
Эффект уступки через наследование
Function.prototype.bind = function(that, ...argv) {
if (typeof this !== 'function') {
throw new TypeError(`${this} is not callable`);
}
// 保存原函数
let self = this;
let func = function() {};
// 获取bind后函数传入的参数
let bindfunc = function(...argu) {
return self.apply(this instanceof func ? this : that, [...argv, ...argu]);
};
// 把this原型上的东西挂载到func原型上面
func.prototype = self.prototype;
// 为了避免func影响到this,通过new 操作符进行复制原型上面的东西
bindfunc.prototype = new func();
return bindfunc;
};
function bar() {
console.log(this.ll);
console.log([...arguments]);
}
let func3 = bar.bind(null);
func3.prototype.value = 1;
console.log(bar.prototype.value) // undefined 可以看到bind后的原型对bind前的原型不产生影响
func3(5); // seve
// [ 5 ]
new func3(5); // undefined
// [ 5 ]
Если в приведенном выше коде или выражении есть какие-либо ошибки или неточности, пожалуйста, укажите на них или обсудите их в области комментариев.Если вы считаете, что моя статья полезна, вы можете подписаться или пометить меня, чтобы поддержать меня.блог
В следующей серии статей я планирую написать о реализации фреймворка koa.В первой статье я познакомлю вас с эффектом и реализацией Object.create