Перевод статьи Everything about null in JavaScript.

Как вы уже, наверное, знаете в языке JavaScript реализовано две большие группы типов данных: примитивы (строки, логические значения, числа, символы) и объекты.

Объекты — это сложные структуры данных. Самым простым объектом в JavaScript является объект представляющий собой набор ключей и соответствующих им значений:

let myObject = {
  name: 'Eric Cartman'
};

Но бывают ситуации, когда объект по разным причинам (отсутствие или некорректные значения аргументов функции и т.д.) не может быть создан. Для таких случаев JavaScript предоставляет специальное значение null, которое указывает на его отсутствие.

let myObject = null;

В этой статье вы узнаете все о null в JavaScript: о его использовании, как можно определить его присутствие, а также о разнице между null и undefined, и почему использование null создает трудности в сопровождении кода.

1. Концепция null

Спецификация JavaScript говорит следующее о null:

null — примитивное значение, которое представляет намеренное отсутствие какого-либо значения типа объект.

Таким образом, если вы замечаете, что значение null было присвоено переменной или возвращается функцией, то этим значением, в общем случае, должен быть объект, но по какой-то причине он не был создан.

Например, функция greetObject() создает объекты, но также она может возвращать null, если объект не может быть создан:

function greetObject(who) {
  if (!who) {
    return null;  }
  return { message: `Hello, ${who}!` };
}

greetObject('Eric'); // => { message: 'Hello, Eric!' }
greetObject();       // => null

И так, при вызове функции со строковым аргументом greetObject('Eric'), как и ожидалось, функция возвращает объект { message: 'Hello, Eric!' }.

Однако при вызове функции без аргументов greetObject() функция возвращается null. Возврат в качестве значения null кажется разумным, так как параметр who по сути не имеет значения, и поэтому объект с информацией о приветствии не может быть создан.

1.1 Аналогия null из реального мира

Думая об аналогии с реальным миром, вы можете представить себе переменную как коробку. Точно так же, как переменная может содержать объект, коробка может содержать некоторые предметы, например, чайник.

Но допустим, что вы получаете коробку, открываете ее… а там ничего! Кто-то ошибся и отправил вам пустой ящик. Коробка ничего не содержит или, иначе говоря, содержит значениеnull.

2. Проверка значения переменной на null

Хороший способ проверить содержит ли значение какой-либо переменной null — использовать оператор строгого равенства :

const missingObject = null;
const existingObject = { message: 'Hello!' };

missingObject  === null; // => true
existingObject === null; // => false

Соответственно инструкция missingObject === null оценивается как true, так как переменная missingObject содержит значение null.

Если переменная содержит ненулевое значение, например, объект, то инструкция existingObject === null оценивается как false.

2.1 null — это ложное значение (false)

null наряду с false, 0, '', undefined, NaN, оцениваются условными операторами как ложные значения false. Если в выражениях с условными операторами встречается одно из этих значений, то JavaScript оценивает его как false.

Boolean(null); // => false

if (null) {
  console.log('null is truthy');
} else {
  console.log('null is falsy'); // logs 'null is falsy'
}

2.2 Тип null

Оператор typeof value определяет тип значения. Например, результатом выполения инструкции typeof 15 является 'number', а результатом выполнения typeof { prop: 'Value' } будет 'object'.

Интересно, каков будет результат следующей инструкции кода: type null?

typeof null; // => 'object'

Хм… как можно определить тип отсутствующего объектакак 'object'? Оказывается, typeof null это тоже 'object', что является следствием ошибки в ранней реализации языка JavaScript.

Не используйте оператор typeof для определения значения типа null. И как упоминалось ранее, используйте для этого оператор строгого равенства myVar === null.

Если вы хотите проверить, содержит ли переменная объект с помощью оператора typeof, то необходимо также предусмотреть проверку на присутствие значения null:

function isObject(object) {
  return typeof object === 'object' && object !== null;
}

isObject({ prop: 'Value' }); // => true
isObject(15);                // => false
isObject(null);              // => false

3. Ловушка null

