【JavaScript】 高级函数

函数是JavaScript中最有趣的部分之一。它们本质上十分简单和过程化,但也可以是非常复杂的,一些额外的功能可以使用闭包来实现,此外,由于所有函数都是对象,所以使用函数指针非常简单。接下来,描述集中在JavaScript中使用函数的高级方法。

一、安全的类型检测

问题:

1、typeof操作符,有一些无法预知的行为,经常会曹植检测数据类型时得到不靠谱的结果。比如在Safary对正则表达式应用typeof操作符时会返回function。
2、instanceOf 操作符在存在多个全局作用域的情况下,也有问题。

解决办法:

用Object.prototype.toString

在任何值上调用Object原生的toString()方法,都会返回一个[Object NativeConstructorName]格式的字符串。由于原生数组的构造函数名与全局作用域无关,因此使用toString()就能保证返回一致的值。
var arr = new Array ();
console.log(Object.prototype.toString.call(arr));

这里写图片描述

安全的类型检测:

//检测是否为数组
function isArray (value) {
    return Object.prototype.toString.call(value) == "[object Array]";
}
//检测是否为函数
function isFunction (value) {
    return Object.prototype.toString.call(value) == "[object Function]";
}
//检测是否为正则表达式
function isRegExp (value) {
    return Object.prototype.toString.call(value) == "[object RegExp]";
}

二、作用域安全的构造函数

问题:

我们都知道,当使用new调用构造函数时,构造函数内用到的this对象会指向新创建的对象实例,但是当没有使用new操作符来调用构造函数时,就会出现问题。因为构造函数的this对象是在运行时绑定的,如果直接调用构造函数,this会映射到全局对象window上。
例如:

function Person (name,age,job) {
            this.name = name;
            this.age = age;
            this.job = job;
        }
        var person = Person("Jay",37,"star");
        //console.log("Name:" + person.name);//报错,person没有name属性
        console.log("Name:" + window.name);//Name:Jay

解决:

创建一个作用域安全的构造函数。
作用域安全的构造函数在进行任何更改之前,首先确认this对象是正确类型的实例。如果不是,那么会创建新的实例并返回。
如:

/*作用域安全的构造函数*/
function Person (name,age,job) {
    if (this instanceof Person) {
        this.name = name;
        this.age = age;
        this.job = job;
    } else {
        return new Person(name,age,job);
    }
}

以上代码,构造函数中添加了一个检查并确保this对象是Person实例的if语句,这样不管是否使用new操作符,都会返回一个Person的新实例。

var person1 = new Person ("Jay",37,"star");
console.log(person1.name);//Jay
console.log(window.name);//""

var person2 = Person("Hanna",23,"star");
console.log(person2.name);//Hanna

构造函数窃取

以上的作用域安全的构造函数在构造函数窃取模式下,也会有问题。

function Polygon (sides) {
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function () {
            return 0;
        };
    } else {
        return new Polygon (sides);
    }
}

function Rectangle (width,height) {
    Polygon.call(this,2);
    this.width = width;
    this.height = height;
    this.getArea = function () {
        return this.width * this.height;
    };
}
var rect = new Rectangle(10,15);
alert(rect.sides);//undefined!!!!

上述代码,Polygon构造函数是作用域安全的,但是Rectangle不是。Rectangle实例通过Polygon.call( )继承Polygon的sides属性,但是,由于Polygon是作用域安全的,this对象并非是Polygon的实例,所以会创建并返回一个新的Polygon对象。

解决:构造函数窃取+原型链

function Polygon (sides) {
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function () {
            return 0;
        };
    } else {
        return new Polygon (sides);
    }
}

function Rectangle (width,height) {
    Polygon.call(this,2);
    this.width = width;
    this.height = height;
    this.getArea = function () {
        return this.width * this.height;
    };
}

Rectangle.prototype = new Polygon();//原型链

var rect = new Rectangle(10,15);
alert(rect.sides);

上述代码中,由于一个Rectangle实例同时也是一个Polygon实例,所以Polygon.call( )会照愿意执行。

三、惰性载入函数

惰性载入函数,表示函数执行的分支仅会发生一次,避免执行不必要的代码
其有两种方式实现:

1、在函数被调用时再处理函数。

第一次调用的过程中,该函数会覆盖为另外一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支了。
使用惰性载入重写createXHR()

//惰性载入函数重写createXHR()
function createXHR(){
    if (typeof XMLHttpRequest != "undefined") {
        createXHR = function () {
            return new XMLHttpRequest();//IE5以上版本直接创建XHR对象
        };      
    } else if (typeof ActiveXObject != "undefined") {
        createXHR = function () {
            if (typeof arguments.callee.activeXString != "string") {
                var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"];
                var i;
                var len =versions.length;
                for (i = 0; i < len; i++) {
                    try{
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch(ex){
                        //跳过
                    }
                }
            }
            return  new ActiveXObject (arguments.callee.activeXString);
        };      
    } else{
        createXHR = function () {
            throw new Error ("No XHR object available.");
        };      
    }
}

if 语句的每个分支都会为createXHR变量赋值,有效覆盖了原有函数。

2、在声明函数时就指定适当的函数

这样,第一次调用函数时就不会损失性能了,而在代码首次加载时会损失一点性能。

//创建XHR对象(兼容IE5以前)
function createXHR(){
    if (typeof XMLHttpRequest != "undefined") {
        return function () {
            return new XMLHttpRequest();//IE5以上版本直接创建XHR对象
        };      
    } else if (typeof ActiveXObject != "undefined") {
        return function () {
            if (typeof arguments.callee.activeXString != "string") {
                var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"];
                var i;
                var len =versions.length;
                for (i = 0; i < len; i++) {
                    try{
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch(ex){
                        //跳过
                    }
                }
            }
            return  new ActiveXObject (arguments.callee.activeXString);
        };      
    } else{
        return function () {
            throw new Error ("No XHR object available.")
        }       
    }
}

以上代码的技巧就是,创建一个匿名、自执行的函数,用以确定应该使用哪一个函数实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值