Может кто-нибудь объяснить этот «двойной отрицательный» трюк? [Дубликат]

178

Я ни в коем случае не эксперт в Javascript, но я читал страницу Mark Pilgrim Dive into HTML5, и он упомянул что-то, что Я хотел бы лучше понять.

Он утверждает:

Наконец, вы используете двойной отрицательный трюк, чтобы заставить результат к логическому значению (true или false).

function supports_canvas() {
  return !!document.createElement('canvas').getContext;
}

Если кто-нибудь может объяснить это немного лучше, я был бы признателен!

Теги:

9 ответов

242
Лучший ответ

Логический оператор NOT ! преобразует значение в логическое значение, противоположное его логическому значению.

Второй ! преобразует предыдущий логический результат обратно в логическое представление его исходного логического значения.

Из этих документов для логического оператора NOT:

Возвращает false, если его единственный операнд может быть преобразован в true; в противном случае возвращает true.

Итак, если getContext дает значение "false", !! заставит его возвращать логическое значение false. В противном случае он вернет true.

Значения "falsey":

  • false
  • NaN
  • undefined
  • null
  • "" (пустая строка)
  • 0
  • 1
    @Grinn: user113716 не перечислил все. Он забыл -0. Я имею в виду не одинарный минус с нулем, а результат того, что является отдельной величиной. Вы можете создать его, например, присвоив -0 переменной.
  • 0
    @MarcodeWit: они равны в JavaScript. -0 === 0 // true user113716 не пропустил ничего интересного для вопроса.
Показать ещё 3 комментария
26

Javascript имеет запутанный набор правил для того, что считается "истинным" и "ложным", когда помещается в контекст, где ожидается логическое ожидание. Но оператор логического-НЕ, !, всегда производит правильное логическое значение (одна из констант true и false). Соединив два из них, идиома !!expression создает правильную булеву с той же правдивостью, что и исходное выражение.

Зачем ты беспокоишься? Потому что он выполняет функции, подобные тем, которые вы показываете более предсказуемыми. Если в нем нет двойного отрицательного значения, он может вернуть объект undefined, a Function или что-то не совсем не похожее на объект Function. Если вызывающая сторона этой функции делает что-то странное с возвращаемым значением, общий код может ошибочно ( "странно" здесь означает "что-либо, кроме операции, которая обеспечивает булевский контекст" ). Двойная отрицательная идиома предотвращает это.

  • 3
    Это не «запутанный» набор правил.
  • 7
    @ Abody97 Список (как показано выше) не является ни настолько коротким, сколько возможно ( false ; для чего-либо еще требуется явный оператор сравнения), ни настолько длинным, насколько это возможно (по крайней мере, добавьте {} и [] ). Таким образом, вы должны запомнить список, а не правило. Это то, что я называю запутанной языковой особенностью.
Показать ещё 2 комментария
12

В javascript оператор "bang" (!) вернет true, если заданное значение истинно, 1, а не null и т.д. Оно вернет false, если значение undefined, null, 0 или пустая строка.

Таким образом, оператор bang всегда будет возвращать логическое значение, но он будет представлять противоположное значение того, с чего вы начали. Если вы снова возьмете результат этой операции и снова "bang", вы можете отменить ее снова, но все равно получите логическое значение (а не undefined, null и т.д.).

Использование бит дважды будет принимать значение, которое могло бы быть undefined, null и т.д., и сделать его просто равным false. Это займет значение, которое могло бы быть 1, "истина" и т.д., И сделать его просто равным true.

Код мог быть написан:

var context = document.createElement('canvas').getContext;
var contextDoesNotExist = !context;
var contextExists = !contextDoesNotExist;
return contextExists;
8

Использование переменной!! дает вам гарантию typecast для boolean.

Чтобы дать вам простой пример:

"" == false (is true)
"" === false (is false)

!!"" == false (is true)
!!"" === false (is true)

Но это не имеет смысла использовать, если вы делаете что-то вроде:

var a = ""; // or a = null; or a = undefined ...
if(!!a){
...

В случае, если он переместит его в boolean, поэтому нет необходимости делать неявный двойной отрицательный отбор.

6

! отбрасывает "что-то" / "все" в boolean.

!! возвращает исходное логическое значение назад (и гарантирует, что выражение является булевым сейчас, независимо от того, что было раньше)

4

document.createElement('canvas').getContext может оценивать либо undefined, либо ссылку на объект. !undefined дает true, ![some_object] дает false. Это почти то, что нам нужно, просто перевернуто. Поэтому !! служит для преобразования undefined в false и ссылки на объект true.

4

Первый ! принуждает переменную к булевскому типу и инвертирует ее. Второй ! снова инвертирует (дает вам исходное (правильное) логическое значение для того, что вы проверяете).

Для ясности вам лучше использовать

return Boolean(....);
  • 5
    Boolean() создает логическое значение в штучной упаковке, которое не ведет себя так же, как примитивное логическое значение, созданное !! (например, typeof сообщит "object" ). Таким образом, !! является предпочтительным.
  • 1
    Чтобы заставить его возвращать примитивное логическое значение: return (new Boolean(...)).valueOf()
Показать ещё 3 комментария
3

Это связано с слабым типом JavaScript. document.createElement('canvas').getContext - это объект функции. Добавляя одиночный !, он оценивает его как булевское выражение и переворачивает ответ. Добавив еще один !, он возвращает ответ. Конечным результатом является то, что функция оценивает его как булево выражение, но возвращает фактический логический результат, а не сам объект функции. Prepending !! - быстрый и грязный способ приведения выражения в булевский тип.

2

Если document.createElement('canvas').getContext не undefined или null, он вернет true. В противном случае он вернет false.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню