company_banner

Малоизвестные возможности JavaScript

Original author: Viral Shah
  • Translation
JavaScript часто называют самым простым языком для новичков, в программировании на котором сложнее всего достичь мастерства. Автор материала, перевод которого мы публикуем, говорит, что не может не согласиться с этим утверждением. Всё дело в том, что JS — это по-настоящему старый и по-настоящему гибкий язык. Он полон таинственных синтаксических конструкций и устаревших возможностей, всё ещё им поддерживаемых.

image

Сегодня мы поговорим о малоизвестных возможностях JavaScript и о вариантах их практического применения.

JavaScript — это всегда что-то новое


Я работаю с JavaScript уже много лет и мне постоянно попадается что-то такое, о существовании чего я и не подозревал. Здесь я попытался перечислить подобные малоизвестные возможности языка. В строгом режиме некоторые из них работать не будут, но в обычном режиме они представляют собой совершенно правильные образцы JS-кода. Надо отметить, что я не берусь советовать читателям брать всё это на вооружение. Хотя то, о чём пойдёт речь, кажется мне весьма интересным, вы, начав всем этим пользоваться, если вы работаете в команде, можете, мягко говоря, удивить коллег.

→ Код, который мы тут будем обсуждать, можно найти здесь

Обратите внимание на то, что я не включил сюда такие вещи, как поднятие переменных, замыкания, прокси-объекты, прототипное наследование, async/await, генераторы и прочее подобное. Хотя эти особенности языка и можно отнести к сложным для понимания, малоизвестными они не являются.

Оператор void


В JavaScript имеется унарный оператор void. Возможно, вы сталкивались с ним в виде void(0) или void 0. Его единственная цель — вычислить выражение, находящееся справа от него и вернуть undefined. 0 тут используется просто потому что так принято, хотя это и необязательно, и тут можно использовать любое правильное выражение. Правда, этот оператор в любом случае вернёт undefined.

// Оператор void
void 0                  // undefined
void (0)                // undefined
void 'abc'              // undefined
void {}                 // undefined
void (1 === 1)          // undefined
void (1 !== 1)          // undefined
void anyfunction()      // undefined

Зачем добавлять в язык особое ключевое слово, служащее для возврата undefined, если можно просто воспользоваться стандартным значением undefined? Не правда ли, тут ощущается некоторая избыточность?

Как оказалось, до появления стандарта ES5 в большинстве браузеров стандартному значению undefined можно было присвоить новое значение. Скажем, можно было успешно выполнить такую команду: undefined = "abc". В результате значение undefined могло оказаться совсем не тем, чем оно должно быть. В те времена использование void позволяло обеспечить уверенность в использовании именно настоящего undefined.

Скобки при вызове конструкторов необязательны


Скобки, которые добавляют после имени класса, вызывая конструктор, совершенно необязательны (если только конструктору не надо передавать аргументы).

В следующем примере наличие или отсутствие скобок на правильность работы программы не влияет.

// Вызов конструктора со скобками
const date = new Date()
const month = new Date().getMonth()
const myInstance = new MyClass()

// Вызов конструктора без скобок
const date = new Date
const month = (new Date).getMonth()
const myInstance = new MyClass

Скобки при работе с IIFE можно не использовать


Синтаксис IIFE всегда казался мне странноватым. Зачем тут все эти скобки?

Как оказалось, скобки нужны лишь для того, чтобы сообщить JavaScript-парсеру о том, что некий код представляет собой функциональное выражение, а не неправильную попытку объявления функции. Знание этого факта позволяет понять то, что есть множество способов избавиться от скобок, в которые заключают IIFE, и при этом написать работающий код.

// IIFE
(function () {
  console.log('Normal IIFE called')
})()
// Normal IIFE called

void function () {
  console.log('Cool IIFE called')
}()
// Cool IIFE called

Здесь оператор void сообщает парсеру о том, что следующий за ним код является функциональным выражением. Это даёт возможность избавиться от скобок вокруг объявления функции. И, кстати, тут можно использовать любой унарный оператор (void, +, !, -, и так далее), и код останется рабочим. Разве это не замечательно?

