JavaScript中的高级函数

在JavaScript中,函数的功能十分强大。它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函数,不仅如此,还能被一个函数返回!可以说,在JS中,函数无处不在,无所不能,堪比孙猴子呀!当你运用好函数时,它能助你取西经,让代码变得优雅简洁,运用不好时,那就遭殃了,要大闹天宫咯~

除了函数相关的基础知识外,掌握一些高级函数并应用起来,不仅能让JS代码看起来更为精简,还可以提升性能。以下是小编总结的一些常用的、重要的高级函数,加上了一些个人见解,特此记录下来。如果您是JS初学者,也不要被“高级”两个字吓到,因为文中穿插讲解了一些原型、this等基础知识,相信并不难理解。如果您是JS大牛,也可以把本文用来查漏补缺。

正文

作用域安全的构造函数

function Person(name,age){
 this.name = name;
 this.age = age;
}
var p1 = new Person("Claiyre",80);

相信您对上面的构造函数一定不陌生,但是,,如果某个粗心的程序猿调用这个构造函数时忘记加 new 了会发生什么?

var p3 = Person("Tom",30);
console.log(p3);    //undefined
console.log(window.name);  //Tom

由于使用了不安全的构造函数,上面的代码意外的改变了window的name,因为 this 对象是在运行时绑定的,使用new调用构造函数时 this 是指向新创建的对象的,不使用 new 时, this 是指向window的。

由于window的name属性是用来识别链接目标和frame的,所在这里对该属性的偶然覆盖可能导致其他错误。

作用域安全的构造函数会首先确认 this 对象是正确类型的实例,然后再进行更改,如下:

function Person(name,age){
 if(this instanceof Person){
 this.name = name;
 this.age = age;
 } else {
 return new Person(name,age);
 }
}

这样就避免了在全局对象上意外更改或设置属性。

实现这个安全模式,相当于锁定了调用构造函数的环境,因此借用构造函数继承模式可能会出现问题,解决方法是组合使用原型链和构造函数模式,即组合继承。

如果您是一个JS库或框架的开发者,相信作用域安全的构造函数一定对您非常有用。在多人协作的项目中,为了避免他们误改了全局对象,也应使用作用域安全的构造函数。

惰性载入函数

由于浏览器间的行为差异,代码中可能会有许多检测浏览器行为的if语句。但用户的浏览器若支持某一特性,便会一直支持,所以这些if语句,只用被执行一次,即便只有一个if语句的代码,也比没有要快。

惰性载入表示函数执行的分支仅会执行一次,有两种实现惰性载入的方式,第一种就是在函数第一次被调用时再处理函数,用检测到的结果重写原函数。

function detection(){
 if(//支持某特性){
 detection = function(){
 //直接用支持的特性
 }
 } else if(//支持第二种特性){
 detection = function(){
 //用第二种特性
 }
 } else {
 detection = function(){
 //用其他解决方案
 }
 }
}

第二种实现惰性载入的方式是在声明函数时就指定适当的函数

var detection = (function(){
 if(//支持某特性){
 return function(){
 //直接用支持的特性
 }
 } else if(//支持第二种特性){
 return function(){
 //用第二种特性
 }
 } else {
 return function(){
 //用其他解决方案
 }
 }
})();

惰性载入函数的有点是在只初次执行时牺牲一点性能,之后便不会再有多余的消耗性能。

函数绑定作用域

在JS中,函数的作用域是在函数被调用时动态绑定的,也就是说函数的this对象的指向是不定的,但在一些情况下,我们需要让某一函数的执行作用域固定,总是指向某一对象。这时怎么办呢?

当当当~~可以用函数绑定作用域函数呀

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

用法:

var person1 = {
 name: "claiyre",
 sayName: function(){
 alert(this.name);
 }
}
var sayPerson1Name = bind(person1.sayName,person1);
sayPerson1Name(); //claiyre

call 函数和 apply 函数可以临时改变函数的作用域,使用bind函数可以得到一个绑定了作用域的函数

函数柯里化(curry)

curry的概念很简单:只传递部分参数来调用函数,然后让函数返回另一个函数去处理剩下的参数。可以理解为赋予了函数“加载”的能力。

