Источник: Nuances of Programming
Представленные ниже 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 зависят от реализации: например, можно писать “непереносимые” регулярные выражения.
Читайте также:
Перевод статьи Aboelez, 20 Hidden JavaScript Features You Probably Don’t Know About