20 скрытых особенностей JavaScript



Книга 20 скрытых особенностей JavaScript

Представленные ниже 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 зависят от реализации: например, можно писать “непереносимые” регулярные выражения.

836   0  

Comments

    Ничего не найдено.