许多js库中都封装了curry函数,具体使用可以这样。

var match = curry(function(what,str){
 return str.match(what)
});
var hasNumber = match(/[0-9]+/g);
var hasSpace = match(/\s+/g)
hasNumber("123asd");  //['123']
hasNumber("hello world!"); //null
hasSpace("hello world!"); //[' '];
hasSpace("hello");   //null
console.log(match(/\s+/g,'i am Claiyre')); //直接全部传参也可: [' ',' ']

一旦函数经过柯里化,我们就可以先传递部分参数调用它,然后得到一个更具体的函数。这个更具体的函数通过闭包帮我们记住了第一次传递的参数,最后我们就可以用这个更具体的函数为所欲为啦~

一个较为简单的实现curry的方式:

function curry(fn){
 var i = 0;
 var outer = Array.prototype.slice.call(arguments,1);
 var len = fn.length;
 return function(){
 var inner = outer.concat(Array.prototype.slice.call(arguments));
 return inner.length === len?fn.apply(null,inner):function (){
 var finalArgs = inner.concat(Array.prototype.slice.call(arguments));
 return fn.apply(null,finalArgs);
 }
 }
}

debounce函数

debounce函数,又称“去抖函数”。它的功能也很简单直接,就是防止某一函数被连续调用,从而导致浏览器卡死或崩溃。用法如下:

var myFunc = debounce(function(){
 //繁重、耗性能的操作
},250);
window.addEventListener('resize',myFunc);

像窗口的resize,这类可以以较高的速率触发的事件,非常适合用去抖函数,这时也可称作“函数节流”,避免给浏览器带来过大的性能负担。

具体的实现时,当函数被调用时,不立即执行相应的语句,而是等待固定的时间w,若在w时间内,即等待还未结束时,函数又被调用了一次,则再等待w时间,重复上述过程,直到最后一次被调用后的w时间内该函数都没有被再调用,则执行相应的代码。

实现代码如下:

function debounce(fn,wait){
 var td;
 return function(){
 clearTimeout(td);
 td= setTimeout(fn,wait);
 }
}

once函数

顾名思义,once函数是仅仅会被执行一次的函数。具体实现如下:

function once(fn){
 var result;
 return function(){
 if(fn){
 result = fn(arguments);
 fn = null;
 }
 return result;
 }
}
var init = once(function(){
 //初始化操作
})

在被执行过一次后,参数fn就被赋值null了,那么在接下来被调用时,便再也不会进入到if语句中了,也就是第一次被调用后,该函数永远不会被执行了。

还可以对上述once函数进行改进,不仅可以传入函数,同时还可以给传入的函数绑定作用域u,同时实现了bind和once。

function once(fn,context){
 var result;
 return function(){
 if(fn){
 result = fn.apply(context,arguments);
 fn = null;
 }
 return result;
 }
}

结语

通过以上的阅读,不难发现很多“高级函数”的实现其实并不复杂,数十行代码便可搞定,但重要的是能真正理解它们的原理,在实际中适时地应用,以此性能提升,让代码简洁,逻辑清晰

(0)

