Записки на лету

Хорошие привычки C# = плохие привычки JavaScript

Правильное понимание false значений

В отличие от языка C# в JavaScript имеется много значений кроме false, которые также расцениваются как false. Они называются False-y values и очень важно их понимать.

Эти значения это:

  1. false
  2. null
  3. undefined
  4. «»
  5. 0
  6. NaN (not a number)

Все остальные значения, включая «0», «false», и много других странных комбинаций рассматриваются как true.

Устанавливайте значения по умолчанию правильно

Обычно мы проверяем переменную на null перед использованием во избежание исключения «Object reference not set to an instance of an object».

Следующий пример типичен для языка C#.

Никогда не делайте так в JavaScript.

 var someString = "";
 if (someString != null && someString.length > 0) {
 someString = "Default value";
 }

Да и в C# тоже так лучше не делать. Для этих целей есть специальная функция.

 var someString = "";
 if (!string.IsNullOrEmpty(someString)) {
 someString = "Default value";
 }

Учитывая false-y значения упомянутые выше, в JavaScript следует делать так:

 if (someString) {
 someString = "Default value";
 }

В данном случае выполняется проверка на значения undefined, null, и empty string.

Еще один вариант у разработчиков C# может выглядеть так:

someString = someString ? someString : "Default value";

Он чуть получше но, все равно не верен.

Некоторые могут попытаться сделать даже так:

someString = someString ?? "Default value";

Что не поддерживается в JavaScript вообще.

В JavaScript, как ни странно, это должно выглядеть так:

someString = someString || "Default value";

Вывод: Если нам надо проверить переменную является-ли она undefined, null и т.д. или имеет значение.

Все, что нам надо сделать это проверить саму переменную.

Правильно используйте операторы сравнения

Операторы == и != в языке JavaScript попытаются сначала привести сравниваемые значения к одному типу перед сравнением, что при ведет к неожиданным результатам.

Т.е. следующие выражения возвращают true

console.log(0 == '') //Returns true
console.log(0 == '0') //Returns true
console.log(false == '0') //Returns true
console.log(null == undefined) //Returns true
console.log(0 == ' \t\r\n') //Returns true

Используйте операторы === и !== т.к. они так же делают проверку и на тип.

Т.е. следующие выражения возвращают false:

console.log(0 === '') //Returns false
console.log(0 === '0') //Returns false
console.log(false === '0') //Returns false
console.log(null === undefined) //Returns false
console.log(0 === ' \t\r\n') //Returns false

Объявляйте объекты и массивы правильно

Рабочее, но неправильное объявление

var person = new Object(),
keys = new Array();

Ключевое слово new было добавлено в JavaScript, от части, чтобы походить на классические языки, но на самом деле программистов оно больше запутывает чем помогает. В JavaScript есть более соответствующие языку способы объявить объекты и массивы, которые и следует использовать.

var person = {},
keys = [];
var person = {
    firstName: "Vasily",
    lastName: "Pupkin",
    fullName: function () {
        return this.firstName + " " + this.lastName;
    }
},
keys = ["123", "676", "242"];

Объявляйте все Ваши переменные используя «literal notation» вместо оператора new.

Определенно не следует использовать оператор new с переменными типа Boolean, Number и String или с функциями.

Используйте оператор new только когда Вы создаете объект и Вы хотите, чтобы он использовал свой конструктор.

Использование циклов

Цикл for…in в JavaScript не гарантирует порядок полученных элементов, так же Вы можете встретить ряд других проблем.

Например, объявим массив.

var myArray = [], name;
myArray[5] = "test";
console.log(myArray.length);

покажет 6, что, в принципе, вполне предсказуемо.

Создадим цикл…

for(var i = 0, length = myArray.length; i < length; i++){
    console.log(i, myArray[i]);
}

Результатом цикла будет:

//0, undefined
//1, undefined
//2, undefined
//3, undefined
//4, undefined
//5, test

Может возникнуть, так же, другая проблема. Предположим, что кто-то или что-то, скажем какая-нибудь библиотека, которую мы подключили, изменила прототип массива следующим образом.

Array.prototype.someVariable = "Some Variable";
for(name in myArray){
    console.log(name, myArray[name]);
}

