Открыть список
Как стать автором
Обновить
2707,07
Рейтинг
RUVDS.com
VDS/VPS-хостинг. Скидка 10% по коду HABR

5 интересных JavaScript-находок, сделанных в исходном коде Vue

Блог компании RUVDS.comРазработка веб-сайтовJavaScriptVueJS
Перевод
Автор оригинала: bitfish
Чтение исходного кода известных фреймворков может хорошо помочь программисту в улучшении его профессиональных навыков. Автор статьи, перевод которой мы сегодня публикуем, недавно анализировал код vue2.x. Он нашёл в этом коде некоторые интересные JavaScript-идеи, которыми решил поделиться со всеми желающими.



1. Определение точного типа любого объекта


Как все мы знаем, в JavaScript существует шесть примитивных типов данных (Boolean, Number, String, Null, Undefined, Symbol) и один объектный тип — Object. А вам известно о том, как различать типы различных объектных значений? Объект может быть массивом или функцией, он может представлять собой коллекцию значений Map или что-то ещё. Что нужно сделать для того чтобы узнать точный тип объекта?

Прежде чем искать ответ на этот вопрос — подумаем о разнице между Object.prototype.toString.call(arg) и String(arg).

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

При вызове String(arg) система попытается вызвать arg.toString() или arg.valueOf(). В результате, если в arg или в прототипе arg будут перезаписаны эти методы, вызовы Object.prototype.toString.call(arg) и String(arg) дадут разные результаты.

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

const _toString = Object.prototype.toString
var obj = {}
obj.toString()  // [object Object]
_toString.call(obj) // [object Object]

Выполним этот код в консоли инструментов разработчика браузера.


В данном случае вызовы obj.toString() и Object.prototype.toString.call(obj) приводят к одним и тем же результатам.

Вот ещё пример.

const _toString = Object.prototype.toString
var obj = {}
obj.toString = () => '111'
obj.toString()  // 111
_toString.call(obj) // [object Object]
/hello/.toString() // /hello/
_toString.call(/hello/) // [object RegExp]

Выполним код в консоли.


А теперь вызов метода объекта .toString() и использование конструкции Object.prototype.toString.call(obj) дают разные результаты.

Вот какие правила описывают в стандарте ECMAScript поведение метода Object.prototype.toString().


Описание метода Object.prototype.toString() в стандарте ECMAScript

Взглянув на документацию, можно сделать вывод о том, что при вызове Object.prototype.toString() для разных объектов будут возвращаться различные результаты.

Исследуем эту идею в консоли.


Кроме того, значение, возвращаемое Object.prototype.toString(), всегда представлено в следующем формате:

‘[object ’ + ‘tag’ +‘] ’

Если нам нужно извлечь из этой конструкции только часть tag, добраться до этой части можно, удалив ненужные символы из начала и конца строки с помощью регулярного выражения или метода String.prototype.slice().

function toRawType (value) {
    const _toString = Object.prototype.toString
    return _toString.call(value).slice(8, -1)
}
toRawType(null) // "Null"
toRawType(/sdfsd/) //"RegExp"

Исследуем эту функцию в консоли.


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

Здесь, в репозитории Vue, можно найти код подобной функции.

2. Кеширование результатов работы функции


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

function computed(str) {    
    console.log('2000s have passed')
    return 'a result'
}

Создавая такую функцию, мы намерены кешировать возвращаемые ей результаты. Когда эту функцию вызовут в следующий раз, передав ей те же параметры, что и ранее, «тяжёлый» код функции выполняться не будет. Вместо этого будет, без лишних затрат времени, возвращён кешированный результат. Как это сделать?

Можно, например, написать функцию-обёртку для целевой функции. Такой функции можно дать имя cached. Эта функция принимает, в виде аргумента, целевую функцию, и возвращает новую функцию, оснащённую возможностями кеширования. В функции cached можно кешировать результаты предыдущих вызовов целевой функции, воспользовавшись сущностью Object или Map. Вот код этой функции:

function cached(fn){
  // Создаём объект для хранения результатов, возвращаемых целевой функцией после её выполнения
  const cache = Object.create(null);

  // Возвращаем целевую функцию в соответствующей обёртке
  return function cachedFn (str) {

    // Если в кеше нет подходящих результатов - функция будет выполнена
    if ( !cache[str] ) {
        let result = fn(str);

        // Сохраняем результат выполнения функции в кеше
        cache[str] = result;
    }

    return cache[str]
  }
}

Вот пример использования вышеописанной функции.


→ Вот код подобной функции, который имеется в кодовой базе Vue.

3. Преобразование строки вида hello-world к строке вида helloWorld


Когда над одним и тем же проектом совместно работает несколько программистов, им очень важно заботиться о единообразии стиля кода. Кто-то, например, может записывать некие составные идентификаторы в формате helloWorld, а кто-то — в формате hello-world. Для того чтобы навести в этой области порядок, можно создать функцию, которая преобразует строки вида hello-world к строкам вида helloWorld.

const camelizeRE = /-(\w)/g
const camelize = cached((str) => {
  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})
camelize('hello-world')
// "helloWorld"

Вот то место кода Vue, откуда взят этот пример.

4. Определение того, в каком именно окружении выполняется JavaScript-код


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

const inBrowser = typeof window !== 'undefined'
const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform
const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase()
const UA = inBrowser && window.navigator.userAgent.toLowerCase()
const isIE = UA && /msie|trident/.test(UA)
const isIE9 = UA && UA.indexOf('msie 9.0') > 0
const isEdge = UA && UA.indexOf('edge/') > 0
const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android')
const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios')
const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge
const isPhantomJS = UA && /phantomjs/.test(UA)
const isFF = UA && UA.match(/firefox\/(\d+)/)

Вот где я нашёл этот код.

5. Различение встроенных и пользовательских функций


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

Array.isArray.toString() // "function isArray() { [native code] }"
function fn(){} 
fn.toString() // "function fn(){}"

Поэкспериментируем с этим кодом в консоли.


Метод toString() нативной функции всегда возвращает конструкцию следующего вида:

function fnName() { [native code] }

Зная это, можно написать функцию, позволяющую различать нативные и пользовательские функции:

function isNative (Ctor){
  return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}

→ Вот то место в кодовой базе Vue, где есть такая функция.

А вам удавалось находить что-то интересное, исследуя код в репозиториях известных JavaScript-проектов?

Теги:JavaScriptVueразработкафреймворкпрограммирование
Хабы: Блог компании RUVDS.com Разработка веб-сайтов JavaScript VueJS
Всего голосов 42: ↑38 и ↓4 +34
Просмотры15.9K

Комментарии 32

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

Похожие публикации

Лучшие публикации за сутки

Информация

Дата основания
Местоположение
Россия
Сайт
ruvds.com
Численность
11–30 человек
Дата регистрации
Представитель
ruvds

Блог на Хабре