2015-12-28 8 views
41

Я просматривал код для фабрики angularjs, чтобы лучше понять, как это работает. Код содержит заявление if, которое я не совсем понимаю.+ !! оператор в выражении if

В plnkr демо автор писал:

if ((+!!config.template) + (+!!config.templateUrl) !== 1) { 
    throw new Error('Expected modal to have exactly one of either `template` or `templateUrl`'); 
} 

It is slightly different in the github repo:

if (!(!config.template^!config.templateUrl)) { 
    throw new Error('Expected modal to have exactly one of either `template` or `templateUrl`'); 
} 

Очевидно сообщением об ошибке он проверяет, чтобы увидеть, если один из этих двух существует. Я просто не знаю, как это получается. Я не смог найти информацию о ^ или +!

Мой вопрос: как это работает, если заявление работает? (^ или +! или +!! специфически)

+8

^является Javascript побитовое Оператор XOR. – Jacques

+0

'Я не смог найти никакой информации о^или +!', Вы не использовали правильное ключевое слово. поиск 'javascript operator' –

+0

Первый! принимает значение - как config.template (что, вероятно, число из-за + !!), преобразует его в true или false на основании того, является ли оно нулевым (false) или ненулевым (true) - которое инвертирует логический смысл - то второй! инвертирует его обратно в один и тот же логический смысл вместо того, чтобы быть инвертированным, тогда + обрабатывает true/false как число 1 или 0. Поэтому, если он не установлен, + !! приведет к * номеру * 0, в противном случае это будет * номер * 1 * независимо от того, какое фактическое значение числа .template равно *. Затем они могут делать + между двумя выражениями. – simpleuser

ответ

72

!! converts a value to a boolean (true or false).+ затем преобразует это логическое значение в число, либо 1 для true или 0 для ложного.

> +true 
1 
> +false 
0 

Лично я считаю его более четким, чтобы написать что-то вроде этого, когда имеет дела с двумя булевыми:

if (!config.template == !config.templateUrl) { 
    throw ... 
} 

Код ясность и читаемость проклята, по-видимому.

+4

Обратите внимание, что если эти два действительно логические, тогда вы также можете написать' if (config.template == config.templateUrl) {} '... однако одна или обе из них, вероятно, являются строкой, а другая - неопределенной или пустой, поэтому операторы'! 'имеют значение. Также было бы неплохо вставить дополнительные строки, чтобы улавливать обе ошибки отдельно; 'if (template && templateUrl) throw new Error (« Увидели оба шаблона и templateUrl, которые вы действительно хотите использовать? »); if (! template &&! templateUrl) throw new Error («Не видел ни шаблона, ни шаблонаURL, мне нечего рисовать!»); ' –

+3

Мне лично не нравится' + !! ', но стоит отметить, что он чтобы «было точно указано 1 из {3 вещи}» более чисто. На мой взгляд, наиболее читаемым будет что-то вроде 'if (trueyCount (item1, item2, item3)! == 1)' –

+0

@torazaburo. Я уверен, что это правильно ... есть внешнее отрицание вокруг XOR. Разве у меня этот мозг пугал? –

28

Это ужасный читаемость способ выписать логическое значение переменной, а затем преобразовать его с помощью одинарного преобразования, чтобы дать результат на 0/1 номер.

Рассмотрим:

+!!true; //this converts true to false, then to true again, and true is 1 when converted 
+!!0; //converts 0 (falsy) to true, then false, and then the numeric 0 

Технически говоря !! не является его собственным оператором, это просто НЕ (!) оператор дважды.

Унарное преобразование: ECMA spec doc Унарный плюс пытается преобразовать в целое число. Number() также будет действительным преобразованием.

+0

'Номер' является подходящей заменой здесь, но' parseInt' не будет полезен для преобразования a * boolean * для числа, правда, правильно? – apsillers

+3

Я часто слышал оправдание: «Этот код настолько сложный/сложный/важный/что-то в этом роде, если вы не знаете, как разбирать таких операторов, как тыльная сторона руки, вы не должны возиться с этим кодом в любом случае «. но, честно говоря, все, что я слышу, - «Давайте сделаем этот невероятно сложный код еще сложнее разобрать!» – corsiKa

+2

