46-JavaScript高级程序设计-高级技巧1高级函数

一、高级函数

1. 安全的类型检测

JavaScript 内置的类型检测机制并非完全可靠。

问题:
typeof 操作符,有一些无法预知的行为,经常会导致检测数据类型时得到不靠谱的结果。Safari(直至第 4 版)在对正则表达式应用 typeof 操作符时会返回 “function”。
instanceof 操作符在存在多个全局作用域(像一个页面包含多个 frame)的情况下,也是问题多多。
在检测某个对象到底是原生对象还是开发人员自定义的对象的时候,也会有问题。

解决方法:在任何值上调用 Object 原生的 toString()方法,都会返回一个[object NativeConstructorName]格式的字符串
Object 的 toString()方法不能检测非原生构造函数的构造函数名。开发人员定义的任何构造函数都将返回[object Object]。
Object.prototype.toString.call(value) // "[object Array]"

检测类型:

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]";
}
// 检测原生 JSON 对象
var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) == "[object JSON]";

注意:对于在 IE 中以 COM 对象形式实现的任何函数,isFunction() 都将返回 false,因为它们并非原生的 JavaScript 函数。

2. 作用域安全的构造函数

构造函数其实就是一个使用 new 操作符调用的函数。当使用 new 调用时,构造函数内用到的 this 对象会指向新创建的对象实例。
问题:当没有使用 new 操作符来调用构造函数的情况。由于 this 对象是在运行时绑定的,所以直接调用 Person(),this 会映射到全局对象 window 上,导致错误对象属性的意外增加。
解决方法:创建一个作用域安全的构造函数。

function Person(name, age, job){
    if (this instanceof Person){ // 检查this对象是Person实例
        this.name = name;
        this.age = age;
        this.job = job;
    } else {
        return new Person(name, age, job);
    }
}
var person1 = Person("Nicholas", 29, "Software Engineer");
alert(window.name);      //""
alert(person1.name);     //"Nicholas"
var person2 = new Person("Shelby", 34, "Ergonomist");
alert(person2.name);     //"Shelby"

关于作用域安全的构造函数的贴心提示:实现这个模式后,你就锁定了可以调用构造函数的环境。如果你使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏。

3. 惰性载入函数

惰性载入表示函数执行的分支仅会发生一次。

实现惰性载入的两种方式:
1)在函数被调用时再处理函数。
在第一次调用的过程中,该函数会被覆盖为另外一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支。
2)在声明函数时就指定适当的函数。
第一次调用函数时就不会损失性能,而在代码首次加载时会损失一点性能。

惰性载入函数的优点:只在执行分支代码时牺牲一点儿性能。两种方式都能避免执行不必要的代码,哪个更合适根据具体需求而定。

4. 函数绑定

函数绑定要创建一个函数,可以在特定的 this 环境中以指定参数调用另一个函数。
该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。

一个简单的 bind() 函数接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并且将所有参数原封不动传递过去。
语法:

function bind(fn, context){
    return function(){
        return fn.apply(context, arguments);
}

ECMAScript 5 为所有函数定义了一个原生的 bind() 方法,进一步简单了操作,不用再自己定义 bind() 函数。
使用方法:fn.bind(context) // 传入作为this值的对象
支持原生 bind() 方法的浏览器:IE9+、Firefox 4+ 和 Chrome。

适用场景:要将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行。
主要用于:事件处理程序以及 setTimeout() 和 setInterval()。
注意:被绑定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所以最好只在必要时使用。

5. 函数柯里化

与函数绑定紧密相关的主题是函数柯里化(function currying),它用于创建已经设置好了一个或多个参数的函数
函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。

柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。
创建柯里化函数的通用方式:

// 这个curry()函数的主要工作就是将被返回函数的参数进行排序
function curry(fn){
    var args = Array.prototype.slice.call(arguments, 1);
    return function(){
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    }; 
}
// curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。
// 这个函数并没有考虑到执行环境,所以调用apply()时第一个参数是null。

调用:

function add(num1, num2){
    return num1 + num2;
}
// 创建了第一个参数绑定为5的add()的柯里化版本
var curriedAdd = curry(add, 5);
alert(curriedAdd(3));   //8
// 柯里化的add()函数两个参数都提供了
var curriedAdd = curry(add, 5, 12);
alert(curriedAdd());   //17

函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的 bind()函数。

function bind(fn, context){
    var args = Array.prototype.slice.call(arguments, 2); 
    return function(){
        var innerArgs = Array.prototype.slice.call(arguments); 
        var finalArgs = args.concat(innerArgs);
        return fn.apply(context, finalArgs);
    };
}

ECMAScript 5 的 bind() 方法也实现函数柯里化,只要在 this 的值之后再传入另一个参数即可。
fn.bind(context, arg));

JavaScript 中的柯里化函数和绑定函数提供了强大的动态函数创建功能。
使用 bind() 还是 curry() 要根据是否需要 object 对象响应来决定。
它们都能用于创建复杂的算法和功能,当然两者都不应滥用,因为每个函数都会带来额外的开销。


上一篇:45-JavaScript高级程序设计-跨域
下一篇:47-JavaScript高级程序设计-高级技巧2防篡改对象&高级定时器

全书整理版:《Javascript高级程序设计》第3版(总结版)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值