国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript中的“黑話”

fjcgreat / 2983人閱讀

摘要:數值表示法科學計數法是一種數學術語,將一個數表示為乘以的次方,如光速萬公里每秒,在計算中通常將米做單位,則記為,而在中我們可使用科學計數法表示。以下情況會自動將數值轉為科學計數法表示小數點前的數字多于位。

因為球是圓的,所以不論發生什么都有可能,對這點我是深信不疑的,但最近我總是在懷疑,JavaScript也是圓的!
什么是“黑話”

黑話,本指舊時江湖幫會人物的暗語、暗號,往往見于小說,后指流行于某一特殊行業中,非局外人所能了解的語言。而本文涉及到的“黑話”,其實是一些利用語言的特征使用的一些不常見的奇淫技巧,JavaScript的語法是十分簡單靈活的,在項目中建議大家遵從ESLint規范編寫可維護性的代碼,各路神仙們也應該進行自我約束,畢竟“黑話”也并不全是什么好的東西,如果很多話可以直接講,何必拐彎抹角的去說呢?

“算術”

算術中的位運算已被作者列為禁術,因此希望你在工程中使用位運算時,請確保你有充足的理由使用,并在需要時寫好Hack注釋。

!與!!

!為邏輯非操作符,可以應用于ECMAScript中的任何值,無論這個值是什么類型,它會被強制轉化為一個布爾值變量,再對其值取反。

!!只是單純的將操作數執行兩次邏輯非,它能將任意類型的值轉化為相應的布爾值,它包含的步驟為:

將一個值轉化為布爾值;

將其取反;

再次取反。

假設你需要通過一個布爾型變量表示是否有id值,以下寫法推薦你使用最后一種方式來進行轉化:

const enable1 = !!id;
const enable2 = id ? true : false;
const enable3 = Boolean(id);
~ 與 ~~

~表示按位取反,~5的運行步驟為:

轉為一個字節的二進制表示:00000101,

按位取反:11111010

取其反碼:10000101

取其補碼:10000110

轉化為十進制:-6

至于原碼、反碼、補碼原理請看原理篇。

~~它代表雙非按位取反運算符,如果你想使用比Math.floor()更快的方法,那就是它了。需要注意,對于正數,它向下取整;對于負數,向上取整;非數字取值為0,它具體的表現形式為:

~~null;      // => 0
~~undefined; // => 0
~~Infinity;  // => 0
--NaN;       // => 0
~~0;         // => 0
~~{};        // => 0
~~[];        // => 0
~~(1/0);     // => 0
~~false;     // => 0
~~true;      // => 1
~~1.9;       // => 1
~~-1.9;      // => -1
+

在變量值前使用+的本意是將變量轉換為數字,在一個函數接受數字類型的參數時特別有用:

+"1" // 1
+"-1" // "-1
+[] // 0
+{} // NaN

根據觀察,+aa * 1結果類似。除此之外,使用+也可以作為立即執行函數:+function() {}(),等效于(function(){})()。

字符串與數字相加時會將數值默認轉為字符串,因此有了一下將數字轉為字符串的快捷方法:"" + 1。

& 與 &&

如何你是從類C語言過來的話,請拋棄之前的刻板印象:&可以充當邏輯操作符號。在JavaScript中,&只能進行位運算。

&,它表示按位與,此運算符需要兩個數字并返回一個數字。如果它們不是數字,則會轉換為數字。如果執行7 & 3, 則會經過以下步驟:

先轉換為2進制: 111 & 11

比較結果為:011

將二進制轉回十進制,因此:7 & 3 = 3

它也可用于基偶數判斷:const isOdd = num => !!(num & 1);

&&,表示邏輯與,通常用于if條件判斷,可跟你想象的不太一樣,&&并不是單純的返回true或者false,而是依據:

若第一個表達式為false,則返回第一個表達式;

若第一個表達式為true,返回第二個表達式。

在這里舉幾個例子:

0 && false          0 (both are false-y, but 0 is the first)
true && false       false (second one is false-y)
true && true        true (both are true-y)
true && 20          20 (both are true-y)

&&可以連接多個操作符,如:a && b && c && d,返回值的規則與上面一樣。除此以外,它還經常被作為短路邏輯使用:若前面表達式不是truthy,則不會繼續執行之后的表達式。如在取一個對象的屬性,我們需要先判斷是否為空才能進行取值,否則會拋出Uncaught TypeError,這種情況下一般我們也會通過邏輯或,給與表達式一個默認值:

const value = obj && obj.value || false

