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方法的实现了。

 

猜你喜欢

转载自blog.csdn.net/wenmin1987/article/details/83478613