你不知道的js技巧

jopen 8年前發布 | 7K 次閱讀 JavaScript開發 JavaScript

JS進階

說起這個應該算是老生常談了吧。所謂的高級,其實就是講了一些我們平常用不到(或許用了不知道),但是非常實在的東西。算是熟練掌握js的一個必經road吧。

檢測函數類型

其實檢測函數的類型應該算是js的一個痛點,因為js是一門弱類型的語言,對類型的檢測不是那么看重。但隨著JS的發展,類型變得更加豐富。而檢測類型的復雜度,也變得復雜了~ (MD). 大致梳理一下吧。如果你想檢測值類型(Number,String,Boolean,undefined,null,Symbol). 使用typeof就可以了

typeof 23; //"number"
typeof "webpack"; //"string"
typeof true; //"boolean"
typeof undefined;  //"undefined"
typeof symbol();  //"symbol"
//但是有個呆毛null.
typeof null; //"object" 如果理解原型鏈的話,那就無可厚非了

而檢測自定義類型,或者原生的應用類型,則需要使用到instanceof

let obj = new Object();
obj instanceof Object; //true
...

但是有時候情況往往不是這么簡單。 比如如果你想檢測iframe里面的屬性值的話,基本上是不可能的。因為檢測的前提要求是在同一個全局作用于下。所以為了能夠正常檢測一些值的類型(排除在iframe的情況).那有沒有什么萬能的方法呢? 確實有,你可以使用調用toString()的方法,得到相關的類型.

let value = new FormData();
console.log(Object.prototype.toString.call(value)); //"[object FormData]"

是不是感覺特別情切呢。 你也可以更進一步的提取。要知道,我們是有情懷的淫。

function getType(value){  //基本上可以返回所有的類型,不論你是自定義還是原生
    return Object.prototype.toString.call(value).match(/\s{1}(\w+)/)[1];
}
let obj = new Object();
console.log(getType(obj)); //"Object"

作用域安全的構造函數

關于函數的坑應該是無處不在(誰叫他是js里面最難懂的一個類型)。關于函數里面的this得說明一下。只有函數在運行的時候,函數里面的this才會真正的綁定.這就造成了一個問題,即,如果你在全局不小心運行了一個函數,那結果就呵呵了。 因為此時,你的this代表的window.這樣你會污染到全局的相關屬性,造成一個蜜汁bug.所以,為了安全需要在創建時,對this指針做一個判斷.

function Father(name){
    this.name = name;
}
var jimmy =  Father("jimmy");  //這樣會污染全局變量window.name的屬性。造成重寫
console.log(window.name); //"jimmy"
console.log(jimmy.name); //"jimmy"
//修改過后
function Father(name){
    if(this instanceof Father){
        this.name = name;
    }else{
        return new Father(name);
    }
}
var jimmy =  Father("jimmy");  //保證了作用域的安全性
console.log(window.name); //"xxx" 
console.log(jimmy.name); //"jimmy"

惰性載入函數

這個應用最多的場景應該是兼容性判斷吧。比如你寫了一個判斷綁定事件方法的檢測函數

function bind(ele,fn,type){
    if(document.addEventListener){  //檢測現代瀏覽器
        ele.addEventListener(type,fn,false);
    }else if(document.attachEvent){  //檢測低版本的IE
        ele.attachEvent(type,fn);
    }
}
let ele = document.querySelector("#first");
bind(ele,function(){console.log("hehe");},'click');  //執行一次判斷
bind(ele,function(){console.log("hehe");},'dbclick');  //第二次執行判斷
bind(ele,function(){console.log("hehe");},'mouseover');  //第三次執行判斷
...

如果你綁定的事件越多,那么他每次綁定時都會執行一次判斷. 為了減少判斷次數,可以使用惰性載入函數,即,先判斷再返回函數.