Однако если вы — внимательный читатель, то вы можете задаться вопросом о том, что унарный оператор воздействует на результат, возвращаемый из IIFE. На самом деле, так оно и есть. Но хорошо то, что если вам нужен результат выполнения IIFE, который вы, например, сохраняете в какой-нибудь переменной, тогда и скобки вокруг IIFE вам не нужны. Вот пример.

// IIFE, возвращающие некие значения

let result = (function () {
  // ... какой-то код
  return 'Victor Sully'
})()

console.log(result) // Victor Sully

let result1 = function () {
  // ... какой-то код
  return 'Nathan Drake'
}()
console.log(result1) // Nathan Drake

Скобки вокруг первого IIFE лишь улучшают читабельность кода, не влияя на его работу.

Если вы хотите лучше разобраться с IIFE — взгляните на этот материал.

Конструкция with


Знаете ли вы о том, что в JavaScript имеется конструкция with, поддерживающая блоки выражений? Выглядит это так:

with (object)
   statement 
// для того чтобы выполнить несколько команд
with (object) {
   statement
   statement
   ...
}

Конструкция with добавляет все свойства переданного ей объекта в цепочку областей видимости, используемую при выполнении команд.

// пример блока выражения with
const person = {
  firstname: 'Nathan',
  lastname: 'Drake',
  age: 29
}

with (person) {
  console.log(`${firstname} ${lastname} is ${age} years old`)
}
// Nathan Drake is 29 years old

Может показаться, что with — это замечательный инструмент. Похоже, что он даже лучше чем новые возможности JS по деструктурированию объектов, но на самом деле это не так.

Конструкция with признана устаревшей и пользоваться ей не рекомендуется. В строгом режиме её использование запрещено. Оказывается, блоки with вызывают проблемы с производительностью и безопасностью.

Конструктор Function


Использование ключевого слова function — это не единственный способ определить новую функцию. Определять функции можно динамически, используя конструктор Function и оператор new. Вот как это выглядит.

// Конструктор Function
const multiply = new Function('x', 'y', 'return x*y')
multiply(2,3)
// 6

Последний аргумент, передаваемый конструктору, представляет собой строку с кодом функции. Два других аргумента — это параметры функции.

Интересно отметить, что конструктор Function является «родителем» всех конструкторов в JavaScript. Даже конструктор Object — это конструктор Function. И собственный конструктор Function — это тоже Function. В результате, вызов вида object.constructor.constructor..., выполненный для любого JS-объекта достаточное количество раз вернёт в итоге конструктор Function.

Свойства функций


Все мы знаем, что функции в JavaScript являются объектами первого класса. Следовательно, никто не мешает нам добавлять в функции новые свойства. Это совершенно нормально, но используется подобное редко.

Когда это может понадобиться?

На самом деле, есть несколько ситуаций, в которых эта возможность функций может оказаться кстати. Рассмотрим их.

▍Настраиваемые функции


Предположим, у нас имеется функция greet(). Нам нужно, чтобы она выводила бы разные приветственные сообщения в зависимости от применяемых региональных настроек. Эти настройки могут храниться в некоей внешней по отношению к функции переменной. Кроме того, в функции может быть свойство, определяющие эти настройки, в частности — настройки языка пользователя. Мы воспользуемся вторым подходом.

// Свойства функций, задаваемые разработчиком
function greet () {
  if (greet.locale === 'fr') {
    console.log('Bonjour!')
  } else if (greet.locale === 'es') {
    console.log('Hola!')
  } else {
    console.log('Hello!')
  }
}

greet()
// Hello!
greet.locale = 'fr'
greet()
// Bonjour!

▍Функции со статическими переменными


Вот ещё один похожий пример. Предположим, нам надо реализовать некий генератор, который выдаёт последовательность упорядоченных чисел. Обычно в подобных ситуациях, для того, чтобы хранить сведения о последнем сгенерированном числе, используются статические переменные-счётчики в классах или IIFE. При таком подходе мы ограничиваем доступ к счётчику и предотвращаем загрязнение глобальной области видимости дополнительными переменными.

