Очень вольный перевод статьи от SmashingMagazine Ten Oddities And Secrets About JavaScript
Переменные и типы данных
1. Null как объект. Ключевое слово null в JavaScript имеет специальный смысл. У значения null объектный тип и оно говорит об отсутствии объекта. Значение null уникально и отличается от любых других. Если переменная равна null, следовательно, в ней не содержится допустимого объекта, массива, числа, строки или логического значения.1
Когда значение null используется в логическом контексте, оно преобразуется в значение false, в числовом контексте оно преобразуется в значение 0, а в строковом контексте— в строку "null"
1 2 3 |
alert(typeof null); //alerts 'object' alert(null instanceof Object); //вернет false, т.е. объекта нет. |
2. NaN это число. Расшифровывается как Not A Number. Обычно это значение возвращается при ошибке выполнения операций с числами. Тем не менее NaN одновременно и является числом и при этом не равно само себе. Как вам эта идея?
1 2 3 4 5 |
alert(typeof NaN); //alerts 'Number' alert(NaN === NaN); //evaluates false alert(NaN == NaN); // false alert(NaN != NaN); // true |
В самом деле NaN не равно ни к чему. Единственный способ проверить что переменная содержит NaN, через функцию isNaN()
3. Массив без ключей == false (Правда и ложь).
Вот еще одна особенность.
1 |
alert(new Array() == false); //evaluates true |
Чтобы понять почему это так, вы должны понимать концепцию правды и лжи. И она отличается от той, которую вы могли получить, изучая логику или философию. Самое простое объяснение, в Javascript каждое не булево значение имеет свой логический флаг(true|false), который присваивается только на время сравнения с логическим значением.
False, zero, null, undefined, пустые строки и NaN эквивалентны false не постоянно, а только для данного выражения. Пример:
1 2 3 |
var someVar = 0; alert(someVar == false); //evaluates true |
В данном примере мы пытаемся сравнить 0 и false. Поскольку типы отличаются, Javascript приводит первое значение к логическому и мы получаем false==false.
Пустые массивы ведут себя более странно. Пустой массив в выражении вычисляется как true, однако в сравнении с логическим значением эквивалентен false. Запутал? Иллюстрирую.
1 2 3 4 |
var someVar = []; //пустой массив alert(someVar == false); //вернули true if (someVar) alert('hello'); //alert runs, someVar вычислено как true |
Чтобы избежать приведения типов, нужно использовать оператор сравнения типов === (== сравнивает только значения).
1 2 3 4 |
var someVar = 0; alert(someVar == false); //evaluates true – zero is a falsy alert(someVar === false); //evaluates false – zero is a number, not a boolean |
Обсудить подробнее концепцию правды и лжи, можно в блоге автора . Ну а если есть желание понять, что происходит изнутри, когда Javascript сравнивает два значения, добро пожаловать в section 11.9.3 of the ECMA-262 документации.
Регулярные выражения
4. replace() может принимать функцию обратного вызова. Эта малоизвестная особенность появилась Javascript 1.3. Обычно replace() используют так.
1 2 |
alert('10 13 21 48 52'.replace(/\d+/g, '*')); //replace all numbers with * |
А если мы хотим заменить только числа меньше 30? Этого не добиться с использованием одних регулярных выражений., они работают со строками. В этом случае можно использовать callback функцию, которая будет исполняться после каждой замены.
1 2 3 4 |
alert('10 13 21 48 52'.replace(/\d+/g, function(match) { return parseInt(match) < 30 ? '*' : match; })); |
После каждого совпадения с регулярным выражением, Javascript передаст совпадение(match) как аргумент callback функции. В примере если значение меньше 30, то произойдет замена на "*", если нет то заменит себя в том же месте.
5. Регулярные выражения: больше чем match и replace. Большинство разработчиков Javascript знают и используют только match() и replace() . Но есть и другие методы.
Особый интерес представляет test() , который работает как match, но он не возвращает совпадения, лишь подтверждает совпадение с шаблоном.
1 2 |
alert(/\w{3,}/.test('Hello')); //alerts 'true' |
В примере, строка Hello содержит более 3 букв или цифр , поэтому совпадает с шаблоном.
Также интересен Regexp, объект который позволяет создавать динамические регулярные выражения. Большинство регулярных выражений пишется в сокращенной форме (заключены в косую черту, как мы это показывали выше). Таким образом нельзся сссылаться на переменые и невозможно создавать динамических выражений. С Regexp() вы избавлены от этого недостатка.
1 2 3 4 5 |
function findWord(word, string) { var instancesOfWord = string.match(new RegExp('\\b'+word+'\\b', 'ig')); alert(instancesOfWord); } findWord('car', 'Carl went to buy a car but had forgotten his credit card.'); |
В примере мы создаем динамический шаблон, в зависимости от переданного в функцию значения перменной word. В отличие от короткого синтаксиса нам приходится дважды слэшить спецсимволы в регулярном выражении.
Функции и видимость
6. Делаем переменную видимой глобально. (You Can Fake Scope — как бы правильней перевести?) Область видимости означает где переменные и функции доступны, и в каком контексте они исполняются.
Выражения javascript(те которые не в теле функции) исполняются в контексте обьекта window. Они будут доступны для всего, что исполняется в window.
1 2 3 |
var animal = 'dog'; function getAnimal(adjective) { alert(adjective+' '+this.animal); } getAnimal('lovely'); //alerts 'lovely dog'; |
this
всегда указывает на текущую область видимости, в данном случае window
(если исполняется в броузере). Таким образом функция ищет и находит window.animal . Но мы можем дать знать нашей функции, что она исполняется в другой видимости используя call() метод.
1 2 3 4 5 |
var animal = 'dog'; function getAnimal(adjective) { alert(adjective+' '+this.animal); }; var myObj = {animal: 'camel'}; getAnimal.call(myObj, 'lovely'); //alerts 'lovely camel' |
В примере функция уже работает в видимости объекта myObj.
Я слышал, многие разработчики говорят, что за годы программирования на Javascript им не приходилось это использовать, не в последнюю очередь потому, что хороший стиль программирования требует обходиться без фокусов. Но знание не бывает лишним.
Метод apply(), аналогичен call(), но аргументы функции задаются в виде массива.
1 2 |
getAnimal.apply(myObj, ['lovely']); //func args sent as array |
7. функции могут исполнять сами себя.
1 |
(function() { alert('hello'); })(); //alerts 'hello' |
Мы объявляем функцию и сразу же ее запускаем используя синтаксис () . Кажущееся противоречие: функция содержит код, который мы хотим запустить позже, не сейчас, иначе мы бы не помещали код в функцию.
Хорошая причина, чтобы использовать само-исполняющиеся функции, привязка текущих значений переменных, для исполнения кода с задержкой внутри функций обратного вызова для событий, тайм-аутов и интервалов.
1 2 3 |
var someVar = 'hello'; setTimeout(function() { alert(someVar); }, 1000); var someVar = 'goodbye'; |
Новички в форумах неизменно спрашивают, почему alert в timeout выдает goodbye и не hello ? Ответ прост, callback функция внутри timeout() исполняется уже тогда, когда someVar перезаписана goodbye.
SEFs позволяет решить эту проблему.
1 2 3 |
var someVar = 'hello'; setTimeout((function() { alert(someVar); })(someVar), 1000); var someVar = 'goodbye'; |
В данном примере мы изолировали someVar внутри функции, используя как аргумент и в результате alert выдаст hello.
Браузер
8. Firefox читает и возвращает цвета в RGB, а не Hex . Я никогда не понимал, почему Mozilla делает это.
1 2 3 4 5 6 7 8 9 10 11 |
<style> #somePara { color: #f90; } </style> <p id="somePara">Hello, world! <script> var ie = navigator.appVersion.indexOf('MSIE') != -1; var p = document.getElementById('somePara'); alert(ie ? p.currentStyle.color : getComputedStyle(p, null).color); </script> </p> |
Хотя большинство браузеров будет возвращать ff9900 , Firefox возвращает rgb(255, 153, 0) . Обратите внимание, что когда я говорю, вычисляется цвет, я имею в виду текущий цвет, независимо от того, как он применяется к элементу.
Разное
9. Особенности вычислений с плавающей точкой 0.1 + 0.2 !== 0.3 . Это встречается во многих языках программирования и не только в Джаваскрипт. Результ этого выражения 0,30000000000000004.
Происходит это из за того, что 0.1 и 0.2 преобразуются в их двоичный эквивалент и затем складываются. Но двоичный эквивалент идентичен исходным числам лишь с потерей точности.
Как обойти эту проблему? Решения выходят за рамки данной статьи, но в общем виде сводятся к двум вариантам:
1. Преобразовать чилса в целые, совершить вычисление и потом, преобразовать в десятичное число результат.
2. Поменять логику сравнения, чтобы она учитывала диапазон.
1 2 3 4 5 |
var num1 = 0.1, num2 = 0.2, shouldEqual = 0.3; alert(num1 + num2 == shouldEqual); //false //variant 2 alert(num1 + num2 > shouldEqual - 0.001 && num1 + num2 < shouldEqual + 0.001); //true |
10. Undefined Can Be Defined. Как ни странно это может звучать, undefined в действительности не является зарезервированным словом в JavaScript, хотя оно имеет особое значение и является единственным способом узнать определена ли переменная.
1 2 3 |
var someVar; alert(someVar == undefined); //evaluates true |
Это нормальное поведение. Но:
1 2 3 |
undefined = "I'm not undefined!"; var someVar; alert(someVar == undefined); //evaluates false! |
Вы так же можете посмотреть Список Mozilla зарезервированных слов в JavaScript
Свежие комментарии