一些冷門的js操做

本文來源於個人github

0.前言

你們學習的時候,一開始小白,而後接觸到進階的東西的時候,發現一切駕輕就熟,有的人可能開始說精通了。忽然有一天,發現了一些基於很基礎的東西的騷操做,就開始懷疑人生:wtf JavaScript? 若是沒有遇到被某些東西打擊到或者見識到新的世界,可能永遠的,就感嘆:jQuery真好用,我精通jQuery,精通js。要不就是,vue?angular?react?我都會,我精通。javascript

然而,我如今只能說我只是熟悉,越學發現越多坑。 對於數據類型轉換和正則的坑,前面有講過: 數據類型 正則表達式vue

1.數組

相似一些遍歷類型的api:forEach、map,可能有人就說了:不就是arr.map(x=>x+1),要是涉及到索引,那就再來個index,arr.map((x,index)=>x+index),多簡單是否是 而後,先拋出一個問到爛的面試題: ['1','2','3'].map(parseInt) 找工做的人,看過面試題的,都知道結果是[1,NaN,NaN],那麼爲何會這樣呢? 首先,map裏面能夠傳兩個參數:map(對每個元素都調用的函數,該函數的this值) 而那個每個元素都調用的函數,他傳入的參數是(current,index,arr)當前元素(必須)、索引、數組。java

1.1對parseInt精通了嗎

在這個例子,對parseInt傳入的就是這三個參數。 而parseInt這個原生的函數,他傳入的參數是(num,radix): radix表示要解析的數字的基數。該值介於 2 ~ 36 之間。 若是省略該參數或其值爲 0,則數字將以 10 爲基礎來解析。若是它以 「0x」 或 「0X」 開頭,將以 16 爲基數。若是該參數小於 2 或者大於 36,則 parseInt() 將返回 NaNreact

parseInt(10,10)//對10進制數字10取整,10
parseInt(10,2)//對2進制數字10取整,2
parseInt(111,2)//對2進制數字111取整,7
parseInt(111,4)//對4進制數字111取整,21
parseInt('1',2,[1])//1,傳入第三個數組是沒有意義的
parseInt('1',2,['1','2','3','4'])//1
parseInt('2',2,['1','2','3','4'])//NaN,2進制沒有2
parseInt('2',3,['1','2','3','4'])//3
複製代碼

那就很明顯了,對於爲何是[1,NaN,NaN],其實就是:git

parseInt('1',0,['1','2','3'])//1,radix爲 0,則數字將以 10 爲基礎來解析
parseInt('2',1,['1','2','3'])//1進制是啥?反正就是NaN
parseInt('3',2,['1','2','3'])//2進制沒有3
複製代碼

另外,parseInt,遇到字符串的數字他會盡可能解釋,直到不能解釋就中止。github

parseInt('123sfd')//123
parseInt('123sfd123')//123
parseInt('sfd213')//NaN
複製代碼

1.2數組遍歷的api第二個參數

第二個參數,表示前面那個函數內部的this,通常不填這個參數,嚴格模式this是undefined。而在非嚴格模式下,this指向window: [1,2,3].forEach(function(x){console.log(this)})//window 咱們能夠強行把他改爲其餘的看看: [1,2,3].forEach(function(x){console.log(this)},Math)//Math面試

1.3讓Array成精

咱們知道,Array能夠直接生成某一個長度元素全是空的數組: Array(5)//注意了,5空和5個undefined是不一樣的 不妨apply一下:正則表達式

Array.apply(null, { length:5 })// [undefined, undefined, undefined, undefined, undefined]
Array.apply(null, { '0':1,length:5 })// [1, undefined, undefined, undefined, undefined]
Array.apply(null, { '2':1,length:5 })// [undefined, undefined, 1, undefined, undefined]
複製代碼

至關於給新建立的array的屬性直接進行改變api

1.4你是否是用循環生成一個序列?

生成一個序號數組:數組

var arr = [];
for(var i = 0;i<10;i++){
    arr.push(i)
}
複製代碼

常規操做,沒什麼問題,可是精通jQuery的你會不會用到其餘方法呢? 好比:

Array.apply(null, {length:5 }).map(f.call,Number)//[0, 1, 2, 3, 4],f能夠是任何函數
Array.apply(null, { '0':1,length:5 }).map(f.call,Number)//[0, 1, 2, 3, 4],無論元素是什麼都同樣
Array.apply(null, {length:5 }).map(f.call,Boolean)//[false, true, true, true, true]
Array.apply(null, {length:5 }).map(f.call,String)//["0", "1", "2", "3", "4"]
Array.apply(null, {length:5 }).map(eval.call,Object)//[Number, Number, Number, Number, Number]
複製代碼

對於最後一個結果,點開第二個看看

1

map的那個函數,傳入了三個參數,第一個參數是當前元素。但是對於加了call的,第一個參數就是call的this指向了。而map後面的那個參數,就是this,也就是後面那個this已經把current覆蓋,起到主導做用的也是後面那個參數。而map的第一個參數fn,fn裏面的第二個參數是index,也就是當前索引。而對於f.call,第一個參數是this指向,第二個參數之後全是f.call的函數f所使用的參數。最後,就至關於對每個元素進行,Number(index),Boolean(index),String(index),Object(index)

2.位操做符

基本用法和概念就不說了,自行看文檔。

2.1字符串轉數字

有全世界都知道的parseInt,可是咱們又能夠用簡單一點的方法:

//就是讓他進行不改變自己的值的數學運算
+"1"
"1"*1
"1"|0
"1" >> 0
"1" << 0
複製代碼

2.2更多的操做

