Представленные ниже 20 хитростей JavaScript были указаны пользователями Stack Overflow.
1. Работа с аргументами функций
Вам не нужно определять параметры для функции — можете просто использовать массивоподобный функциональный объект аргумента:
function sum() {
var retval = 0;
for (var i = 0, len = arguments.length; i < len; ++i) {
retval += arguments[i];
}
return retval;
}
sum(1, 2, 3) // возвращает 6
2. Операторы === и !==
Всегда используйте === и !==
вместо == и !=
.
alert('' == '0'); //false
alert(0 == ''); // true
alert(0 == '0'); // true
Оператор ==
не является транзитивным. Если вы используете ===
, то он вернет значение false
, как и ожидалось, для всех вышеуказанных случаев.
3. Функции в JavaScript
Функции являются полноправными гражданами в JavaScript:
var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };
var sum = function(x,y,z) {
return x+y+z;
};
alert( passFunAndApply(sum,3,4,5) ); // 12
В частности, функции могут передаваться в качестве параметров, например Array.filter()
— это функция обратного вызова:
[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]
В качестве альтернативы вы можете объявить “закрытую” функцию, которая существует только в области действия определенной функции:
function PrintName() {
var privateFunction = function() { return "Steve"; };
return privateFunction();
}
4. Оператор in
Вы можете использовать оператор in
для проверки наличия у объекта такого ключа:
var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true
Обнаружив, что объектные литералы выглядят недостаточно хорошо, вы можете объединить их с помощью функции без параметров:
function list() {
var x = {};
for(var i=0; i < arguments.length; ++i)
x[arguments[i]] = 0;
return x
}
5 in list(1,2,3,4,5) //true
5. Значения по умолчанию
Вы можете использовать ||
в выражении присваивания, чтобы указать значение по умолчанию:
var a = b || c;
Переменная a
получит значение c только в том случае, если b = false
(то есть, если значение null
, false
, не определено, 0
, пустая строка или NaN
), в противном случае a
получит значение b
.
Обычно это полезно в функциях, когда вы хотите присвоить аргументу значение по умолчанию, если оно не указано:
function example(arg1) {
arg1 || (arg1 = 'default value');
}
Например, IE завершается неуспехом в обработчике событий:
function onClick(e) {
e || (e = window.event);
}
The debugger
Этот оператор позволяет устанавливать точки прерывания внутри кода:
// ... debugger; // ...
Если отладчик активен, он совершит прерывание прямо в этой строке.
Многострочные литералы:
var str = "This is a \
really, really \
long line!";
Имейте в виду, что символ рядом с \
должен заканчивать строку. Если у вас пробел после \
, будет выдана синтаксическая ошибка.
6. Область видимости в JavaScript
JavaScript не имеет области видимости блока:
var x = 1;
{ var x = 2; }
alert(x); // выводит 2
7. Свойства объекта
Вы можете получить доступ к свойствам объекта, используя []
вместо ‘.’
. Это позволяет обнаруживать свойство, соответствующее переменной.
obj = {a:"test"};
var propname = "a";
var b = obj[propname]; // "test"
Кроме того, вы можете использовать это для получения/установки значения свойства, если его имя является неразрешенным идентификатором.
obj["class"] = "test"; // class - зарезервированное слово; obj.class не будет иметь силы.
obj["two words"] = "test2"; // использование оператора-точки невозможно с пробелом.
Некоторые разработчики этого не знают и используют eval ()
, что на самом деле не очень:
var propname = "a";
var a = eval("obj." + propname);
Это затрудняет чтение кода, усложняет поиск ошибок (вы не можете использовать JSLint), замедляя его выполнение, и может привести к XSS.
8. mdc
Когда вы ведете поиск в Google по теме JavaScript, добавьте “mdc” в свой запрос. Тогда первые результаты будут получены из Центра разработчиков Mozilla (Mozilla Developer Center; сокращенно: MDC).
Например:
Google: сортировка массива javascript mdc
(в большинстве случаев можно обойтись в запросе без “javascript”)
Примечание: Mozilla Developer Center был переименован в Mozilla Developer Network (MDN). Ключевое слово “mdc” все еще работает, но вскоре вам, возможно, его поменяют на “mdn”.
9. Капитан Очевидность
Установите Firebug и используйте console.log("hello")
. Это гораздо удобнее, чем использовать случайные оповещения alert();
10. Закрытые методы
Объекты могут иметь закрытые методы:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
// Частный метод, видимый только из этого конструктора
function calcFullName() {
return firstName + " " + lastName;
}
// Публичный метод, доступный каждому
this.sayHello = function () {
alert(calcFullName());
}
}
//Использование:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();
// Так не получится, поскольку метод не виден из этой области
alert(person1.calcFullName());
11. parseInt() — JavaScript
Метод parseInt()
требует осторожного использования. Если вы передадите ему строку, не сообщив простой базис, она может вернуть неожиданные числа. Например, parseInt('010')
возвращает 8, а не 10. Передача базы в parseInt приводит к корректной работе:
parseInt('010') // возвращает 8! (в FF3)
parseInt('010', 10); // возвращает 10, потому что мы сообщили, с какой базой работать.
12. Функции
Функции являются объектами и, следовательно, могут иметь свойства:
fn = function(x) {
// ...
}
fn.foo = 1;
fn.next = function(y) {
//
}
13. Параметры объекта
Сколько параметров ожидается функцией:
function add_nums(num1, num2, num3 ){
return num1 + num2 + num3;
}
add_nums.length // 3 - количество ожидаемых параметров.
Сколько параметров получает функция:
function add_many_nums(){
return arguments.length;
}
add_many_nums(2,1,122,12,21,89); //возвращает 6
14. Методы
Методы (или функции) могут вызываться для объектов, не относящихся к тому типу, для которого они были предназначены. Если осуществляется вызов нативных (быстрых) методов для пользовательских объектов, то все замечательно
var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });
Этот код аварийно завершает работу, потому что listNodes
не является Array
Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);
Этот код работает, потому что listNodes
определяет достаточно массивоподобных свойств (длину, оператор []
), которые будут использоваться sort()
.
15. Прототипное наследование
Наследование через прототипы (популяризированное Дугласом Крокфордом) полностью революционизирует ваши представления о множестве вещей в JavaScript:
Object.beget = (function(Function){
return function(Object){
Function.prototype = Object;
return new Function;
}
})(function(){});
Жаль, что этой фичей почти никто не пользуется.
Она позволяет производить новые экземпляры любого объекта, расширять их, сохраняя при этом (прямую) связь цепочки прототипов с их другими свойствами. Пример:
var A = {
foo : 'greetings'
};
var B = Object.beget(A);
alert(B.foo); // 'greetings'//
изменения и дополнения к А отражены в В
A.foo = 'hello';
alert(B.foo); // 'hello'
A.bar = 'world';
alert(B.bar); // 'world'
// ...но не наоборот
B.foo = 'wazzap';
alert(A.foo); // 'hello'
B.bar = 'universe';
alert(A.bar); // 'world'
16. Замыкание
Как насчет замыканий в JavaScript (аналогичных анонимным методам в C# версии 2.0+)? Вы можете создать функцию, которая вызывает функцию или выражение.
Пример замыкания:
//Берет функцию, которая фильтрует числа, и вызывает другую функцию //для построения списка чисел, удовлетворяющих этой функции
function filter(filterFunction, numbers)
{
var filteredNumbers = [];
for (var index = 0; index < numbers.length; index++)
{
if (filterFunction(numbers[index]) == true)
{
filteredNumbers.push(numbers[index]);
}
}
return filteredNumbers;
}
//Создает функцию (закрытие), которая запомнит переданное значение
"lowerBound"
//и сохранит его копию
function buildGreaterThanFunction(lowerBound)
{
return function (numberToCheck) {
return (numberToCheck > lowerBound) ? true : false;
};
}
var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];
var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);
numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);
numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);
17. Объекты вместо переключателей
Большую часть времени можно использовать объекты вместо переключательных элементов:
function getInnerText(o){
return o === null? null : {
string: o,
array: o.map(getInnerText).join(""),
object:getInnerText(o["childNodes"])
}[typeis(o)];
}
Примечание 1: если вам кажется, что предварительная оценка случаев неэффективна (почему вы беспокоитесь об эффективности на раннем этапе разработки программы??), можете сделать следующее:
function getInnerText(o){
return o === null? null : {
string: function() { return o;},
array: function() { return o.map(getInnerText).join(""); },
object: function () { return getInnerText(o["childNodes"]; ) }
}[typeis(o)]();
}
Этот стиль более обременителен для ввода (или чтения), чем использование переключателя или объекта. Зато при нем сохраняются преимущества объекта вместо переключателя (подробное описание в разделе примечаний ниже). Кроме того, этот стиль делает более простым превращение объекта, как только он достаточно подрастет, в надлежащий класс.
Примечание 2: с предлагаемыми для ES.next расширениями синтаксиса, это будет выглядеть так:
let getInnerText = o -> ({
string: o -> o,
array: o -> o.map(getInnerText).join(""),
object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);
18. hasOwnProperty
Обязательно используйте метод hasOwnProperty
при повторном просмотре свойств объекта:
for (p in anObject) {
if (anObject.hasOwnProperty(p)) {
//Do stuff with p here
}
}
Это необходимо для того, чтобы вы могли получить доступ только к прямым свойствам объекта, а не использовать свойства, которые находятся в цепочке прототипов.
19. Скрытые переменные с открытым интерфейсом
Маленькая хитрость связана с определением функции самостоятельного вызова. Все, что находится внутри возвращаемого объекта, доступно в публичном интерфейсе, в то время как все остальное является скрытым.
var test = function () {
//частные участники
var x = 1;
var y = function () {
return x * 2;
};
//публичный интерфейс
return {
setx : function (newx) {
x = newx;
},
gety : function () {
return y();
}
}
}();
assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());
20. Вот еще несколько интересных лайфхаков:
- Сравнение
NaN
с чем-либо (даже сNaN
) всегда ложно, включая==
,<
и>
. NaN
(Not-a-Number) означает “не число”, но если вы запросите тип,NaN
вернет число.Array.sort
может выполнять функцию сравнения и вызывается драйвером, подобным быстрой сортировке (зависит от реализации).- Некоторые версии JavaScript позволяют вам получать доступ к элементам
$0
,$1
,$2
в регулярном выражении. null
не похож ни на что другое. Это не объект, не логическое значение, не число, не строка и неundefined
. Это немного похоже на “альтернативный”undefined
. (Примечание:typeof null == "object"
)- В самом внешнем контексте
this
вызывает противоположный неназванный [глобальный] объект. - Объявление переменной с помощью
var
вместо расчета на автоматическое объявление переменной, дает среде выполнения реальный шанс оптимизировать доступ к этой переменной. - Конструкция
with
уничтожит такую оптимизацию. - Имена переменных могут содержать символы Юникода.
- Регулярные выражения JavaScript на самом деле не являются регулярными. Они основаны на регулярных выражениях Perl. Можно создавать выражения с предварительным просмотром, для оценки которых требуется очень много времени.
- Блоки можно пометить и использовать в качестве целей
break
, а циклы в качестве целейcontinue
. - Массивы не являются разреженными. Установка 1000-го элемента в противоположном пустом массиве должна заполнить его
undefined
(зависит от реализации). if (new Boolean(false)) {...}
выполнит блок{...}
- Механизмы регулярных выражений JavaScript зависят от реализации: например, можно писать “непереносимые” регулярные выражения.
Читайте также:
- Как преобразовать функции JavaScript в генераторы, эффективно использующие память
- Применение методов Bind(), Call(), and Apply() в JavaScript
- 8 мощных пакетов NPM для любого веб-разработчика
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Aboelez, 20 Hidden JavaScript Features You Probably Don’t Know About