當JavaScript壓縮工具遇到if判斷時,也會使用&&短路邏輯從而節省內存空間:

// before
if (test) { alert("hello") }
// after
test && alert("hello")
| 與 ||

它們與&&&使用方法很相似,不同的是它們表示的是邏輯或,因此使用|會進行按位或運算,而||會返回第一個Truthy值。

使用||進行默認值賦值在JavaScript中十分常見,這樣可以省略很多不必要的if語句,比如:

// before
let res;
if (a) {
  res = a;
} else if (b) {
  res = b;
} else if (c) {
  res = c;
} else {
  res = 1;
}

// after
const res = a || b || c || 1;
== 與 ===

==為相等運算符,操作符會先將左右兩邊的操作數強制轉型,轉換為相同的操作數,再進行相等性比較。

===為全等運算符,它除了在比較時不會將操作數強制轉型,其余相等判斷與==一致。

簡單而言,==用于判斷值是否相等,===判斷值與類型是否都相等,因此使用全等運算符判斷操作數會更準確,新手也在學習JavaScript接收到的前幾條Tips就是避免使用相等運算符,真的是這樣嗎?沒錯,這樣能確保在你不徹底熟悉語言的情況下,盡可能的去避免犯錯,但是我們也應該清楚在哪些情況下應該使用相等運算符,規則往往只針對于新手,而對聰明的你來說,最重要的是要清楚自己在做什么。

相等操作符對于不同類型的值,進行的比較如下圖所示:

? B
? ? Undefined Null Number String Boolean Object
A Undefined true true false false false IsFalsy(B)
Null true true false false false IsFalsy(B)
Number false false A === B A === ToNumber(B) A=== ToNumber(B) A=== ToPrimitive(B)
String false false ToNumber(A) === B A === B ToNumber(A) === ToNumber(B) ToPrimitive(B) == A
Boolean false false ToNumber(A) === B ToNumber(A) === ToNumber(B) A === B ToNumber(A) == ToPrimitive(B)
Object false false ToPrimitive(A) == B ToPrimitive(A) == B ToPrimitive(A) == ToNumber(B) A === B

針對于undefined與null:undefined與null互等,與其余任意對象都不相等,因此在某些lib里,你可能會看到如下寫法:

if (VAR == undefined) {}
if (VAR == null) {}

它等效于:

if (VAR === undefined || VAR === null) {}

對于 "", false, 0而言,他們都屬于Falsy類型,通過Boolean對象都會轉換為假值,而通過==判斷三者的關系,他們總是相等的,因為在比較值時它們會因為類型不同而都被轉換為false值:

console.log((false == 0) && (0 == "") && ("" == false)) // true

或者有時候我們希望利用強轉特性比較字符串與數字:

console.log(11 == "11") // true
console.log(11 === "11") // false
^

按位異或運算符,對比每一個比特位,當比特位不相同時則返回1,否則返回0。很少人在Web開發中使用此運算符吧,除了傳說中的一種場景:交換值。

若要交換a與b的值,如果可以的話推薦你使用:

[a, b] = [b, a];

或者新建一個c,用于存儲臨時變量,如果你遇到有人這樣書寫:

// 異或運算,相同位取0,不同位取1,a ^ b ^ b = a, a ^ a ^ b = b
a = a ^ b
b = a ^ b
a = a ^ b

這樣通過異或運算進行交換兩個數字型變量,請原諒他并忽視它,他只可能是一個醉心于魔法的初心者,并祝愿他早日發現,簡潔易讀的函數才是最佳實踐。

數值表示法 3e9

科學計數法是一種數學術語,將一個數表示為a乘以10的n次方,如光速30萬公里每秒,在計算中通常將米做單位,則記為:300000000m/s,而在JavaScript中我們可使用科學計數法 3e9表示。

在這里舉幾個科學計數法的示例:

1e5; // 100000
2e-4; // 0.0002
-3e3; // -3000

Number對象有toExponential(fractionDigits)方法以科學計數法返回該數值的字符串表示形式,參數fractionDigits可選,用于用來指定小數點后有幾位數字,例如:(179000).toExponential(); // "1.79e+5"

以下情況JavaScript會自動將數值轉為科學計數法表示:

小數點前的數字多于21位。

小數點后的零多于5個。

.5px

通常某些人習慣省略0.開頭的數字,常見于數值計算、css屬性中,比如0.5px可直接寫為.5px,0.2 * 0.3可寫為: .2 * .3

0x、0o和0b