Но что если нам нужна гибкость, если требуется читать или даже модифицировать значение подобного счётчика и при этом не засорять глобальную область видимости?

Конечно, можно создать класс с соответствующей переменной и с методами, позволяющими с ней работать. Или можно не утруждать себя подобными делами и просто использовать свойства функций.

// Свойства функций, задаваемые разработчиком
function generateNumber () {
  if (!generateNumber.counter) {
    generateNumber.counter = 0
  }
  return ++generateNumber.counter
}

console.log(generateNumber())
// 1
console.log(generateNumber())
// 2
console.log('current counter value: ', generateNumber.counter)
// current counter value: 2
generateNumber.counter = 10
console.log('current counter value: ', generateNumber.counter)
// current counter value: 10
console.log(generateNumber())
// 11

Свойства объекта arguments


Уверен, большинство из вас знают о том, что в функциях имеется объект arguments. Это — массивоподобный объект, доступный внутри всех функций (за исключением стрелочных, у которых нет собственного объекта arguments). Он содержит список аргументов, переданных функции при её вызове. Кроме того, в нём имеются некоторые интересные свойства:

  • arguments.callee содержит ссылку на текущую функцию.
  • arguments.caller содержит ссылку на функцию которая вызвала текущую функцию.

Рассмотрим пример.

// свойства callee и caller объекта arguments
const myFunction = function () {
  console.log('Current function: ', arguments.callee.name)
  console.log('Invoked by function: ', arguments.callee.caller.name)
}

void function main () {
  myFunction()
} ()

// Current function: myFunction
// Invoked by function: main

Стандарт ES5 запрещает использование свойств callee и caller в строгом режиме, но они всё ещё широко встречаются во многих скомпилированных в JavaScript текстах программ, например — в библиотеках. Поэтому о них полезно знать.

Тегированные шаблонные литералы


Наверняка вы, если имеете хоть какое-то отношение к программированию на JavaScript, слышали о шаблонных литералах. Шаблонные литералы — это одно из многих замечательных новшеств стандарта ES6. Однако знаете ли вы о тегированных шаблонных литералах?

// Обычный шаблонный литерал
`Hello ${username}!`

// Тегированный шаблонный литерал
myTag`Hello ${username}!`

Тегированные шаблонные литералы позволяют разработчику управлять тем, как шаблонный литерал превращается в строку. Делается это путём использования особых тегов. Тег — это всего лишь имя функции-парсера, которая получает массив строк и значений, интерпретированных строковым шаблоном. При использовании теговой функции ожидается, что она вернёт готовую строку.

В следующем примере наш тег — highlight, интерпретирует данные шаблонного литерала и внедряет эти данные в готовую строку, помещая их в HTML-тег <mark>, чтобы выделить их при выводе такого текста на веб-страницу.

// Объявление теговой функции
function highlight(strings, ...values) {
  // здесь i - это итератор для массива строк
  let result = ''
  strings.forEach((str, i) => {
    result += str
    if (values[i]) {
      result += `<mark>${values[i]}</mark>`
    }
  })
  return result
}

const author = 'Henry Avery'
const statement = `I am a man of fortune & I must seek my fortune`
const quote = highlight`${author} once said, ${statement}`

// <mark>Henry Avery</mark> once said, <mark>I am a man of fortune
// & I must seek my fortune</mark>

Интересные способы использования этой возможности можно найти во многих библиотеках. Вот несколько примеров:

  • styled-components — для использования в React-приложениях.
  • es2015-i18n-tag — для перевода и интернационализации проектов.
  • chalk — для вывода в консоль разноцветных сообщений.

Геттеры и сеттеры в стандарте ES5