相关推荐

  • javascript中利用柯里化函数实现bind方法【推荐】

    • 柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预先存储的值进行相关的操作处理即可: • 柯里化函数主要起到预处理的作用: • bind方法的作用:把传递进来的callback回调方法中的this预先处理为上下文context; /** * bind方法实现原理1 * @param callback [Function] 回调函数 * @par

  • javascript性能优化之分时函数的介绍

    分时函数和函数节流的问题不一样,函数节流针对的事件不是用户主动调用的,前面已经提过了. 函数节流的原理是:延迟当前函数的执行,如果该次延迟还没有完成,那么忽略接下来该函数的请求.也就是说会忽略掉很多函数请求. 在一些开发场景中,我们可能会一次性向文档中注入上千个节点,在短时间内向浏览器中大量添加DOM节点可能会让浏览器吃不消,结果往往会让浏览器卡顿或吃不消,解决方案之一便是使用分时函数(timeChunk). timeChunk分时函数让创建节点的工作分批进行,比如一秒钟创建1000个节点,改为

  • 深入浅出理解JavaScript高级定时器原理与用法

    本文实例讲述了JavaScript高级定时器原理与用法.分享给大家供大家参考,具体如下: setTimeout()和setInterval()可以用来创建定时器,其基本的用法这里就不再做介绍了.这里主要介绍一下javascript的代码队列.在javascript中没有任何代码是立即执行的,一旦进程空闲则尽快执行.所以说定时器中设置的时间并不代表执行时间就一定相符,而是代表代码会在指定时间间隔后加入到队列中进行等待.如果在这个时间点上,队列中没有其他东西,那么这段代码就会被执行,表面上看上去好像

  • JavaScript高级函数应用之分时函数实例分析

    本文实例讲述了JavaScript高级函数应用之分时函数.分享给大家供大家参考,具体如下: 一.前提介绍: 某些函数是用户主动调用的,但因为一些客观的原因,这些函数会严重影响页面的性能. 二.应用场景: 在短时间内往页面中大量添加DOM节点显然会让浏览器吃不消,往往会导致浏览器的卡顿甚至假死. 三.解决方案: 以上述添加节点的例子来说明问题,我们将创建节点的工作分批进行,比如把1秒钟创建1000个节点,改为每隔200毫秒创建8个节点. 四.实现代码如下: var timeChunk = func

  • Js setInterval与setTimeout(定时执行与循环执行)的代码(可以传入参数)

    Document自带的方法: 循环执行:var timeid = window.setInterval("方法名或方法","延时");window.clearInterval(timeid); 定时执行:var tmid = window.setTimeout("方法名或方法", "延时");window.clearTimeout(tmid); 举例说明: A.当要执行的方法中不需要参数时 复制代码 代码如下: <scr

  • javascript中利用柯里化函数实现bind方法

    柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预先存储的值进行相关的操作处理即可: 柯里化函数主要起到预处理的作用: bind方法的作用:把传递进来的callback回调方法中的this预先处理为上下文context; /** * bind方法实现原理1 * @param callback [Function] 回调函数 * @param con

  • javascript中有趣的反柯里化深入分析

    写在前面的话:国内对前端的研究在某些方面也不逊色于国外,这篇文章虽然看不太懂,但我很欣赏这种深入研究的精神! 反科里化的话题来自javascript之父Brendan Eich去年的一段twitter. 近几天研究了一下,觉得这个东东非常有意思,分享一下.先忘记它的名字,看下它能做什么. 不要小看这个功能,试想下,我们在写一个库的时候,时常会写这样的代码,拿webQQ的Jx库举例. 我们想要的,其实只是借用Array原型链上的一些函数.并没有必要去显式的构造一个新的函数来改变它们的参数并且重新运

  • 深入剖析JavaScript中的函数currying柯里化

    curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名).   柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程. 柯里化一个求和函数 按照分步求值,我们看一个简单的例子 var concat3Words = function (a, b, c) { r

  • js定时器的使用(实例讲解)

    在javascritp中,有两个关于定时器的专用函数,分别为: 1.倒计定时器:timename=setTimeout("function();",delaytime);2.循环定时器:timename=setInterval("function();",delaytime); 第一个参数"function()"是定时器触发时要执行的动作,可以是一个函数,也可以是几个函数,函数间用":"隔开即可.比如要弹出两个警告窗口,便可将

  • 深入解析JavaScript中函数的Currying柯里化

    引子 先来看一道小问题: 有人在群里出了到一道题目: var s = sum(1)(2)(3) ....... 最后 alert(s) 出来是6  var s = sum(1)(2)(3)(4) ....... 最后 alert(s) 出来是10  问sum怎么实现? 刚看到题目,我第一反应是sum返回的是一个function,但是没有最终实现,印象中看到过类似的原理,但是记不清了.   后来同事说,这个是叫柯里化, 实现方法比较巧妙: function sum(x){ var y = func

  • JavaScript函数柯里化详解

    什么是柯里化 柯里化是这样的一个转换过程,把接受多个参数的函数变换成接受一个单一参数(译注:最初函数的第一个参数)的函数,如果其他的参数是必要的,返回接受余下的参数且返回结果的新函数. 柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预先存储的值进行相关的操作处理即可: 柯里化函数主要起到预处理的作用: bind方法的作用:把传递进来的callba

随机推荐