在十進制的世界里呆久了,請不要忘記還有其他進制的存在,在計算機中它們是同地位的。JavaScript提供了以下進制的表示方法:

二進制:只用0和1兩個數字,前綴為0b,十進制13可表示為0b1101

八進制:只用0到7八個數字,前綴為0o、0,十進制13可表示為0o15、015

十六進制:只用0到9的十個數字,和a到f六個字母,前綴為0x,十進制13可表示為0xd

默認情況下,JavaScript 內部會自動將八進制、十六進制、二進制轉為十進制再進行運算。從十進制轉其他進制請查閱toString方法,從其他進制轉十進制請查閱parseInt方法,從其他進制轉其他進制請先轉為十進制再轉為其他方法。

“話術” Array.prototype.sort

Array.prototype.sort()默認根據字符串的Unicode編碼進行排序,具體算法取決于實現的瀏覽器,在v8引擎中,若數組長度小于10則使用從插入排序,大于10使用的是快排。

而sort支持傳入一個compareFunction(a, b)的參數,其中a、b為數組中進行比較的兩個非空對象(所有空對象將會排在數組的最后),具體比較規則為:

返回值小于0,a排在b的左邊

返回值等于0,a和b的位置不變

返回值大于0,a排在b的右邊

因此利用sort即可寫一個打亂數組的方法:

[1,2,3,4].sort(() => .5 - Math.random())

但是以上的實現并不是完全隨機的,究其原因,還是因為排序算法的不穩定性,導致一些元素沒有機會進行比較,具體請參考問題,在抽獎程序中若要實現完全隨機,請使用 Fisher–Yates shuffle 算法,以下是簡單實現:

function shuffle(arrs) {
  for (let i = arrs.length - 1; i > 0; i -= 1) {
    const random = Math.floor(Math.random() * (i + 1));
    [arrs[random], arrs[i]] = [arrs[i], arrs[random]];
  }
}
Array.prototype.concat.apply

apply接收數組類型的參數來調用函數,而concat接收字符串或數組的多個參數,因此可使用此技巧將二維數組直接展平:

Array.prototype.concat.apply([], [1, [2,3], [4]])

而通過此方法也可以寫一個深層次遍歷的方法:

function flattenDeep(arrs) {
  let result = Array.prototype.concat.apply([], arrs);
  while (result.some(item => item instanceof Array)) {
    result = Array.prototype.concat.apply([], result);
  }
  return result;
}

經過測試,效率與lodash對比如下:

對上述方法中的Array.prototype.concat.apply([], target)亦可以寫成:[].concat(...target)。

Array.prototype.push.apply

在es5中,若想要對數組進行拼接操作,我們習慣于使用數組中的concat方法:

let arrs = [1, 2, 3];
arrs = arrs.concat([4,5,6]);

但還有酷的方法,利用apply方法的數組傳參特性,可以更簡潔的執行拼接操作:

const arrs = [1, 2, 3];
arrs.push.apply(arrs, [4, 5, 6]);
Array.prototype.length

它通常用于返回數組的長度,但是也是一個包含有復雜行為的屬性,首先需要說明的是,它并不是用于統計數組中元素的數量,而是代表數組中最高索引的值:

const arrs = [];
arrs[5] = 1;
console.log(arrs.length); // 6

另外,length長度隨著數組的變化而變化,但是這種變化僅限于:子元素最高索引值的變化,假如使用delete方法刪除最高元素,length是不會變化的,因為最高索引值也沒變:

const arrs = [1, 2, 3];
delete arrs[2]; // 長度依然為3

length還有一個重要的特性,那就是允許你修改它的值,若修改值小于數組本身的最大索引,則會對數組進行部分截取:

const arrs = [1, 2, 3, 4];
arrs.length = 2; // arrs = [1, 2]
arrs.length = 0; // arrs = []

若賦予的值大于當前最大索引,則會得到一個稀疏數組:

const arrs = [1, 2];
arrs.length = 5; // arrs = [1, 2,,,,]

若將值賦為0,則執行了清空數組的操作:

const arrs = [1, 2, 3, 4];
arrs.length = 0; // arrs = []

使用此方法會將數組中的所有索引都刪除掉,因此也會影響其他引用此數組的值,這點跟使用arrs = []有很大的區別:

let a = [1,2,3];
let b = [1,2,3];
let a1 = a;
let b1 = b;
a = [];
b.length = 0;
console.log(a, b, a1, b1); // [], [], [1, 2, 3], []

在對length進行修改的時候,還需要注意:

值需要為正整數

傳遞字符串會被嘗試轉為數字類型