JavaScript-объекты, по большей части, довольно просты. Предположим, у нас имеется объект user, и мы пытаемся обратиться к его свойству age, используя конструкцию user.age. При таком подходе, если это свойство определено, мы получим его значение, а если не определено — получим undefined. Всё очень просто.

Но работа со свойствами вовсе не должна быть настолько примитивной. JS-объекты реализуют концепцию геттеров и сеттеров. Вместо того чтобы напрямую возвращать значение некоего свойства объекта, мы можем написать собственную функцию-геттер, которая возвращает то, что мы сочтём нужным. То же самое касается и записи в свойства новых значений с использованием функций-сеттеров.

Геттеры и сеттеры позволяют реализовывать продвинутые схемы работы со свойствами. При чтении или записи свойств можно пользоваться концепциями виртуальных полей, можно проверять значения полей, при их записи или чтении могут происходить некие полезные побочные эффекты.

// Геттеры и сеттеры
const user = {
  firstName: 'Nathan',
  lastName: 'Drake',

  // fullname - это виртуальное поле
  get fullName() {
    return this.firstName + ' ' + this.lastName
  },

  // проверка возраста перед записью значения
  set age(value) {
    if (isNaN(value)) throw Error('Age has to be a number')
    this._age = Number(value)
  },
  get age() {
    return this._age
  }
}

console.log(user.fullName) // Nathan Drake
user.firstName = 'Francis'
console.log(user.fullName) // Francis Drake
user.age = '29'
console.log(user.age) // 29
// user.age = 'invalid text' // Error: Age has to be a number

Геттеры и сеттеры не относятся к новшествам стандарта ES5. Они всегда присутствовали в языке. В ES5 лишь добавлены удобные синтаксические средства для работы с ними. Подробности о геттерах и сеттерах можно почитать здесь.

Среди примеров использования геттеров можно отметить популярную Node.js-библиотеку Colors.

Эта библиотека расширяет класс String и добавляет в него множество методов-геттеров. Это позволяет преобразовывать строку в её «раскрашенный» вариант для того, чтобы эту строку потом использовать при логировании. Делается это путём работы со свойствами строки.

Оператор «запятая»


В JS есть оператор «запятая». Он позволяет записывать в одной строке несколько выражений, разделённых запятой, и возвращать результат вычисления последнего выражения. Вот как выглядят подобные конструкции.

let result = expression1, expression2,... expressionN

Здесь будут вычислены значения всех выражений, после чего в переменную result попадёт значение выражения expressionN.

Вполне возможно, что вы уже пользовались оператором «запятая» в циклах for.

for (var a = 0, b = 10; a <= 10; a++, b--)

Иногда этот оператор оказывается очень кстати при необходимости записи нескольких выражений в одной строке.

function getNextValue() {
    return counter++, console.log(counter), counter
}

Он может пригодиться и при конструировании небольших стрелочных функций.

const getSquare = x => (console.log (x), x * x)

Оператор «плюс»


Если вам нужно быстро превратить строку в число — вам пригодится оператор «плюс». Он способен работать с самыми разными числами, а не только, как может показаться, с положительными. Речь идёт об отрицательных, восьмеричных, шестнадцатеричных числах, и о числах в экспоненциальной записи. Более того, он способен преобразовывать во временные метки объекты Date и объекты библиотеки Moment.js.

// Оператор "плюс"
+'9.11'          // 9.11
+'-4'            // -4
+'0xFF'          // 255
+true            // 1
+'123e-5'        // 0.00123
+false           // 0
+null            // 0
+'Infinity'      // Infinity
+'1,234'         // NaN
+new Date      // 1542975502981 (временная метка)
+momentObject    // 1542975502981 (временная метка)

Двойной восклицательный знак


Надо отметить, что то, что иногда называют «оператор двойной восклицательный знак» (Bang Bang или Double Bang), на самом деле, не является оператором. Это — оператор «логическое НЕ», или оператор логического отрицания, выглядящий как восклицательный знак, повторённый два раза. Двойной восклицательный знак хорош тем, что позволяет конвертировать любое выражение в логическое значение. Если выражение, с точки зрения JS, истинно — после обработки его двойным восклицательным знаком будет возвращено true. В противном случае будет возвращено false.

