Pull to refresh

20 и 1 примочка Javascript, которые я никак не могу запомнить

Reading time8 min
Views21K
Original author: Cody Lindley
Статья описывает особенности Javascript ES3, которые имеют свойство забываться. Одни из них — странности языка, а другие просто свойства, которые, по-моему, труднозапоминаемы. Я постарался собрать здесь не те из них, по которым можно дать просто ссылку на описание языка (как по различиям между apply и call), а о которых мало сказано в руководствах.

Прежде чем начать, хочу уточнить 3 момента, которые я не стараюсь доказать этой статьёй:

1. Я не говорю, что эти особенности забываются всеми или что они такие по сути. Я их нахожу трудными лично для меня (например, забываются детали, если их не освежать в памяти). Они могут быть лёгкими для вас. Тем не менее, думаю, что этот сборник может быть полезен не только мне.

2. Я не говорю, что нужно использовать эти части языка, а лишь утверждаю, что если вы знаете их, то сможете легко читать код других разработчиков. Большинство их могут быть полезны, но не всегда наличие их в коде — лучшее решение.

3. Я не затрагиваю особенностей ES5 — ни Strict, ни вообще, ни ES6 в целом. Я расскажу о них позже. Я признаю, что некоторые из обсуждаемых особенностей изменяются или развиваются в последующих версиях ECMAScript.

C учётом сказанного, начнём.

1. −0 = +0 = 0 (или строгий нуль равен положительному и отрицательному нулям)


В JavaScript есть значение числа -0 и +0, показывающее, с какой стороны вы подошли к нулю. Но оно преобразуется к строгому нулю при сравнении.
jsfiddle.net/codylindley/6phD9/light
console.log(-0); //logs 0
console.log(+0); //logs 0
console.log(-0 === +0); //logs true
console.log(0 === -0); //logs true
console.log(0 === +0); //logs true


2. Слово new самостоятельно может выполнить конструктор


Вызов конструктора без аргументов может выполниться одним ключевым словом new, без написания скобок после конструктора.
jsfiddle.net/codylindley/nHCvx/light
var Human = function(){
    this.type = 'human';
    console.log('I was called');
};
new Human; //вызов без скобок выполняет конструктор и выводит "I was called"

console.log((new Human).type); //выводит human (свойство созданного объекта)


3. instanceof не работает с примитивами


Оператор instanceof возвращает false для строк, чисел и логических переменных. Если же они расположены в объектах (т.е. в обёртках примитивов), то нормально работает. Не забывайте, что null и undefined не имеют объектной обёртки.
jsfiddle.net/codylindley/bpJjZ/light

//instanceof не работает на примитивах
console.log("" instanceof String); //выводит false
console.log(3 instanceof Number); //выводит false
console.log(false instanceof Boolean); //выводит false

//instanceof работает на сложных значениях (объектах)
console.log(new String() instanceof String); //выводит true
console.log(new Number() instanceof Number); //выводит true
console.log(new Boolean() instanceof Boolean); //выводит true
console.log([] instanceof Array); //выводит true
console.log({} instanceof Object); //выводит true
console.log(/foo/ instanceof RegExp); //выводит  true

//к сведению,
console.log([] instanceof Object); //выводит true


4. Оператор typeof — неуниверсальный и с ошибками


Применимость typeof ограничивается примитивами, но и там имеет ошибку: (typeof null === «object») == true, из-за чего самописно-костыльные способы проверок оказываются наилучшими./
jsfiddle.net/codylindley/U64aZ/light

//удобно для значений примитивов string, number, и boolean
console.log(typeof ""); //выводит "string"
console.log(typeof 4); //выводит number
console.log(typeof true); //выводит boolean
console.log(typeof undefined); //выводит undefined

//но ошибочно, когда переходим к null
console.log(typeof null); //ЧТО?? Неправильно! выводит Object


5. Булевый объект от ложных значений всегда true


console.log(!!(new Boolean(false)) )  //true

new Boolean(new Boolean(false)) — не false, потому что возвращает объект-обёртку. А объект (следите за руками) всегда преобразуется к true. Поэтому названный софизм относится вообще к любому значению конструктора. А настоящий false создают лишь примитивы (NaN, false, 0, null, undefined, и '') и значения выражений.

6. Аргументы или параметры функции?


Аргументы используются в момент вызова функции. Мы передаём функции аргументы. Параметры устанавливаются в момент определения функции. Таким образом, говорят, что параметры используются для определения функции. Эти слова взаимозаменяемы, но знание их разницы может пригодиться.
jsfiddle.net/codylindley/8Zkdd/light