Object.prototype.toString.call

每個對象都有一個toString(),用于將對象以字符串方式引用時自動調用,如果此方法未被覆蓋,toString則會返回[object type],因此Object.prototype.toString.call只是為了調用原生對象上未被覆蓋的方法,call將作用域指向需要判斷的對象,這樣一來就可以通過原生的toString方法打印對象的類型字符串: Object.prototype.toString.call([]) => "[object Array]" ,利用這個特性,可以較為精確的實現類型判斷。

在ES3中,獲取到的type為內部屬性[[Class]]屬性,它可以用來判斷一個原生屬性屬于哪一種內置的值;在ES5中新增了兩條規則:若this值為null、undefined分別返回: [object Null]、[object Undefined];在ES6中不存在[[Class]]了,取而代之的是一種內部屬性:[[NativeBrand]],它是一種標記值,用于區分原生對象的屬性,具體的判斷規則為:

19.1.3.6Object.prototype.toString ( )
When the toString method is called, the following steps are taken:

If the this value is undefined, return "[object Undefined]".
If the this value is null, return "[object Null]".
Let O be ! ToObject(this value).
Let isArray be ? IsArray(O).
If isArray is true, let builtinTag be "Array".
Else if O is a String exotic object, let builtinTag be "String".
Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
Else if O has a [[Call]] internal method, let builtinTag be "Function".
Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
Else, let builtinTag be "Object".
Let tag be ? Get(O, @@toStringTag).
If Type(tag) is not String, set tag to builtinTag.
Return the string-concatenation of "[object ", tag, and "]".
This function is the %ObjProto_toString% intrinsic object.

NOTE
Historically, this function was occasionally used to access the String value of the [[Class]] internal slot that was used in previous editions of this specification as a nominal type tag for various built-in objects. The above definition of toString preserves compatibility for legacy code that uses toString as a test for those specific kinds of built-in objects. It does not provide a reliable type testing mechanism for other kinds of built-in or program defined objects. In addition, programs can use @@toStringTag in ways that will invalidate the reliability of such legacy type tests.
Object.create(null)

用于創建無“副作用”的對象,也就是說,它創建的是一個空對象,不包含原型鏈與其他屬性。若使用const map = {}創建出來的對象相當于Object.create(Object.prototype),它繼承了對象的原型鏈。

JSON.parse(JSON.stringify(Obj))

很常用的一種深拷貝對象的方式,將對象進行JSON字符串格式化再進行解析,即可獲得一個新的對象,要注意它的性能不是特別好,而且無法處理閉環的引用,比如:

const obj = {a: 1};
obj.b = obj;
JSON.parse(JSON.stringify(obj)) // Uncaught TypeError: Converting circular structure to JSON

這樣通過JSON解析的方式其實性能并不高,若對象可通過淺拷貝復制請一定使用淺拷貝的方式,不管你使用{...obj}還是Object.assign({}, obj)的方式,而如果對性能有要求的情況下,請不要再造輪子了,直接使用npm:clone這個包或是別的吧。

“理論” Truthy與Falsy

對每一個類型的值來講,它每一個對象都有一個布爾型的值,Falsy表示在Boolean對象中表現為false的值,在條件判斷與循環中,JavaScript會將任意類型強制轉化為Boolean對象。
以下這些對象在遇到if語句時都表現為Falsy:

if (false)
if (null)
if (undefined)
if (0)
if (NaN)
if ("")
if ("")
if (document.all)

document.all屬于歷史遺留原因,所以為false,它違背了JavaScript的規范,可以不管它,而NaN這個變量,千萬不要用全等或相等對其進行判斷,因為它發起瘋來連自己都打:

console.log(NaN === 0) // false
console.log(NaN === NaN) // false
console.log(NaN == NaN) // false

但是我們可以使用Object.is方法進行判斷值是否為NaN,它是ES6新加入的語法,用于比較兩個值是否相同,它可以視為比全等判斷符更為嚴格的判斷方法,但是不可混為一談:

Object.is(NaN, NaN) // true
Object.is(+0, -0) // false

而除了Falsy值,所有值都是Truthy值,在Boolean上下文中表現為true。

原碼, 反碼, 補碼

在JavaScript進行位運算時,采用32位有符號整型,即數字5有以下表示方式:

原碼:00000000 00000000 00000000 00000101

反碼:00000000 00000000 00000000 00000101

補碼:00000000 00000000 00000000 00000101

而數字-5的表示方式為:

原碼:10000000 00000000 00000000 00000101

反碼:11111111 11111111 11111111 11111010