// Применение двойного восклицательного знака

!!null            // false
!!undefined       // false
!!false           // false
!!true            // true
!!""              // false
!!"string"        // true
!!0               // false
!!1               // true
!!{}              // true
!![]              // true

Оператор побитового отрицания


Давайте посмотрим правде в глаза: никому нет дела до побитовых операторов. Я уж не говорю о том, чтобы ими пользовались. Однако оператору побитового отрицания можно найти применение во многих ситуациях.

Когда этот оператор применяется к числам, он преобразует их следующим образом: из числа N получается -(N+1). Такое выражение даёт 0 в том случае если N равно -1.

Эту возможность можно использовать с методом indexOf() при выполнении с его помощью проверки существования в массиве или в строке некоего элемента, так как этот метод, не находя элемент, возвращает -1.

// Оператор побитового отрицания и метод indexOf
let username = "Nathan Drake"

if (~username.indexOf("Drake")) {
  console.log('Access denied')
} else {
  console.log('Access granted')
}

Тут надо отметить, что в стандартах ES6 и ES7, соответственно, у строк и массивов, появился метод includes(). Он, определённо, куда удобнее для определения наличия элементов, чем использование оператора побитового отрицания и indexOf().

Именованные блоки


В JavaScript есть концепция меток, используя которую, можно назначать имена (метки) циклам. Затем можно использовать эти метки для обращения к соответствующему циклу при применении инструкций break или continue. Метки можно назначать и обычным блокам кода.

Циклы с метками удобно использовать при работе с вложенными циклами. Но их можно применять и для удобной организации кода в блоках или при создании блоков, выполнение кода в которых можно прерывать.

// Работа с метками
declarationBlock: {
  // этот подход можно использовать для группировки
  // логически связанных блоков кода
  var i, j
}


forLoop1: // Метка для первого цикла - "forLoop1"
for (i = 0; i < 3; i++) {      
   forLoop2: // Метка для второго цикла -  "forLoop2"
   for (j = 0; j < 3; j++) {   
      if (i === 1 && j === 1) {
         continue forLoop1
      }
      console.log('i = ' + i + ', j = ' + j)
   }
}
/*
i = 0, j = 0
i = 0, j = 1
i = 0, j = 2
i = 1, j = 0
i = 2, j = 0
i = 2, j = 1
i = 2, j = 2
*/

// выполнение кода в блоке прерывается
loopBlock4: {
  console.log('I will print')
  break loopBlock4
  console.log('I will not print')
}
// I will print

Обратите внимание на то, что, в отличие от некоторых других языков, в JS нет инструкции goto. В результате метки используются лишь с инструкциями break и continue.

Итоги


В этом материале мы поговорили о малоизвестных возможностях JavaScript, знание о которых пригодится любому JS-программисту, по меньшей мере, для того, чтобы быть готовым к встрече с чем-то необычным в чужом коде. Если вам тема «неизвестного JS» интересна — можете взглянуть на эту нашу публикацию.

Уважаемые читатели! Если вы знаете о каких-нибудь малоизвестных возможностях JS и видите варианты их практического применения — просим о них рассказать.

RUVDS.com
VDS/VPS-хостинг. Скидка 10% по коду HABR