var myFunction = function(x,y,z){ //x,y,z - параметры
    return x+y+z
};
myFunction(1,2,3); //1,2,3 - аргументы


7. Изменение значения параметра не изменяет значение аргумента


Например, если вы передадите массив в функцию, то изменение значения параметра не изменит исходного значения параметра.
jsfiddle.net/codylindley/c2xb4/light
var myFunction = function(parameter){
    parameter = undefined; //изменяем значение параметра на undefiend
    console.log(parameter);
};
var myArray = [1,2,3];
myFunction(myArray); //вызываем myFunction, передаём массив

//myArray - не undefined , несмотря на то, что он как параметр изменён в функции
console.log(myArray); //logs [1,2,3]


8. Вызов Boolean() как функции преобразует любое значение в boolean


Вызов конструктора как функции (без new) с аргументом преобразует его в логическое.

jsfiddle.net/codylindley/4wQLd/light
//всё, что ниже, возвращает false 
console.log(Boolean(undefined));
console.log(Boolean(null));
console.log(Boolean(0));
console.log(Boolean(''));
console.log(Boolean(NaN));

//всё, что ниже, возвращает true
console.log(Boolean(1));
console.log(Boolean('false'));
console.log(Boolean([]));
console.log(Boolean({}));
console.log(Boolean(function(){}));
console.log(Boolean(/foo/));

Конечно, двойная инверсия !!() даст тот же эффект. Но это недостаточно выразительно, чтобы мне нравилось.
jsfiddle.net/codylindley/jNPvH/light
//всё, что ниже, возвращает false 
console.log(!!undefined);
console.log(!!null);
console.log(!!0);
console.log(!!'');
console.log(!!NaN);

//всё, что ниже, возвращает true
console.log(!!1);
console.log(!!'false');
console.log(!![]);
console.log(!!{});
console.log(!!function(){});
console.log(!!/foo/);


9. Вызов конструктора примитивных объектов без new возвращает примитивы


jsfiddle.net/codylindley/ymGmG/light
//выводит true
console.log(String('foo') === 'foo');
console.log(Number(5) === 5);
console.log(Boolean('true') === true);

//выводит false, потому что создаётся оболочка объекта
console.log(new String('foo') === 'foo');
console.log(new Number(5) === 5);
console.log(new Boolean('true') === true);


10. Object() создаёт объектные оболочки примитивов


В отличие от других конструкторов, Object() оборачивает примитивы в оболочки.
jsfiddle.net/codylindley/tGChw/light
//примитив number в оболочке объекта
console.log(Object(1) instanceof Number); //logs true

//примитив string в оболочке объекта
console.log(Object('foo') instanceof String);

//примитив boolean в оболочке объекта
console.log(Object(true) instanceof Boolean);


11. Доступ к свойствам примитивов таит ошибку



Всё отлично пишется в примитивы, кто бы что там ни говорил. Проблем нет и при чтении. Если не считать, что читается всегда undefined. Причина в том, что объектная оболочка для примитива не создаётся.
jsfiddle.net/codylindley/E7GGK/light
//создаём примитивы
var N = 5;
var S = 'foo';
var B = true;

//добавим к примитивам свойства, которые исчезнут, если у примитивов нет обёрток объектов
N.test = 'test';
S.test = 'test';
B.test = 'test';

//проверим, что свойств не осталось
console.log(N.test); //выводит undefined
console.log(S.test); //выводит undefined
console.log(B.test); //выводит undefined


12. delete не удаляет унаследованные свойства


jsfiddle.net/codylindley/xYsuS/light

var Person = function(){}; //создаёт конструктор Person

Person.prototype.type = 'person'; //наследуемое свойство

var cody = new Person(); //потомок Person

delete cody.type //удаляем (?) type

console.log(cody.type) //выводит person, потому что сын за отца не отвечает


13. Объектные оболочки строк массивоподобны