Значение типа null может неожиданно появиться, в тех случаях, когда вы ожидаете объект. И далее, если вы попытаетесь получить значение какого-либо свойства объекта из значения null, то интерпретатор JavaScript выдаст ошибку.

Давайте снова воспользуемся функцией greetObject()и попробуем получить доступ к свойству message у возвращенного объекта:

let who = '';

greetObject(who).message; 
// throws "TypeError: greetObject() is null"

Поскольку переменная who является пустой строкой, то функция возвращает в качестве значения null. При доступе к свойству message у null соответственно получаем ошибку типа TypeError.

Обычно для того, чтобы избежать такого эффекта используются два альтернативных варианта, которые мы рассмотрим в следующем разделе.

4. Альтернативы использованию null

И хотя очень заманчиво возвращать значение null в том случае, если вы не можете по той или иной причине создать объект, но у этой практики есть существенные недостатки.

Как только значение null появляется в вашем стеке выполнения, то вы обязательно должны проверять его присутствие.

При написании кода я стараюсь не возвращать в функциях null, а вместо этого:

  • возвращать вместо null объект по умолчанию;
  • генерировать вместо возврата null исключение соответствующего типа.

Давайте вернемся к функции greetObject(), возвращающей объект с информацией о приветствии. Вместо возврата null в случае отсутствия при ее вызове значения аргумента, мы можем вернуть объект по умолчанию следующим образом:

function greetObject(who) {
  if (!who) {
    who = 'Stranger';
  }
  return { message: `Hello, ${who}!` };
}

greetObject('Eric'); // => { message: 'Hello, Eric!' }
greetObject();       // => { message: 'Hello, Stranger!' }

Либо возбуждать исключение с выводом соответствующего сообщения об ошибке:

function greetObject(who) {
  if (!who) {
    throw new Error('"who" argument is missing');
  }
  return { message: `Hello, ${who}!` };
}

greetObject('Eric'); // => { message: 'Hello, Eric!' }
greetObject();       // => throws an error

Рассмотренные практики написания кода позволяют вам не использовать null вообще, и избежать проблем с отладкой и сопровождением вашего кода в будущем.

5. null vs undefined

undefined — соответствует значению неинициализированной переменной или свойства объекта.

Например, если вы объявляете переменную без присвоения начального значения, то результатом операции доступа к ее значению переменной будет undefined:

let myVariable;

myVariable; // => undefined

Основное различие между null и undefinedзаключается в том, что значение null интерпретируется как отсутствие в качестве значения некоторого объекта, а — undefined как неинициализированное состояние переменной или возвращаемого значения.

Использование оператора строгого равенства позволяет === позволяет различать значения null и undefined:

null === undefined; // => false

В то время как оператор нестрогого равенства== определяет, что null и undefined эквивалентны:

null == undefined; // => true

Использование оператора нестрого равенства для проверки, содержит ли переменная null или undefined приведет к получению следующего результата:

function isEmpty(value) {
  return value == null;
}

isEmpty(42);                // => false
isEmpty({ prop: 'Value' }); // => false
isEmpty(null);              // => true
isEmpty(undefined);         // => true

6. Заключение

null — специальное значение, реализованное в языке JavaScript, которое соответствует отсутствию объекта, который ожидается в качестве значения переменной или должен возвращаться функцией.

Оператор строгого равенства позволяет определить содержится ли в переменной null: variable === null.

Оператор typoef полезен для определения типа переменной (число, строка, логическое значение). Однако его использование вводит в заблуждение в случае появления значения null, так как результатом выполнения инструкции typeof null будет 'object'.

Значения null и undefined в какой-то степени эквивалентны, тем не менее, null представляют собой отсутствующее значение типа объект, а undefined неинициализированное состояние переменной или значения возвращаемого функцией.

По возможности избегайте возврата из ваших функций значений null, а также установки значения переменных в null. Такая практика приводит к распространению значений типа null и необходимости проверок на null. Вместо этого попробуйте использовать техники с возвратом функцией объекта со свойствами по умолчанию или даже возбуждения исключения, соответствующего типа.

А какое условие вы используете для проверки на null?

Оставить комментарий