JavaScript中的高级函数


Posted in Javascript onJanuary 04, 2018

在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;
 }
}

结语

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

Javascript 相关文章推荐
在vs2010中调试javascript代码方法
Feb 11 Javascript
JSONP 跨域共享信息
Aug 16 Javascript
基于jQuery 实现bootstrapValidator下的全局验证
Dec 07 Javascript
Javascript 实现全屏滚动实例代码
Dec 31 Javascript
js实现日历的简单算法
Jan 24 Javascript
Bootstrap下拉菜单样式
Feb 07 Javascript
jquery.flot.js简单绘制折线图用法示例
Mar 13 Javascript
关于在vue-cli中使用微信自动登录和分享的实例
Jun 22 Javascript
使用ECharts实现状态区间图
Oct 25 Javascript
详解vue 图片上传功能
Apr 30 Javascript
详细聊聊vue中组件的props属性
Nov 02 Vue.js
create-react-app开发常用配置教程
Jun 25 Javascript
Three.js 再探 - 写一个微信跳一跳极简版游戏
Jan 04 #Javascript
JS实现带导航城市列表以及输入搜索功能
Jan 04 #Javascript
微信小程序实现的贪吃蛇游戏【附源码下载】
Jan 03 #Javascript
详解Angular2学习笔记之Html属性绑定
Jan 03 #Javascript
Angular2学习笔记之数据绑定的示例代码
Jan 03 #Javascript
vue、react等单页面项目应该这样子部署到服务器
Jan 03 #Javascript
AngularJS实现的2048小游戏功能【附源码下载】
Jan 03 #Javascript
You might like
在PHP中使用XML
2006/10/09 PHP
php结合正则批量抓取网页中邮箱地址
2015/05/19 PHP
讲解WordPress中用于获取评论模板和搜索表单的PHP函数
2015/12/28 PHP
微信支付的开发流程详解
2016/09/13 PHP
关于PHP中协程和阻塞的一些理解与思考
2017/08/11 PHP
tp5框架无刷新分页实现方法分析
2019/09/26 PHP
discuz论坛更换域名,详细文件修改步骤
2020/12/09 PHP
Javascript 更新 JavaScript 数组的 uniq 方法
2008/01/23 Javascript
jQuery Animation实现CSS3动画示例介绍
2013/08/14 Javascript
js实现对table动态添加、删除和更新的方法
2015/02/10 Javascript
jQuery给多个不同元素添加class样式的方法
2015/03/26 Javascript
JavaScript获取URL汇总
2015/06/08 Javascript
学JavaScript七大注意事项【必看】
2016/05/04 Javascript
Query常用DIV操作获取和设置长度宽度的实现方法
2016/09/19 Javascript
浅谈DOM的操作以及性能优化问题-重绘重排
2017/01/08 Javascript
JavaScript 事件流、事件处理程序及事件对象总结
2017/04/01 Javascript
js学习总结_基于数据类型检测的四种方式(必看)
2017/07/04 Javascript
在angular 6中使用 less 的实例代码
2018/05/13 Javascript
Vue2.0仿饿了么webapp单页面应用详细步骤
2018/07/08 Javascript
javascript实现简易的计算器
2020/01/17 Javascript
Python+MongoDB自增键值的简单实现
2016/11/04 Python
Python+Selenium自动化实现分页(pagination)处理
2017/03/31 Python
利用Python暴力破解zip文件口令的方法详解
2017/12/21 Python
对numpy下的轴交换transpose和swapaxes的示例解读
2019/06/26 Python
python修改文件内容的3种方法详解
2019/11/15 Python
pytorch中torch.max和Tensor.view函数用法详解
2020/01/03 Python
django 解决model中类写不到数据库中,数据库无此字段的问题
2020/05/20 Python
利用python查看数组中的所有元素是否相同
2021/01/08 Python
奥地利网上现代灯具和灯饰店:Lampenwelt.at
2018/01/29 全球购物
澳大利亚二手奢侈品网站:Modsie
2019/09/23 全球购物
监理资料员岗位职责
2014/01/03 职场文书
领导干部“四风”查摆问题个人整改措施
2014/10/28 职场文书
2015年银行大堂经理工作总结
2015/04/24 职场文书
三年级作文之小小梦想
2019/12/06 职场文书
nginx简单配置多个server的方法
2021/03/31 Servers
JUnit5常用注解的使用
2021/07/02 Java/Android