Определить, является ли функция JavaScript связанной функцией
Есть ли способ определить, является ли функция JavaScript связанной функцией?
Пример:
var obj = {
x:1
};
function printX() {
document.write(this.x);
}
function takesACallback(cb) {
// how can one determine if this is a bounded function
// not just a function?
if (typeof cb === 'function') {
cb();
}
}
takesACallback(printX.bind(obj)); // 1
takesACallback(printX); // undefined
Ответы
Ответ 1
Обе связанные функции и функции стрелки не имеют свойства prototype
:
typeof (function() {}).prototype // 'object' as usual
typeof (function() {}).bind(null).prototype // 'undefined'!
typeof (() => {}).prototype // 'undefined'!
Это не на 100% безопасно, поскольку вы все еще можете вручную назначить это свойство (хотя это было бы странно).
Таким образом, простой способ проверить связывание будет следующим:
// ES5
function isBindable(func) {
return func.hasOwnProperty('prototype');
}
// ES6
const isBindable = func => func.hasOwnProperty('prototype');
Использование:
isBindable(function () {}); // true
isBindable(() => {}); // false
isBindable(
(function () {}).bind(null)
); // false
Таким образом вы можете убедиться, что переданная функция может иметь дело с динамическим this
.
Вот пример использования, для которого это не выполнено:
const arrowFunc = () => {};
arrowFunc.prototype = 42;
isBindable(arrowFunc); // true :(
Интересно, что в то время как связанные функции не имеют свойства prototype
, они все равно могут использоваться как конструкторы (с new
):
var Animal = function(name) {
this.name = name;
};
Animal.prototype.getName = function() {
return this.name;
};
var squirrel = new Animal('squirrel');
console.log(squirrel.getName()); // prints "squirrel"
var MutatedAnimal = Animal.bind({}); // Radiation :)
console.log(MutatedAnimal.hasOwnProperty('prototype')); // prints "false"
var mutatedSquirrel = new MutatedAnimal('squirrel with two heads');
console.log(mutatedSquirrel.getName()); // prints "squirrel with two heads"
В этом случае вместо этого используется исходная функция prototype
(Animal
).
Смотрите JS Bin, код и ссылка любезно предоставлены Дмитрием Павлутиным.
Это, конечно, не будет работать со стрелочными функциями, поскольку они не могут использоваться как конструкторы.
К сожалению, я не знаю, есть ли способ отличить связанную функцию (используемую как конструктор) от функции стрелки (не используемой в качестве конструктора) без try
с помощью new
и проверки, если она throws (new (() => {})
вызывает ошибку "не является конструктором" ).
Ответ 2
В средах, поддерживающих ES6, вы можете проверить, начинается ли имя функции с "bound "
(слово "bound", за которым следует пробел).
Из спецификации:
[...]
15. Выполните SetFunctionName (F, targetName, "bound" ).
Конечно, это может привести к ложным срабатываниям, если имя функции было изменено вручную.
Ответ 3
Можно переопределить существующее связывание прототипа, привязанные функции тегов.
Простое решение. Это, скорее всего, убьет некоторые оптимизации в V8 (и, возможно, в других средах) из-за скрытых классов.
(function (bind) {
Object.defineProperties(Function.prototype, {
'bind': {
value: function (context) {
var newf = bind.apply(this, arguments);
newf.context = context;
return newf;
}
},
'isBound': {
value: function () {
return this.hasOwnProperty('context');
}
}
});
}(Function.prototype.bind));
Ответ 4
Нет.
bind
- это просто функция, которая возвращает новую функцию. Нет ничего особенного в этом вопросе, который указывает, что bind
использовался для его создания.
Ответ 5
На основе предыдущих ответов я создаю функцию для определения:
function isBoundFunction(func) {
if(typeof func.prototype === 'object') return false
try {
new func()
}
catch(e) {
return false
}
return true
}
Эта функция определяет три типа функций: 1. оригинальная функция, прототипом которой является объект, 2. функция стрелки, которая не может использоваться как конструктор, 3. связанная функция.
Ответ 6
Вам нужно будет написать свою собственную функцию bind
на прототипе. Эта функция создаст индекс того, что было связано.
Затем у вас может быть другая функция для выполнения поиска по объекту, в котором хранится этот индекс.
Ответ 7
Я новичок здесь и не имею достаточной репутации для публикации комментариев, просто ответов. Извините, но это не отвечает OP, потому что я понятия не имею, как это сделать. Хотел бы я этого сделать.
Однако очень общие ответы, связанные с отсутствующим свойством prototype
, не будут работать. Методы класса, определения методов в объектах и функции async
также не имеют свойств prototype
, но они не связаны естественным образом.
Кроме того, имейте в виду, что по-прежнему можно вручную привязать функцию закрытием, что не позволит выявить ее связанные состояния:
const bind=(fn,obj)=>{
return (...args)=>{
return fn.apply(obj,args)
}
}