谈谈JS中的高级函数
博客原文地址:Claiyre的个人博客
如需转载,请在文章开头注明原文地址
在JavaScript中,函数的功能十分强大。它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函数,不仅如此,还能被一个函数返回!可以说,在JS中,函数无处不在,无所不能,堪比孙猴子呀!当你运用好函数时,它能助你取西经,让代码变得优雅简洁,运用不好时,那就遭殃了,要大闹天宫咯~
除了函数相关的基础知识外,掌握一些高级函数并应用起来,不仅能让JS代码看起来更为精简,还可以提升性能。以下是博主总结的一些常用的、重要的高级函数,加上了一些个人见解,特此记录下来。如果您是JS初学者,也不要被“高级”两个字吓到,因为文中穿插讲解了一些原型、this等基础知识,相信并不难理解。如果您是JS大牛,也可以把本文用来查漏补缺。
正文
作用域安全的构造函数
1
2
3
4
5
|
function Person(name,age){
this.name = name;
this.age = age;
}
var p1 = new Person("Claiyre",80);
|
相信您对上面的构造函数一定不陌生,但是,,如果某个粗心的程序猿调用这个构造函数时忘记加new
了会发生什么?
1
2
3
|
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
对象是正确类型的实例,然后再进行更改,如下:
1
2
3
4
5
6
7
8
|
function Person(name,age){
if(this instanceof Person){
this.name = name;
this.age = age;
} else {
return new Person(name,age);
}
}
|
这样就避免了在全局对象上意外更改或设置属性。
实现这个安全模式,相当于锁定了调用构造函数的环境,因此借用构造函数继承模式可能会出现问题,解决方法是组合使用原型链和构造函数模式,即组合继承。
如果您是一个JS库或框架的开发者,相信作用域安全的构造函数一定对您非常有用。在多人协作的项目中,为了避免他们误改了全局对象,也应使用作用域安全的构造函数。
惰性载入函数
由于浏览器间的行为差异,代码中可能会有许多检测浏览器行为的if语句。但用户的浏览器若支持某一特性,便会一直支持,所以这些if语句,只用被执行一次,即便只有一个if语句的代码,也比没有要快。
惰性载入表示函数执行的分支仅会执行一次,有两种实现惰性载入的方式,第一种就是在函数第一次被调用时再处理函数,用检测到的结果重写原函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function detection(){
if(//支持某特性){
detection = function(){
//直接用支持的特性
}
} else if(//支持第二种特性){
detection = function(){
//用第二种特性
}
} else {
detection = function(){
//用其他解决方案
}
}
}
|
第二种实现惰性载入的方式是在声明函数时就指定适当的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var detection = (function(){
if(//支持某特性){
return function(){
//直接用支持的特性
}
} else if(//支持第二种特性){
return function(){
//用第二种特性
}
} else {
return function(){
//用其他解决方案
}
}
})();
|
惰性载入函数的有点是在只初次执行时牺牲一点性能,之后便不会再有多余的消耗性能。
函数绑定作用域
在JS中,函数的作用域是在函数被调用时动态绑定的,也就是说函数的this对象的指向是不定的,但在一些情况下,我们需要让某一函数的执行作用域固定,总是指向某一对象。这时怎么办呢?
当当当~~可以用函数绑定作用域函数呀
1
2
3
4
5
|
function bind(fn,context){
return function(){
return fn.apply(context,arguments);
}
}
|
用法:
1
2
3
4
5
6
7
8
|
var person1 = {
name: "claiyre",
sayName: function(){
alert(this.name);
}
}
var sayPerson1Name = bind(person1.sayName,person1);
sayPerson1Name(); //claiyre
|
call
函数和apply
函数可以临时改变函数的作用域,使用bind函数可以得到一个绑定了作用域的函数
函数柯里化(curry)
curry的概念很简单:只传递部分参数来调用函数,然后让函数返回另一个函数去处理剩下的参数。可以理解为赋予了函数“加载”的能力。
许多js库中都封装了curry函数,具体使用可以这样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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的方式:
1
2
3
4
5
6
7
8
9
10
11
12
|
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函数,又称“去抖函数”。它的功能也很简单直接,就是防止某一函数被连续调用,从而导致浏览器卡死或崩溃。用法如下:
1
2
3
4
|
var myFunc = debounce(function(){
//繁重、耗性能的操作
},250);
window.addEventListener('resize',myFunc);
|
像窗口的resize,这类可以以较高的速率触发的事件,非常适合用去抖函数,这时也可称作“函数节流”,避免给浏览器带来过大的性能负担。
具体的实现时,当函数被调用时,不立即执行相应的语句,而是等待固定的时间w,若在w时间内,即等待还未结束时,函数又被调用了一次,则再等待w时间,重复上述过程,直到最后一次被调用后的w时间内该函数都没有被再调用,则执行相应的代码。
实现代码如下:
1
2
3
4
5
6
7
|
function debounce(fn,wait){
var td;
return function(){
clearTimeout(td);
td= setTimeout(fn,wait);
}
}
|
once函数
顾名思义,once函数是仅仅会被执行一次的函数。具体实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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。
1
2
3
4
5
6
7
8
9
10
|
function once(fn,context){
var result;
return function(){
if(fn){
result = fn.apply(context,arguments);
fn = null;
}
return result;
}
}
|
结语
通过以上的阅读,不难发现很多“高级函数”的实现其实并不复杂,数十行代码便可搞定,但重要的是能真正理解它们的原理,在实际中适时地应用,以此性能提升,让代码简洁,逻辑清晰
谈谈JS中的高级函数的更多相关文章
- 浅谈JS中的高级函数
在JavaScript中,函数的功能十分强大.它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函数,不仅如此,还能被一个函数返回!可以说,在JS中,函数无处不在,无所不能,堪比孙 ...
- js中的回调函数的理解和使用方法
js中的回调函数的理解和使用方法 一. 回调函数的作用 js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个操作,这时候就需要用到回调函数. 二. 回调函数的解释 因为 ...
- underscore.js中的节流函数debounce及trottle
函数节流 throttle and debounce的相关总结及想法 一开始函数节流的使用场景是:放止一个按钮多次点击多次触发一个功能函数,所以做了一个clearTimeout setTimeou ...
- js中如何在一个函数里面执行另一个函数
1.js中如何在函数a里面执行函数b function a(参数c){ b(); } function b(参数c){ } 方法2: <script type="text/javasc ...
- JavaScript -- 时光流逝(七):js中的全局函数
JavaScript -- 知识点回顾篇(七):js中的全局函数 全局函数可用于所有内建的 JavaScript 对象. (1) encodeURI():把字符串编码为 URI. <script ...
- js中的匿名函数和匿名自执行函数
1.匿名函数的常见场景 js中的匿名函数是一种很常见的函数类型,比较常见的场景: <input type="button" value="点击" id ...
- JS中关于把函数作为另一函数的参数的几点小总结
//JS中关于把函数作为函数的参数来传递的问题的小总结//第一,最简单的形式无参函数,直接形式函数的函数名放到括号中,再在执行部分这个函数即可.//当然调用时要穿另一个真正的定义好的函数/*funct ...
- js中的Generators函数
js中的Generators函数 generator一般操作 generator函数的作用就是函数体分段执行,yield表示分隔点 function *test() { console.log(1); ...
- JS中的回调函数实例浅析
本文实例讲述了JS中的回调函数.分享给大家供大家参考,具体如下: 在说回调函数之前,不妨先看一段代码,相信有点js基础的同学都能明白他的含义: ? 1 2 3 document.getElementB ...
随机推荐
- HDU-5695-拓扑排序+优先队列
Gym Class Time Limit: 6000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total S ...
- HDU 4696 Answers (脑补+数形结合)
题意 给一个图,每个点的出度为1,每个点的权值为1或者2.给Q个询问,问是否能找到一条路径的权值和M. 思路 由于每个点的出度为1,所以必然存在环.又因为c[i]只能取1或者2,可以组成任意值,所以只 ...
- IOS UI-控制器的创建和控制器的View的创建
一.控制器的创建和控制器的View的创建 说明:控制器有三种创建方式,下面一一进行说明. 一.第一种创建方式(使用代码直接创建) 1.创建一个空的IOS项目. 2.为项目添加一个控制器类. 3.直接在 ...
- 几款必备LINUX的命令行神器
Dstat & sar iostat, vmstat, ifstat 三合一的工具,用来查看系统性能(我在<性能调优攻略>中提到过那三个xxstat工具). 官方网站:http:/ ...
- JavaScript中的call、apply、bind是怎么回事?
在JS中,这三者都是用来改变函数的this对象的指向的,他们有什么样的区别呢.在说区别之前还是先总结一下三者的相似之处:1.都是用来改变函数的this对象的指向的.2.第一个参数都是this要指向的对 ...
- Bootstrap 可视化布局--拖拽后弹窗进行编辑
Bootstrap 可视化布局--拖拽后弹窗进行编辑 最近后台想一个需求,使用可视化布局-中文 | en中拖拽表格后,弹窗进行编辑,保存下载后在后台生成pdf格式. 奈何各种问题不断,使用 jquer ...
- p标签多行文字内容实现上下垂直居中兼容ie8
之前实现上下居中一般都是用height和line-height的来设置. 今天在修改样式的时候,p标签的文字内容可能是一行也可能是两行, 所以用height和line-height就没效果. 今天找到 ...
- node.js利用express连接mysql数据库
我们创建一个mysql.js (好像大神们,称呼这叫一个模块,然后暴露一个接口)用来连接数据库 var connction ={}; connction.mysql = { host:"lo ...
- PHP获取日期对应星期、一周日期、星期开始与结束日期的方法
本文实例讲述了PHP获取日期对应星期.一周日期.星期开始与结束日期的方法.分享给大家供大家参考,具体如下: /* * 获取日期对应的星期 * 参数$date为输入的日期数据,格式如:2018-6-22 ...
- C++面向对象高级编程(六)转换函数与non-explicit one argument ctor
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 1.conversion function 转换函数 //1.转换函数 //conversion function //只要你认为合理 你可以任 ...