1. 程式人生 > >javascript專精--常見高階技巧

javascript專精--常見高階技巧

原文:https://blog.csdn.net/mapbar_front/article/details/78484963 

1、型別檢測
JavaScript中提供兩種型別檢測語法——typeof、instanceof。但是這兩種都有一定的缺陷。

typeof僅僅適用於基本型別的檢測判斷,但無法對Array型別和Object型別進行區分;
instanceof只能對資料進行基本的true或者false的判定,而無法像typeof的方式進行快速判定。
一般成熟的做法是使用Object.prototype.toString.call(value)的方式進行型別判斷。
(1)、typeof的方式:


var num = 1;
console.log(typeof num);//number

var obj = null;
console.log(typeof obj);//object

var arr  = [];
console.log(typeof arr);//object

var obj1 = {};
console.log(typeof obj1);//object

(2)、instanceof的方式
var a = 1;
console.log(a instanceof Array);//false
console.log(a instanceof Number);//true

//優勢是能夠確定是屬於陣列Array還是屬於物件Object
var b = [];
console.log(b instanceof Array);//true
console.log(b instanceof Object);//false

(3)、Object.prototype.toString.call(value)的方式:


這種方式的原理就是:建構函式的原型的toString方法,會返回一個類似”[object type]”的字串。

function getType(value) {
    if(arguments.length !== 1){
        throw new TypeError('引數必須僅僅一個!');
    }
    if(Object.prototype.toString.call(value) == '[object Object]'){
        return 'object';
    } else if(Object.prototype.toString.call(value) == '[object Array]'){
        return 'array';
    } else if(Object.prototype.toString.call(value) == '[object Number]'){
        return 'number';
    } else if(Object.prototype.toString.call(value) == '[object String]'){
        return 'string';
    } else if(Object.prototype.toString.call(value) == '[object Function]'){
        return 'function';
    } else if(Object.prototype.toString.call(value) == '[object Boolean]'){
        return 'boolean';
    } else if(Object.prototype.toString.call(value) == '[object Null]'){
        return 'null';
    } else if(Object.prototype.toString.call(value) == '[object Undefined]'){
        return 'undefined';
    } else {
        throw new Error('這是一個未知的型別!');
    }

}
var a = undefined;
console.log(Object.prototype.toString.call(a));//直接使用Object.prototype.toString.call()




2、建構函式作用域安全機制
建構函式,是面向物件的核心,可是,在使用建構函式的過程中,會有什麼樣的坑呢?

function Cat(name){
    this.name = name;
}

一般而言,new Cat(‘name’)是它的使用方式!!

但是,如果我執行 Cat(“name”) 呢?

導致的結果就是給window物件添加了一個name屬性。

(1)、如何解決???????
function Cat(name){
    if(this instanceof Cat){ 
        this.name = name;
    } else { 
        return new Cat(name);
    }
}

上面的建構函式,就被稱為作用域安全的建構函式。(本質是判斷this的作用域,或者是判斷this的指向問題。)

3、惰性函式載入
惰性函式載入,在我看來,可以認為是一種函式的再次定義。

function add(a,b){
    return function(){ 
        return a + b;
    }

}

這樣的方式有什麼用呢?其實個人覺得,它最大的用處在於瀏覽器相容。一個非常經典的示例就是ajax的封裝!

function ajax(){
    if(XMLHttpRequest){
        var xhr = new XMLHttpRequest();
        //other code
    } else { 
        //code
    }
}
//下面是惰性載入的方式
function ajax(){
    if(XMLHttpRequest){  
        return function(){
            xhr = new XMLHttpRequest();
            //code  
        }
    } else {  
        return function() {}
    }
}

相對而言,第二種函式的使用方式是很有優勢的,第一種函式的方式每次都要判斷瀏覽器是否支援XMLHttpRequest物件,而第二種方式,其實我們只需要判斷一次,這就是惰性函式載入的好處

4、函式柯里化
函式的柯里化,其實就是用已有的函式,傳入適當的引數,得到一個新的函式的過程。

函式柯里化(function currying),官方概念是這樣的:用於建立已經設定好了一個或多個引數的函式。

一般使用閉包的方式進行建立柯里化函式。(下面是一個通用的柯里化函式的建立方式)。

function curry(fn){
    var arg1 = Array.prototype.slice.call(arguments, 1);
    return function(){ 
        var arg2 = Array.prototype.slice.call(arguments);
        return fn.apply(null,arg1.concat(arg2)); 
    }
}

5、函式繫結
個人認為,函式繫結的問題,重點是this的指向問題。

函式繫結中,Function.prototype.bind()是一個重要的實現方式。

一般有下面這樣的使用:

var obj = document.getElementById('root');
obj.onclick = function(){
    console.log(this);//這裡代表obj物件。
}
//也有這樣的方式
obj.onclick = function(){
    console.log(this);//這裡代表window
}.bind(this);

其實bind方法和call、apply有點類似,都是改變了this的指向問題。

有人說,bind()方法的內部實現原理是什麼?我自己用下面的方式寫一個簡單的demo。

Function.prototype.bind = function(obj){
    var self = this;
    return function(){
        self.apply(obj,arguments);
    }
}

考慮到函式的柯里化,我們需要給bind方法進行傳遞引數。bind函式的封裝應該如下:

Function.prototype.bind = function(context){
    var self = this;
    var arg1 = Array.prototype.slice.call(arguments,1);
    return function(){
        var arg2= Array.prototype.slice.call(arguments);
        self.apply(context,arg1.concat(arg2));
    }
}

最後,考慮到原型鏈的繼承,如果想在建構函式中正確的使用bind,可能我們還需要這樣:

Function.prototype.bind = function(context){
    var self = this;
    var F = function(){};
    var arg1 = Array.prototype.slice.call(arguments,1);
    var bound = function(){
        var arg2= Array.prototype.slice.call(arguments);
        self.apply(context,arg1.concat(arg2));
    }
    F.prototype = self.prototype;
    bound.prototype = new F();
    return bound;
}

這樣可能就是最完美的bind方法的實現了。