Результатом цикла будет:

//5, test
//someVariable, Some Variable

Вместо цикла for…in следует использовать for по крайней мере с массивами.

При использовании for…in с объектами желательно делать проверку на наличие свойства как показано ниже что бы исключить из результата неожиданные объекты наследованные от прототипа.

for(name in myArray){
    if(object.hasOwnProperty(name)){
        //Do something
    }
}

Вывод: Используйте цикл for для массивов и for…in с проверкой методом hasOwnProperty() для объектов.

Правильно используйте скобки

Нижеследующий код работает неправильно.

При клике мышкой на любую ссылку Вы получите «You’ve clicked 10»

var unorderedList = $("ul");
for(var i =0; i < 10; i++){
    $(""), {
        id: i,
        text: "Link " + i,
        click: function() {
        console.log("You've clicked " + i);
        }
    }).appendTo(unorderedList);
}

Исправленный код.

var unorderedList = $("ul"), i;
for(i = 0; i < 10; i++){
    $(""), {
        id: i,
        text: "Link " + i,
        click: function(index) {
            return function(){
                console.log("You've clicked " + index);
            }
        }
    }).appendTo(unorderedList);
}

Область видимости

Область видимости определяется не фигурными скобками а функцией.

Рассмотрим нижеследующий код.

function eat() {
    var food = "bacon",
    isHungry = true;
    if (isHungry) {
        var timeToWait = 10;
        alert("Waiting " + timeToWait + " minutes");
        chew();
    }
    function chew() {
        var bodyPart = "mouth";
    }
    alert("After waiting " + timeToWait + " minutes I am eating " +
    food + " with my " + bodyPart);
}

Разработчик C# посмотрев на код, представленный выше, скорее всего подумает, что переменная timeToWait доступна только в рамках выражения if (isHungry){}, но это не так.

Переменная timeToWait доступна везде внутри функции eat() что значит, что и функция chew() тоже имеет доступ к переменной.

Вывод: Определяйте все свои переменные в начале функции, так чтобы область видимости была очевидна.

Вы можете получить проблемы если в Вашем коде встретится похожая конструкция.

Подъем переменных

В JavaScript все переменные внутри функции находятся в одном поле видимости. Внутри функции JavaScript сначала ищет все переменные и поднимает их объявления в начало функции и инициализирует как undefined.

Присвоение Ваших значений остается там, где Вы это сделали в коде.

Это называется про поднятие переменных (variable hoisting).

Рассмотрим пример.

function sayHello(firstName, middleName, lastName) {
    var fullName = firstName + " " + middleName + " " + lastName;
    if (fullName === "Carlos Rey Norris") {
        var nickName = "Chuck";
        console.log("Hello " + nickName + " " + fullName.split(" ")[2]);
    }
}
sayHello("Carlos", "Rey", "Norris"); //Hello Chuck Norris

JavaScript «За кадром» берет переменные fullName и nickName и объявляет их в начале функции. Их присвоения остаются на своем месте, но их объявления переносятся.

Получается примерно так.

function sayHello(firstName, middleName, lastName) {
    //Hoists all the variable declarations to the top
    //of the function and sets to undefined
    var fullName = undefined,
    nickName = undefined;
    //fullName assignment remains in original position
    fullName = firstName + " " + middleName + " " + lastName;
    if (fullName === "Carlos Rey Norris") {
        //nickName assigned remains in original position
        nickName = "Chuck";
        console.log("Hello " + nickName + " " + fullName.split(" ")[2]);
    }
}
sayHello("Carlos", "Rey", "Norris"); //Hello Chuck Norris

Незнание этого может привести к ошибкам в таких случаях как следующий.

for(var i = 0; i < 10; ++i){
    for(var i = 0; i < 5; ++i){
        alert("Hello");
    }
}

Это будет бесконечный цикл.

Статья написана на основе статьи How Good C# Habits can Encourage Bad JavaScript Habits by Elijah Manor

Перевод и редакция Григорий Жуков

 
Comments

Хорошая статья. Скажите а какая специальная функция есть в C# чтобы обходить такой код !string.IsNullOrEmpty(someString)

В C# работает и то, что Вы написали. А в JS это будет !!someString.