JS: Rename function name
Мутируем иммутабельное, переписываем имена функций
Многие, кто работает с JS, знают, что все есть объект в JS, а что не объект, то с этим можно работать как с объектом. Поэтому мы можем в JS вытворять такие штуки:
30..toString(16) // = 1e
"abc".length // = 3
и так далее. Но при этом надо понимать, что не все такие свойства можно изменять. Некоторые из них readonly. У кого что и как работает — надо знать (изучая документацию или опытным путем). Поэтому может быть непонятно, как так выходит, что:
let a = [1,2,3];
let s = "123";a.length = 2;
a; // [1,2]s.length = 2; s; // TypeError: Cannot assign to read only property ‘length’ of string ‘123’
Хотя было бы логично же сделать поведение как и в массивах, да?
И вот одна из интересных задач, которую можно встретить не только на собеседовании, но и в жизни:
function foo(){}
var foo = function (){};
var foo = function bar(){};
Очень интересный, но уже стар как мир, вопрос: в чем различия?
Надо ли рассказывать в чем? Если надо раскрыть задачи такого плана — напишите в комментариях. Сейчас в 2х словах для тех, кто только изучает JS или пришел из других языков:
function foo(){}
// всплывет, доступна перед объявлением по foo(),
// function.name = ‘foo’,
// внутри тела доступна по foo() для рекурсииvar foo = function (){};
// всплывет var foo, доступна только после присвоения по foo(),
// function.name = ‘foo’,
// внутри тела доступна по foo() для рекурсииvar foo = function bar(){}; // всплывет var foo, доступна только после присвоения по foo(), // function.name = ‘bar’, // внутри тела функции доступна как по bar() // так и по foo() для рекурсивного вызова
Очень кратко. Надо будет подробнее — распишу или ищите в документации. Так вот, к чему этот вопрос? Видите вот этот загадочный function.name
?
Для тех, кто не в курсе, имя функции можно получить через свойство name
:
function foo(){}
console.log( foo.name ) // ‘foo’var foo = function(){}
console.log( foo.name ) // ‘foo’var foo = function bar(){} console.log( foo.name ) // ‘bar’
Ну и что? Зачем это нам и где это используется?
А это используется в том же console.log()
, вы можете просто написать:
function foo(){}
console.log( foo ) // ‘[Function: foo]’var foo = function(){}
console.log( foo ) // ‘[Function: foo]’var foo = function bar(){} console.log( foo ) // ‘[Function: bar]’
Вы обратили внимание что в нашем случае вроде бы анонимная функция всеравно имеет имя? Но что если:
function some(fn) {
console.log(fn) // ‘[Function]’
console.log(fn.name) // ”
}some(function(){ // some callback });
И вот у вас весь стек трейс из таких вот анонимных [Function]
. Что это? Откуда? И приходится гадать и распутывать. Что можно сделать, чтобы добавить имя? Все верно, присвоить в переменную или дать имя:
function some(fn) {
console.log(fn) // ‘[Function: foo]’
console.log(fn.name) // ‘foo’
}var foo; some(foo = function(){ // some callback });
Это экзотика какая-то, кто так пишет, скажете вы. Ну да, лучше писать так:
some(function foo(){
// some callback
});
И я полностью согласен. Подписывать (давать имена) функции — это полезно для отладки.
Задачка
Как-то, не помню уже как и из какой задачи, возникла идея, в результате которой родился вопрос (возможно даже встретить его на собеседованиях):
const foo = function bar() {}
console.log(foo.name)foo.name = ‘lol’; console.log(foo.name) // ???
Что будет? Можем переименовать функцию?
Ответ:
foo.name = ‘bar’
foo.name = ‘lol’
—TypeError: Cannot assign to read only property ‘name’ of function ‘function bar() {}’
Выходит что нельзя? А если прям ну очень надо?
Ну если “ну очень надо”, то можно. В мире нет ничего невозможного :)
const foo = function bar() {}
console.log(foo.name)Reflect.defineProperty(foo,‘name’,{value:‘lol’});console.log(foo.name) // ‘lol’ console.log(foo) // [Function: lol]
Вот таким вот нехитрым способом можно переименовать функцию. Доступна ли она по имени lol()
? Нет, недоступна. Но в логах вы будете видеть это имя. Таким образом можно генерить программно имена колбэков и подписывать их.
Если недоступен Reflect, то используйте Object:
Object.defineProperty(foo,‘name’,{value:‘lol’});
Смысл тот же.
P.S.:
Я удивлен, как люди засирают NPM пакетами из 2–3 строк. Какие же это, матьево, пакеты? Я понимаю прелесть переиспользования логики, но если бы они хотя бы были сгруппированы в пакеты. Тот же leftpad, который вызвал пару лет назад бурю негодования сообщества и все кричали что мы разучились программировать, имеет хотя бы 47 строк кода. Но когда я наткнулся на этот “пакет”: https://github.com/sindresorhus/rename-fn
Я был в шоке… А какой следующий шаг? Экспортироватьть нативные функции? Этот модуль для переименования функции состоит из 1 строчки:
module.exports = (fn, name) => Object.defineProperty(fn, ‘name’, {value: name, configurable: true});
Друзья!.. Коллеги!.. Ребята! Не делайте так! Если хочется поделиться с миром — напишите статью, пост в твиттере, но не пульте в NPM такие вещи. Не надо вам в зависимости такой пакет. Вообще не нужна функция, используйте сразу вызов Reflect.defineProperty()
с нужными параметрами на нужном объекте и не заворачивайте это все в функции. Зачем?
Geekjob.ru — анонимный поиск работы без палева где можно найти новую работу без проблем на текущем месте. Только для IT, никакого “левого” стафа. Только релевантные предложения.
New.HR — место где помогают найти работу мечты. Работаем только с отборными вакансиями в сфере IT & Digital. Помогаем кандидатам найти работу по душе. Работаем с кандидатами, которые не ищут работу!
Подписывайтесь на Телеграм канал. Следить за обновлениями и прочими материалами от меня можно именно там: @prowebit . В этом канале публикую различные новости и мысли, которых может не быть в этом блоге. Подписывайтесь!