Неверный. 'parseInt (!! x)' не совпадает с '+ !! x'. Первый - «NaN». Правильность в сторону, я не уверен, что такое * ужасная удобочитаемость. '+ !! x' является устойчивым и недвусмысленным. (Хотя выражение в целом может быть упрощено по мере того, как указывает Мэтт.) –

6

Между тем ^ является побитовым оператором XOR.

При работе с числами, меньшими, чем 2, ^ будет работать, как логическое ИЛИ (||), если вы считаете 0 = false и 1 = true.

+0

Боковое примечание, его сравнительно медленное в javascript из-за необходимости конвертировать операнды в 32-битные целые числа, а затем обратно в 64-битные поплавки. –

6

! является логическим оператором. Это унарный оператор, который преобразует его операнд в логическое и затем отрицает его. !! - это только тот оператор, второй ! отменяет отрицание, поэтому конечным результатом является просто преобразование в логическое.

+ является оператором унарного плюса, который преобразует его операнд в число. В случае логического значения false становится 0, а true становится 1.

Итак, +!!(expression) оценивает 1, если выражение является правдивым и 0, если выражение является ложным.

3
if ((+!!config.template) + (+!!config.templateUrl) !== 1) { 

      0   +   0    !== 1 true 
      0   +   1    !== 1 false 
      1   +   0    !== 1 false 
      1   +   1    !== 1 true 

равна

if (!config.template === !config.templateUrl) { 

несмотря на содержание этих двух свойств.

33

+ !! использует неявное преобразование для приведения значения как 0 или 1 в зависимости от его логического значения

По большей части это проверка на наличие. Например, пустая строка является ложью (!!"" === false), и поэтому не определена, и ряд других. Те, являются основными, хотя два

"Falsey" конверсии

+!!""  === 0 
+!!false  === 0 
+!!0   === 0 
+!!undefined === 0 
+!!null  === 0 
+!!NaN  === 0 

"Truthy" конверсии

+!!1   === 1 
+!!true  === 1 
+!!"Foo"  === 1 
+!!3.14  === 1 
+!![]  === 1 
+!!{}  === 1 

, если ((+ !! config.template) + (+ !! config.templateUrl)! == 1)

Надеюсь, это имеет смысл в этом вопросе. Объект config имеет два свойства, которые мы рассматриваем. .template и .templateUrl. Неявное преобразование в 0 или 1 с использованием +!! будет добавлено, а затем сравнивается, чтобы убедиться, что оно не равно 1 (что означает, что оно равно 0 или 2). Свойства могут быть как включенными, так и выключенными, но не разными.

Таблица истинности здесь заключается в следующем:

template templateUrl (+!!) + (+!!)  !==1 
"foo"  "foo"    1 + 1   true 
undefined undefined   0 + 0   true 
undefined ""     0 + 0   true 
""   undefined   0 + 0   true 
12   ""     1 + 0   false 
""   12     0 + 1   false 
undefined "foo"    0 + 1   false 
""   "foo"    0 + 1   false 
"foo"  ""     1 + 0   false 
"foo"  undefined   1 + 0   false 

гораздо простой способ, чтобы все это было бы просто использовать неявное логическое преобразование

if (!config.template === !config.templateUrl) 
+1

Ваш простой метод - это меньше кода, да, но я бы сказал, что он менее читабель для тех, кто раньше не видел этот шаблон (логический xor не появляется часто). Ваша версия требует некоторого размышления: «отрицание этого должно быть равно отрицанию этого» по сравнению с «не этим xor not this» (отрицание в коде оператора OP, конечно, связано с тем, что они проверяют, выполняется ли это условие не смогли). Я лично разбил бы это на две строки: var hasValidTemplate = !! config.template^config.templateUrl; если (! hasValidTemplate) '. – Kat

+0

[продолжение - предыдущий комментарий также забыл '!!' перед 'config.templateUrl'] Принуждение' !! 'к булевому, к сожалению, необходимо в вышеуказанном коде, к сожалению, необходимо из-за того, что'^'является побитовым xor и нам нужен логический xor, для которого мы должны преобразовать в booleans. Альтернативой читаемости может быть «var hasTemplate = !! config.template; var hasTemplateUrl = !! config.templateUrl; if (! (hasTemplate^hasTemplateUrl) '. – Kat

Смежные вопросы