要是咱們要隨意獲得一個很大的數,通常就是9999*9999這樣子吧,而位移操做能夠至關於乘上2的n次方:1<<30//1073741824 好像沒什麼用,先拋出一個需求:隨機生成字符串(數字+字母) 我知道,不少人徹底不須要思考,直接拿起鍵盤擼:好比生成一個6位隨機字符串

var n = 6
var str = 'abcdefghijklmnopqrstuvwxyz0123456789'
var result = ''
for (var i = 0 ;i<n;i++){
   result += str[parseInt(Math.random()*(str.length+1))]
}
複製代碼

對於位操做,就是短短的代碼解決:

(~~(Math.random()*(1<<24))).toString(16)
(~~(Math.random()*(1<<30))).toString(36)
複製代碼

首先生成一個大數,再轉進制。16進制就是0-9加上前面幾個字母,36進制就是0-9加上26個字母,那麼咱們能夠獲得一個穩定的生成n位隨機字符串版本:

function f(n){
	if(n==1) return (~~(Math.random()*36)).toString(36)
	return (~~(Math.random()*36)).toString(36) + f(n-1)
}
//尾遞歸優化
function k(n){
	return function f(n,s){
		if(n==1) return s
		return  f(n-1,(~~(Math.random()*36)).toString(36)+s)
	}(n,(~~(Math.random()*36)).toString(36))
}
複製代碼

另外一種方法:(也是基於高進制) 咱們能夠從Math.random().toString(36)獲得一個0.xxx後面有11位小數的字符串,因此咱們只要取第2位之後的就能夠了Math.random().toString(36).slice(2)

來一段小插曲

對於追求代碼極其簡短的強迫症,看見上面的if -else,忽然想起來平時寫了一大堆if-else實在是不順眼,好的,除了無腦if和簡短的三元表達式,咱們還有短路表達式: || && a&&b:a爲true,跑到b a||b:a爲false,跑b,a爲true就取a

//來一個有點智障的例子
function f(a){
if(a==1) console.log(1)
if(a==2) console.log(2)
if(a==3) console.log(3)
}

//必定要記得寫分號
function f(a){
(a==1)&& console.log(1);
(a==2) &&console.log(2);
(a==3) &&console.log(3);
}
複製代碼

若是在實際應用上面,代碼將會大大簡潔,可是可能第一次讓別人看難以理解

位操做交換倆整數

不用中間變量,加減法實現交換 a = a+b;b = a-b;a = a-b 用位操做: a ^= b; b ^= a; a ^= b;

具體過程能夠這樣子證實:

咱們先令a0 = a, b0 = b。a = a ^ b 能夠轉化爲a = a0 ^ b
第二句:b = b ^ a =  b0 ^ a = b0 ^ (a0 ^ b0) = a0 ^ (b0 ^ b0) = a0 ^ 0 = a0//達到了原始值a0和實際值b交換
第三句同樣:a = a ^ b = a ^ (b0 ^ a) = b0 ^ (a ^ a)= b0 ^ 0 = b0,和原始的b0交換成功
複製代碼

3. 構造類

繼續回到前面的例子:

Array.apply(null, {length:5 }).map(f.call,Number)//[0, 1, 2, 3, 4],f能夠是任何函數
Array.apply(null, { '0':1,length:5 }).map(f.call,Number)//[0, 1, 2, 3, 4],無論元素是什麼都同樣
Array.apply(null, {length:5 }).map(f.call,Boolean)//[false, true, true, true, true]
Array.apply(null, {length:5 }).map(f.call,String)//["0", "1", "2", "3", "4"]
Array.apply(null, {length:5 }).map(eval.call,Object)//[Number, Number, Number, Number, Number]
複製代碼

map第二個參數ctx是this指向,而第一個參數是一個函數f(任何函數),f的第一個參數已經報廢,由於第一個參數是call的上下文this,可是這個this又被後面的ctx替代了,所以f有效的參數是從第二個開始,最後就至關於ctx(index),便是 :構造類(index)

因而咱們又能夠看看構造類另外一個有意思的地方

var toFixed = 1;
var obj = {
     toFixed:"我只是客串的",
    show:function(){
        return this. toFixed;
    }
}
obj.show.call( toFixed);   //ƒ toFixed() { [native code] }
複製代碼

也許一眼看上去是1,然而call的第一個參數竟然不是null、undefined,效果不同了。 咱們call的上下文就是toFixed。能夠這樣理解,對於js內部,1實際上是構造類Number(1)構造出來的,至關於this指向了Number,而咱們能夠打印一下Number.prototype,結果有

1
咱們把toFixed方法打印出來了

對於String也是能夠的

var big = '1sdasdsadsdasd';//不是字符串的話,其餘構造類沒有big方法,返回undefined
var obj = {
    big:"我是客串的",
    show:function(){
        return this.big;
    }
}
obj.show.call(big);  //ƒ big() { [native code] }

//或者說,打印一個length看看
var l = '1sdasdsadsdasd';//變量換成l
var obj = {
    length:"我是客串的",
    show:function(){
        return this.length;//主要是這個,變量是什麼不重要
    }
}
obj.show.call(l); //14
複製代碼

屬性太多了,能夠去控制檯看看String.prototype有什麼。

再或者說,看看函數執行起來會發生什麼事情:

var l = true;//此次試一下boolean類型
var obj = {
    length:"我是客串的",
    show:function(){
        return this.valueOf();//此次咱們不打印這個函數了,讓他執行
    }
}
obj.show.call(l,this); //true,直接調用類型轉換過程當中的那個valueOf
複製代碼

道理都是同樣,你們本身能夠回去玩一下

相關文章
相關標籤/搜索