function bind(){
    if(document.addEventListener){
        bind = function(ele,fn,type){
            ele.addEventListener(type,fn,false);
        }
    }else if(document.attachEvent){  //檢測低版本的IE
        bind = function(ele,fn,type){
            ele.attachEvent(type,fn);
        }
    }else{
      throw "u browser is from outer space";
    }
}
bind();  //首先檢測一遍,然后返回對應的檢測版本
console.log(bind);  //可以檢測一下現在bind里面的內容
//當然如果不爽的話可以直接使用匿名函數,直接執行
var bind = (function(){
    if(document.addEventListener){
       return function(ele,fn,type){
            ele.addEventListener(type,fn,false);
        }
    }else if(document.attachEvent){  //檢測低版本的IE
        return function(ele,fn,type){
            ele.attachEvent(type,fn);
        }
    }else{
      throw "u browser is from outer space";
    }
})();
console.log(bind);

本人推薦下面哪種寫法,因為言簡意賅,不用顯示調用~.

函數的綁定

這個坑應該大多數人都踩過.比如我使用單例,創建了一系列的函數和內容.然后再執行綁定.

let sendMsg = {
    ele: document.querySelector('#element'),
    change:function(){
        this.ele.classList.toggle(".active"); //改變狀態
    }
}
document.querySelector('#button').addEventListener('click',sendMsg.change,false);

意淫的效果是,點擊#button元素,#element會改變狀態。但實際是會報錯。找不到你的ele.

原因出現在,綁定事件的回調函數是在全局作用域中執行的。 上面那種寫法,就像當對于把change函數的代碼給拷貝到第二個參數.

document.querySelector('#button').addEventListener('click',function(){
    this.ele.classList.toggle(".active"); //改變狀態
},false);

而執行的時候,是在window的全局環境里執行的。所以會拋出錯誤。解決辦法就是創建一個閉包,來保存這個調用方法的作用域.

document.querySelector('#button').addEventListener('click',function(){
    sendMsg.change();
},false);

這樣就不會出錯了。但這樣寫有悖我們作為一名代碼藝術家的風格。 通常是不提倡使用閉包的(即不要讓別人看出來你在使用閉包). 這時候可以自己創建一個綁定函數(I call it as 代理)

function bind(fn,context){
    return function(){
        fn.apply(context,arguments);  //arguments是作為參數傳入的
    }
}
//上面的閉包可以改為
document.querySelector('#button').addEventListener('click',bind(sendMsg.change,sendMsg),false);

在es5中,每個函數都自帶了自已bind的方法,這樣就更容易,讓別人看不出,你在使用閉包了。

document.querySelector('#button').addEventListener('click',sendMsg.change.bind(sendMsg),false);

由于這個方法只兼容到IE9+,所以遇到IE8的時候你就呵呵了.

函數的Curry

函數的柯里化應該算是函數綁定的一個升級版。但他們兩個有個共同點就是: 都是用了閉包并且返回了一個函數. 但是Curry 可以額外的傳入參數,這是函數綁定所不具備的.關于Curry還有一個好處就是,實現自定義參數函數的重用性.

//這是JS高程上面的例子
function curry(fn){
    var args = Array.prototype.slice.call(arguments,1);
    return function(){
        var innerArgs = Array.prototype.slice.call(arguments);  
        var final = args.concat(innerArgs);
    }
}
function add(num1,num2){
    return num1+num2;
}
var Cadd = curry(add,5);
console.log(Cadd(3)); //8
console.log(Cadd(5)); //10

可以重寫上面的bind

function bind(fn,context){
    var args = Array.prototype.slice.call(arguments,2); //獲取上面兩個參數以外的其余參數
    return function(){
        //獲取你第二次傳入的參數,并轉化為數組
        var innerArgs = Array.prototype.slice.call(arguments);  
        var final = args.concat(innerArgs);
        fn.apply(context,final);  //使用apply解析參數并調用.
    }
}

這樣我們就可以傳入多個參數,而且還可以自定義參數. 當然也可以使用原來的調用方式。差不多了,覺得上面的如果你用到了,說明你的js水平應該有一些,如果沒有用到的話,可以當做學習,萬一以后踩坑了,應該知道自己是怎么屎的~

來自: http://segmentfault.com/a/1190000004267888

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!