2015-12-28 32 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的值(這可能是因爲+ !!而產生的一個數字),根據它是零(假)還是非零(真)將其轉換爲真或假 - 這反轉了邏輯意義 - 然後第二個!將其反轉回相同的邏輯意義而不是反轉,那麼+將真/假當作數字1或0.所以,如果它沒有設置,+ !!將導致* number * 0,否則它將是* number * 1 *,無論.template的實際數值是多少*。然後他們可以做兩個表達式之間的+。 – simpleuser

回答

72

!! converts a value to a boolean (true or false).+然後該布爾變換爲號碼,要麼1true0爲假。

> +true 
1 
> +false 
0 

我個人覺得它更清晰的寫這樣的事情,有兩個布爾打交道時:

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

代碼的清晰度和可讀性被定罪,顯然。

+4

請注意,如果這兩個確實是布爾值,那麼你也可以寫'if(config.template == config.templateUrl ){...}但是其中的一個或兩個可能是一個字符串,而另一個是'undefined'或'null',這就是爲什麼'!'操作符有所作用的原因。只需插入額外的行來分別捕獲兩個錯誤也會很好; 'if(template && templateUrl)throw new Error(「Saw both template and templateUrl; which which you do actually want to use?」);如果(!template &&!templateUrl)拋出新的錯誤(「既不是模板也不是templateURL,我沒有任何東西可以渲染!」);' –

+3

我個人不喜歡'+ !!'但值得注意的是它會規模「更準確地預期{3件事情}中的1件」。在我看來,大多數可讀性就像'if(truthyCount(item1,item2,item3)!== 1)' –

+0

@torazaburo我很確定這是正確的......異或的外部否定。我放棄了這個放屁嗎? –

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 

技術上講!!不是它自己的運營商,它只是NOT(!)運營商的兩倍。

一元轉換:ECMA spec doc一元加試圖轉換爲整數。​​也將是一個有效的轉換。

+0

'Number'在這裏是一個合適的替換,但'parseInt'對於轉換不會有用一個*布爾*爲一個數字,但是,對不對? – apsillers

+3

我經常聽到這樣的理由:「這段代碼太複雜/複雜/重要/不管怎麼說,如果你不知道如何解析這些操作符就像手背一樣,那麼不管怎樣,你都不應該搞亂這段代碼。 「。但老實說,我所聽到的是「讓我們製作這個令人難以置信的複雜代碼更難解析!」 – corsiKa

+2

不正確。 'parseInt(!! x)'與'+ !! x'不一樣。前者是'NaN'。拋開正確性,我不確定*可怕的可讀性*理由是什麼。 '+ !! x'非常有效而且毫不含糊。 (雖然整個表達式可以簡化,但是馬特指出) –

6

同時,^是按位異或運算符。

當小於2的數字打交道,^會像一個布爾OR(||)如果你考慮0 = false1 = true

+0

注意,由於需要將操作數轉換爲32位整數,然後返回到64位浮點數,所以它的JavaScript相對較慢。 –

6

!是邏輯非運算符。它是一個一元運算符,它將其操作數轉換爲布爾值,然後否定它。 !!就是那個運算符的兩倍,第二個!解除了否定,所以最終的結果就是轉換爲布爾值。

+是一元加運算符,它將操作數轉換爲數字。在布爾型的情況下,假成爲0,並且成爲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 

IF((+!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不是這個」(OP的否定語句代碼,當然是因爲他們正在檢查這個條件失敗)。我會親自把它分成兩行,用'var hasValidTemplate = !! config.template^config.templateUrl;如果(!hasValidTemplate)'。 – Kat

+0

[繼續 - 前面的註釋也忘了config.templateUrl之前的'!!']在上面的代碼中,布爾型的'!!'強制是非常必要的,因爲'^'是按位異或我們需要邏輯xor,爲此我們必須轉換爲布爾值。另一種可讀性可以是'var hasTemplate = !! config.template; var hasTemplateUrl = !! config.templateUrl;如果(!(hasTemplate^hasTemplateUrl)'。 – Kat