Из-за того, что оболочка строкового объекта создаёт массивоподобный объект, (т.е. console.log(new String('foo')); //выводит foo {0=«f», 1=«o», 2=«o»} ), можно использовать индексирование символов (кроме IE7-).
jsfiddle.net/codylindley/7p2ed/light

//примитив строки
var foo = 'foo';

//доступ к символам как к массиву
console.log(foo[0]) //выводит "f", потому что создаётся оболочка объекта {0="f", 1="o", 2="o"}

//есть доступ к свойствам через угловые скобки
console.log(foo['length']); //выводит 3


14. Доступ к свойствам из примитивов чисел


Первая точка после примитива числа распознаётся как десятичная точка и конфликтует с точкой в смысле доступа к свойству. Но вторая…
jsfiddle.net/codylindley/Pn6YT/light

//скобочная нотация
console.log(2['toString']()); //logs 2

//точечная нотация доступа к свойствам после десятичной точки. 
console.log(2..toString()); //logs 2

//скобки для точечной нотации
console.log((2).toString()); //logs 2


15. Array.length можно переписывать, что имеет побочные эффекты


Явным присваиванием свойства length можно добавить ряд undefined-значений в конец массива или укоротить массив.
jsfiddle.net/codylindley/9Pp9h/light

var myArray1 = [];
myArray1.length = 3;
console.log(myArray1); //выводит [undefined, undefined, undefined]

//удалить часть значений
myArray2 = [1,2,3,4,5,6,7,8,9]
myArray2.length = 1;
console.log(myArray2); //выводит [1]


16. Логический оператор "||" работает до первого истинного значения


Если значение левого аргумента в этом операторе приведётся к true, правое значение вычисляться не будет, что образно именуется термином «short-circuiting» (закорачивание, короткое замыкание, или для данного случая — «идти коротким путём»). Фактически, обнаружение true в первом значении приводит к незатрагиванию функций и выражений правой части, неизменению значений в их скрытых сеттерах.
jsfiddle.net/codylindley/NUKKZ/light

//возвращает первое значение, преобразуемое к true, затем выражения не вычисляются
var foo = false || 0 || '' || 4 || 'foo' || true;

console.log(foo); //выводит 4, потому что число приводится
//    к первому истинному логическому значению в цепочке


17. Логический оператор "&&" работает до первого ложного значения


Аналогично, но с обратным логическим значением, выполняется до первого фола включительно.
jsfiddle.net/codylindley/DEbk5/light

//возвращает первое значение, преобразуемое к false, затем выражения не вычисляются
var foo = true && 'foo' && '' && 4 && 'foo' && true;

console.log(foo); //выводит '', потому что строка приводится к false


18. Когда использовать null, а когда — undefined?


Проще говоря, null — это значение для «ничего», а undefined — это само «ничего»: отсутствие какого-либо примитива, объекта, свойства, пустая ссылка. В обычной практике разработчикам рекомендуется использовать null для индикации явно проставленного отсутствия значений, а undefined — оставлять для Javascript для указания отсутствия того, чего «никогда не было». При использовании null в программе вы отличите сообщения скрипта от следов сознательных установок null.

19. undefined по умолчанию


undefined описывает отсутствие значения. Следующие примеры не создают значений (возвращают undefined).
jsfiddle.net/codylindley/3buPG/light
jsfiddle.net/codylindley/3buPG/light
//переменные без присвоенных значений - не имеют значений
var foo;
console.log(foo); //undefined

//попытка доступа к отсутствующему свойству возвращает undefined
console.log(this.foo); //undefined

//передача отсутствующих аргументов функции, ожидающей их, приводит к undefined-параметрам
var myFunction = function f(x){return x}
console.log(myFunction()); //undefined

//функция возвратит undefined, если не выполнила явный return вообще
var myFunc = function f(){};
console.log(myFunc()); //undefined


20. Выражение может работать там, где не сможет оператор


Выражения возвращают значения, в то время как операторы выполняют действия, что ярко иллюстрируется на примере различий между if и тернарным логическим оператором для условного выражения. «if» не может работать при задании параметров функций, условное выражение — может.
jsfiddle.net/codylindley/SSh68/light
var verify = true;

//оператор if работает с любыми операторами
if(verify){console.log('verify is true');}

//Выражение может работать там, где не могут быть применены операторы

//запись в переменную (странный пример --прим.пер.)
var check = verify ? console.log('verify is true') : console.log('verify is false');

//передача значений в функцию
console.log(verify ? 'verify is true' : '
verify is false');


21. Расположение операторов ++ и --


Если операторы префиксные (перед переменной), то сначала изменяется значение, затем оно возвращается в выражение. Если постфиксные — в выражение возвращается начальное значение, но перед возвратом переменная изменяется.
jsfiddle.net/codylindley/QzG9w/light
http://jsfiddle.net/codylindley/QzG9w/light/
var boo = 1;
var foo = 1;

console.log(boo++); //даёт 1, но следующий console.log(boo) дал бы 2
console.log(++foo); //даёт 2, возвращает новое значение foo
Tags:
Hubs:
Total votes 56: ↑27 and ↓29-2
Comments32

Articles