補碼:11111111 11111111 11111111 11111011

綜上所述,有以下規律:

正數的原碼、反碼、補碼都是它本身

負數的反碼:在其原碼的基礎上, 符號位不變,其余各個位取反

負數的補碼:負數的反碼 + 1

那么它們到底有什么用呢?其實位運算就是用計算機底層電路所有運算的基礎,為了讓計算機的運算更加簡單,而不用去辨別符號位,所有值都采用加法運算,因此,人們設計了原碼,通過符號位來標識數字的正負:

1 = 0000 0001
-1 = 1000 0001

假如計算機要對兩個數相加:1 + (-1),使用原碼相加的運算結果為:10000010,很明顯-2并不是我們想要的結果,因此出現了反碼,若使用反碼進行運算會有什么結果呢,讓我們來看一下:

1[反碼] + (-1)[反碼] =  0000 0001 + 1111 1110 = 11111111[反碼] = 10000000[原碼]

此時運算結果是正確的,可是這樣還存在一個問題,有兩個值可以表示0:1000 0000、0000 0000,對于計算機來說,0帶符號是沒有任何意義的,人們為了優化0的存在,設計出了補碼:

1[補碼] + (-1)[補碼] =  0000 0001 + 1111 1111 = 00000000[原碼]

這樣一來,-0的問題就可以解決了。

參考資料

https://modernweb.com/45-useful-javascript-tips-tricks-and-best-practices/

https://dmitripavlutin.com/the-magic-behind-array-length-property/

https://medium.freecodecamp.org/9-neat-javascript-tricks-e2742f2735c3

https://stackoverflow.com/questions/7310109/whats-the-difference-between-and-in-javascript

http://javascript.ruanyifeng.com/grammar/number.html

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98146.html

相關文章

  • AI 黑話大全

    摘要:意思就是地板上的真相,基礎事實。在樸素貝葉斯方法訓練垃圾郵件分類的任務中,人工標注的結果就是一個。對比的名詞是,統計學上意思是在合理條件人類可實現下能夠達到的最好結果。等于廢話,什么都沒說。如果回歸模型正確的話,可以將殘差看作誤差的觀測值。 - Ground truth In machine learning, the term ground truth refers to the a...

    李世贊 評論0 收藏0
  • Java虛擬機類加載過程

    摘要:二驗證驗證主要是為了確保文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機的自身安全。五初始化類的初始化階段是類加載過程的最后一步,該階段才真正開始執行類中定義的程序代碼或者說是字節碼。 關注我,每天三分鐘,帶你輕松掌握一個Java相關知識點。 虛擬機(JVM)經常出現在我們面試中,但是工作中卻很少遇到,導致很多同學沒有去了解過。其實除了應付面試,作為java程序員,了解...

    lentoo 評論0 收藏0
  • 2017年2月份前端資源分享

    平日學習接觸過的網站積累,以每月的形式發布。2017年以前看這個網址:http://www.kancloud.cn/jsfron... 1. Javascript 前端生成好看的二維碼 十大經典排序算法(帶動圖演示) 為什么知乎前端圈普遍認為H5游戲和H5展示的JSer 個人整理和封裝的YU.js庫|中文詳細注釋|供新手學習使用 擴展JavaScript語法記錄 - 掉坑初期工具 漢字拼音轉換...

    lily_wang 評論0 收藏0
  • 2017年2月份前端資源分享

    平日學習接觸過的網站積累,以每月的形式發布。2017年以前看這個網址:http://www.kancloud.cn/jsfron... 1. Javascript 前端生成好看的二維碼 十大經典排序算法(帶動圖演示) 為什么知乎前端圈普遍認為H5游戲和H5展示的JSer 個人整理和封裝的YU.js庫|中文詳細注釋|供新手學習使用 擴展JavaScript語法記錄 - 掉坑初期工具 漢字拼音轉換...

    chengjianhua 評論0 收藏0
  • 2017年2月份前端資源分享

    平日學習接觸過的網站積累,以每月的形式發布。2017年以前看這個網址:http://www.kancloud.cn/jsfron... 1. Javascript 前端生成好看的二維碼 十大經典排序算法(帶動圖演示) 為什么知乎前端圈普遍認為H5游戲和H5展示的JSer 個人整理和封裝的YU.js庫|中文詳細注釋|供新手學習使用 擴展JavaScript語法記錄 - 掉坑初期工具 漢字拼音轉換...

    Anonymous1 評論0 收藏0

發表評論

0條評論

fjcgreat

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<