Comments 42

    0
    а в чем разница между
    (function () {
      console.log('Normal IIFE called')
    })()
    и
    (function () {
      console.log('Not normal IIFE called')
    }())
      0
      Исключительно в порядке скобок.
        0
        Не, ну Крокфорд это уже комментировал:
        www.youtube.com/watch?v=eGArABpLy0k&t=1m10s
          0

          Так то, можно даже так


          !function () {
            console.log('Normal IIFE called')
          }();
          +function () {
            console.log('Normal IIFE called')
          }();
          ~function () {
            console.log('Normal IIFE called')
          }();

          Главное, чтобы JS понял, что это выражение а не конструкция. Для этого и применяются скобки, операторы. Например вот это блок/конструкция
          {}
          а вот это уже будет объект
          ({})

          +2
          Двойной восклицательный знак

          Он редко когда может пригодится в JS, т.к. в условиях и так идет преобразование в булевой тип. Возможно, только когда нужно явно передать булевое значение, например в JSON, или в переменную is*. Но странно, что вы его засунули в статью малоизвестных возможностей.


          Побитовые операторы нужно использовать по предназначению. Такие вот штуки


          if (~username.indexOf("Drake")) {
            console.log('Access denied')
          }

          очень сильно усложняют код. Это неявно, а неявное — плохо.


          Оператор «запятая»

          Тоже забудьте в продакшине. Неопытных разработчик не сразу скажет, чему тут будет равно а


          var a = (10 * 3, 
                   20 * 4, 
                   10 * 2)

          Неявная штукенция, в 99% случаев подходит лишь, когда стоит цель, написать минимум символов. Все равно минификатором все будет сжато.


          Все эти штуки из статьи, описываются почти в любом учебнике по JS. Думал будет что-то реально малоизвестное. Вы же вроде хотели доперевести You Dont Know JS, там реально полезный материал.

            0
            Тоже забудьте в продакшине.

            В трансдьюсерах это позволяет обойтись без; и return

            Неопытных разработчик не сразу скажет, чему тут будет равно а

            Вы так думаете? Мне казалось, что семантика запятой достаточно проста.
              0
              > очень сильно усложняют код. Это неявно, а неявное — плохо.

              Не согласен. Конструкция ~array.indexOf(x) // array.includes(x) легко запоминается и потом читается лучше чем != -1. Или там > -1? или >= 0?
                +2
                Когда-то сам стал причиной повсеместного использования ~array.indexOf(x) во многих проектах компании. Но это такая себе практика, так как это не каждый js-разработчик то поймет на-лету, не то что проходящий мимо джавист или питонист. Сейчас убежден, что корпоративный код должен быть максимально понятен любому разработчику без разницы на каком языке. И если язык предоставляет готовый метод у массива/коллекции (Array.prototype.includes, не IE) или имеет широко-распространенную либу (lodash), то не надо использовать бинарные операции и прочие хаки не по назначению. Читаемость кода достигается не запоминанием каких-то специфичных конструкций, а декларативностью и явным поведением.
                  0
                  Если питонист заглянет в мой код, предпочитаю, чтобы он знал JS, а то когда я отпуске, питонисты и джависты всякую хрень пишут. Лучше пусть ничего не поймёт и свалит в ужасе, если ему не дано, даже если думает, что ему дано.

                  PS. На самом деле не хрень, но обычно люди не считывают практики из соседних участков кода, а пытаются делать как их учили (или как считают правильным). Оно работает, но артефактно для проекта и часто приходится голову ломать как же такое зарефакторить, чтобы не переделывать с нуля. Так что пусть читают, что написано у меня и включают мозг.
                    0
                    Если питонист заглянет в мой код, предпочитаю, чтобы он знал JS, а то когда я отпуске, питонисты и джависты всякую хрень пишут

                    Я часто заглядываю в бек, не для того, чтобы что написать, а чтобы лучше понимать, как работать с нужным методом, и что от него ожидать. В реальном мире не весь код хорошо документируется, и часто быстрее прочитать чужой код, чем спрашивать у автора. Но конечно, нет проблем загуглить.

                      0
                      Если человек хорошо читает код, то он реально легко загуглит простые идиомы.

                      Кроме того, я всегда подробно обсуждаю интерфейсы и возможные кейсы использования с бэкерами (равно как, будучи бэком, обсуждал интерфейсы с фронтами). Но согласен, что может быть код, от которого не осталось разработчиков. И тогда его придётся читать (но не боже мой мне указывать какие идиомы (не)использовать коллегам, да и спросить я не постесняюсь, если не нагуглю).

                      Впрочем, примерно на эту тему у меня две статьи для хабра в работе. Там можно будет и поспорить в комментариях) Ближайшей надо примерно 8-10 поездок на метро, когда удаётся сесть)
                0
                1.!! — стандартная конструкция. Если у меня есть большая структура, то я хочу видеть в console.log или при трассировке именно true/false, а не 42 или Object{....}. Сразу понятно, что тут bool. Использую не то чтобы часто, но если:
                this.is_found = _.find(list, {id})

                то лучше запись

                this.is_found = !!_.find(list, {id})


                она страшноватая, но не засоряет структуру

                2. Обожаю ~. Первый раз когда увидел его прифигел, когда прочитал по нему доку, прифегел второй раз: «оказывается вот так можно!». На порядок лучше, чем
                a.indexOf(...) >= 0

                Просто потому, что можно забыть поставить = и потом долго «хохотать» при поиске ошибки. Если ко мне придёт падаван с вопросом, а что это ~ в коде, то я буду сильно разочарован в человеке не способном воспользоваться один раз гуглом.

                3. Не использовал, потому что не знал. Теперь знаю, но в продакшене использовать не планирую, но ребята выше говорят, что это вполне юзабельно в определённом контексте. Вне контекста хороший способ выстрелить в ногу, тут согласен.

                В девелопе пригодится, буду юзать для отладки стрелок, оно поизящнее того изврата, что использую обычно:

                list.filter(x => console.log(x) || <condition>) // это изврат, да?
                list.filter(x => console.log(x) , <condition>)   // а так прикольнее
                list.filter(x => x.id === 42 && console.log(x) , <condition>) // а тут можно и условие замутить и даже тернарник


                  +1
                  В третьем пункте вы скобки забыли.
                    0
                    Ох! Ё! Точно)

                    Моё извращение остаётся жить, оборачивать второй скобкой «слишком дорого» с учётом, что её потом убирать
                    0

                    По первому, я так и написал


                    или в переменную is*

                    Под is* имелся ввиду шаблон названия гетера, например isActive, isFound и.т.д.

                      0
                      Я бы просто перенёс это исключение в стандартную практику. Я не большой фанат лишних символов, но!! надо понимать когда использовать и как.

                      Хотя возможно вы ближе к реакту, где меньше «предварительной магии», чем в ангуляре.
                  +9
                  Умоляю, не делайте так!
                    0
                    Ок. С тебя миллион долларов и вертолёт
                    0
                    Про with был хороший доклад на последнем Московском HolyJS: тыц. Как только орги опубликуют видео обязательно посмотрите.

                    Если я ничего не путаю суть в том, что необходимо было исполнять функции из некоего набора API и не дать вызвать какие-то другие методы из цепочки прототипов, то есть сделать песочницу. Так вот докладчик использовал для этого связку with + Proxy и это магическим образом и парой костылей (вернее сказать затычек) превратилось в абсолютно изолированную среду. Магия, не иначе!
                      0

                      Интересно. Я пару дней назад тоже экспериментировал с with + Proxy. Проблема в том, что with не дёргает обработчик свойств у прокси. Интересно как у докладчика получилось.

                        +1

                        Очень даже дергает. Главное — не забывать про то, что, в соответствии с пунктом 8.1.2.1 спецификации, сначала дергается has, а потом уже все остальное.

                          0

                          А ведь точно. Я совсем про has забыл, без него ReferenceError вываливается.


                          const console = global.console;
                          
                          const sandbox = new Proxy({}, {
                            has() {
                              return true;
                            },
                          
                            get(target, prop) {
                              console.log('global.%s was called', prop);
                          
                              if (prop === 'console') {
                                return console;
                              }
                          
                              return prop;
                            }
                          });
                          
                          with(sandbox) {
                            console.log(hello)
                          }
                      +3
                      Что из этого малоизвестно?
                        +1
                        Жаль только в строгом режиме все вкусности arguments выпилили (callee, caller). Хотя в конструкторе функций callee еще работает.

                        'use strict';   
                        Function`console.log(arguments.callee)```;
                        
                          0
                          Ну, если верить старым статьям, то когда-то очень давно аргументы были доступны как свойства arguments не только по номерам, но и по именам (если они были). Кроме того, членами arguments становились локальные переменные функции.
                            0
                            Эх… старый-добрый свободный js, это не какой-то TypeScript — тюрьма народов, где все затипизировано и шаг право, шаг влево — расстрел )))
                            –3

                            Вместо callee можно использовать this.name, например. Конечно, this должно быть равно функции в этом случае.

                              –1
                              ну чето совсем не так, у меня ccылается на window.

                              'use strict';
                              (new Function('console.log(this)'))();

                                –1

                                Ну разумеется, если Вы вызываете его из глобального окружения. В данном случае нужно явно передать контекст:


                                const f = new Function('console.log(this.name)')
                                f.call(f) // anonymous
                                
                                const AAA = function () {console.log(this.name)}
                                AAA.call(AAA) // AAA

                                Конечно, this должно быть равно функции в этом случае.

                                Именно это подразумевало.

                          0
                          А мне простым языком программирования для новичков, показался python. JS знаю лучше чем питон, но недавно нужно было написать простенький скрипт пингующий сервер и отправляющий в телеграмм сообщение если сервер не пингуется, я попытался все это реализовать с помощью nodejs и socket.io, но в итоге плюнул и буквально за полчаса все сделал на питоне и уложился в 20 строчек. Хотя питон почти не знаю, чуть чуть основы.
                            +2
                            Зачем вам для этой задачи socket.io понадобился?
                              0
                              Извиняюсь ошибся, хотел написать Telegram API
                              0
                                0

                                Хм, а что вам помешало за полчаса все сделать на ноде?

                                +1

                                with давно устарел и не рекомендуется к использованию.
                                void 0 используется по сей день в большей части в минификаторах, так как занимает меньше места.
                                про + тема не раскрыта. Магия не в том, что + способен "даже moment.js превращать в число", а в том, что он использует valueOf метод, который можно переопределить (ну и делает конверсию, конечно).

                                  0
                                  Никогда не понимал, в чём смысл писать «магию» вроде !!x если можно по-человечески написать Boolean(x). Символы на вес золота что-ли?
                                    +1
                                    Символы, допустим, нет — но вот зачем, например, делать при ограничении на длину строки многоэтажный if на ровном месте, если в этом нет особой нужды? Один-два символа могут стоить многих слов, молодёжь с эмодзи не даст соврать ;-)
                                      –1
                                      тогда !Boolean(x) не очень смотрится. И почему сразу магия? Простое поведение, унарный оператор возвращает значение и передает второму такому же оператору. В javascript можно и !!!!!!!!x написать.
                                      0
                                      |0
                                        +2
                                        Сложно назвать эти особенности языка малоизвестными. Они описываются в любом более-менее толковом учебнике по JS, например, у Флэнэгана или Закаса. Скорее тут речь идет о малоиспользуемых возможностях, что неудивительно, посколько многое из перечисленного признано устаревшим подходом или плохой практикой.
                                          0
                                          Статья — просто выжимка из learn.javascript.ru, где это все описано достаточно доходчиво и последовательно. В чем минус статьи и ей подобных: уровень подачи материала скатывается до нескольких тезисов, в итоге статья становится одной из многих, после которых надо все-таки идти на тот же learn.javascript и заполнять пробелы в знаниях. Говорю по опыту, как человек, изучавший js с нуля (преимущественно сам), преподававший этот язык и как js девелопер.
                                            0
                                            к чему это я. Для людей, кто изучает предмет систематично — перечисленное не станет малоизвестным, а вот те, кто изучает js по отрывкам (например, начал изучать js аж с реакта, с какой-нибудь статьи «реакт за 10 мин для домохозяек» или со статьи выше) — для них любое ключевое слово js (прототипы,ajax, промисы, стрелочные функции и т д до бесконечности) — любое будет чем-то малоизвестным.

                                          Only users with full accounts can post comments. Log in, please.