Repository navigation
JavaScript专题之惰性函数 #44
Description
Activity
function addEvent (type, el, fn) { if (window.addEventListener) { addEvent = function (type, el, fn) { el.addEventListener(type, fn, false); } } else if(window.attachEvent){ addEvent = function (type, el, fn) { el.attachEvent('on' + type, fn); } } }
如果我没理解错,这段函数,在第一次执行的时候,addEvent并不会绑定事件,只是对addEvent重新赋值了一次,这样修改如何?
function addEvent (type, el, fn) { if (window.addEventListener) { el.addEventListener(type, fn, false); addEvent = function (type, el, fn) { el.addEventListener(type, fn, false); } } else if(window.attachEvent){ el.attachEvent('on' + type, fn); addEvent = function (type, el, fn) { el.attachEvent('on' + type, fn); } } }
或者立即执行它
Reacted by qujsh, xue1234jun, faq, hua03, btea, Orange, tobee, levi, huas_wg, naixy28 and 28 more@fi3ework 感谢补充哈~ 确实是这样的,第一次并会不绑定事件,所以其实还需要先执行一次:
function addEvent (type, el, fn) { if (window.addEventListener) { addEvent = function (type, el, fn) { el.addEventListener(type, fn, false); } } else if(window.attachEvent){ addEvent = function (type, el, fn) { el.attachEvent('on' + type, fn); } } } addEvent();
然后再使用 addEvent 绑定事件
不过你补充的这种方法非常好,就不用再执行一次了~ o( ̄▽ ̄)d
Reacted by trylang, yongzhenzhao, FENGHUA513, Jason Gao, xiaopihaishini, 李云花, kk and JiangHaofunction addEvent (type, el, fn) { if (window.addEventListener) { addEvent = function (type, el, fn) { el.addEventListener(type, fn, false); } } else if(window.attachEvent){ addEvent = function (type, el, fn) { el.attachEvent('on' + type, fn); } } addEvent(type, el, fn) }
其实最后直接执行一次就好了
Reacted by Max, xiterjia, Lian, EmilyYang, fangdasimon, YanMuXin, huas_wg, yongzhenzhao, lishihong, Sara Zhang and 26 morefunction addEvent (type, el, fn) { if (window.addEventListener) { addEvent = function (type, el, fn) { el.addEventListener(type, fn, false); } } else if(window.attachEvent){ addEvent = function (type, el, fn) { el.attachEvent('on' + type, fn); } } addEvent(type, el, fn) }
其实最后直接执行一次就好了
怎么这么多人赞同的啊,这样写不是死循环了吗。。。
Reacted by 李云花 and 安静Reacted by SiddhaDo, Aji, sicraf, zhangboyce, z, HChange, WAGFS and Neo@lishihong addEvent经过条件判定后已经被重写了
Reacted by hongzzz, lishihong, SiddhaDo, ww24kobe, sicraf, longer, Journey and zhangboyce@lishihong addEvent经过条件判定后已经被重写了
哦 是啊 明白了
感觉用这个例子来引入惰性函数不是很适合
我们现在需要写一个 foo 函数,这个函数返回首次调用时的 Date 对象,注意是首次。
这个需求应该是用 once 函数实现比较好,不过 addEvent 的例子是适合惰性函数的。
function once(fn) { var fire, ret return function() { var self = this if (!fire) { fire = true ret = fn.apply(self, arguments) } return ret } }
function addEvent (type, el, fn) { if (window.addEventListener) { addEvent = function (type, el, fn) { el.addEventListener(type, fn, false); } } else if(window.attachEvent){ addEvent = function (type, el, fn) { el.attachEvent('on' + type, fn); } } addEvent(type, el, fn) }
其实最后直接执行一次就好了
怎么这么多人赞同的啊,这样写不是死循环了吗。。。
如果存在这么一款浏览器
window.addEventListener和window.attachEvent都不存在时,就是死循环了吧Reacted by 李云花, twinkle77, SiddhaDo, guozhangqiang, yiongq, zhangboyce, Shawn Leo, Adophlidu and WAGFSReacted by blast_onionReacted by jlongguovar foo = function() { //假设这个是匿名函数A var t = new Date(); foo = function() { //假设这个是匿名函数B return t; }; return foo(); }; foo(); //一个时间 foo(); //同样的事件如上代码,前后两次执行 foo() 返回同样的时间,我认为应该是利用了闭包的特性。
如果按照您在JavaScript深入之执行上下文中的讲解,那此处的执行过程应该怎么描述呢(注:以下描述只描述到了执行第一个 foo() )。
1.进入全局代码,创建全局执行上下文,全局执行上下文压入执行上下文栈
2.全局执行上下文初始化
接下来就有点不清楚了,是?
3.执行 foo 指向的匿名函数 A ,创建 匿名函数 A 执行上下文,匿名函数 A 执行上下文被压入执行上下文栈
4.匿名函数 A 执行上下文初始化,创建变量对象、作用域链、this等anonymousAContext = { AO: { arguments: { length: 0 }, t: undefined, }, Scope: [AO, globalContext.VO], this: undefined }5.执行 foo 指向的匿名函数 B ,创建 匿名函数 B 执行上下文,匿名函数 B 上下文被压入执行上下文栈
6.匿名函数 B 执行上下文初始化,创建变量对象、作用域链、this等anonymousBContext = { AO: { arguments: { length: 0 }, }, Scope: [AO, anonymousAContext.AO, globalContext.VO], this: undefined }7.匿名函数 B 执行,沿着作用域链查找 t 值,返回 t 值
8.匿名函数 B 函数执行完毕,匿名函数 B 函数上下文从执行上下文栈中弹出
9.匿名函数 A 函数执行完毕,匿名函数 A 执行上下文从执行上下文栈中弹出而之所以形成闭包,是因为步骤 4 至步骤 5 中,对 foo 进行了重新赋值,从而让 foo 所指向函数的 [[Scope]] 值为 [ anonymousAContext.AO, globalContext.VO] 。
不知道这样的解释是否正确,对于 foo 是指向匿名函数的变量,之前的教程貌似没有介绍。其实就是foo的指向发生了变化
最开始初始化的时候
foo指向
function () {
var t = new Date()
foo = function () {
console.log(the same time: ${t});
}
return foo();
}
运行完成以后
foo指向
foo = function () {
console.log(the same time: ${t});
}还有就是一些闭包在起作用
妙啊
妙,之前就这样写过,不过就与 const 无缘了。
妙,之前就这样写过,不过就与 const 无缘了。
改成自执行函数就可以把。
const addEvent = (function(type, el, fn) { if(window.addEventListener) { return function(type, el, fn) { el.addEventListener(type, fn, false); } } if(window.attachEvent) { return function(type, el, fn) { el.attachEvent('on' + type, fn); } } })();
Reacted by sicrafReacted by ferrinweb感觉用这个例子来引入惰性函数不是很适合
我们现在需要写一个 foo 函数,这个函数返回首次调用时的 Date 对象,注意是首次。
这个需求应该是用 once 函数实现比较好,不过 addEvent 的例子是适合惰性函数的。
function once(fn) { var fire, ret return function() { var self = this if (!fire) { fire = true ret = fn.apply(self, arguments) } return ret } }
个人觉得惰性函数是 通过改写函数来 避免多次做不必要的判断, once 函数 还是需要每次执行都进行判断
学习了,这两篇都简单了好多,之前看柯里化看得头大
element-ui 处理 dom 事件的源码就是这样写的。
export const on = (function() { if (!isServer && document.addEventListener) { return function(element, event, handler) { if (element && event && handler) { element.addEventListener(event, handler, false); } }; } else { return function(element, event, handler) { if (element && event && handler) { element.attachEvent('on' + event, handler); } }; } })();
@fi3ework @mqyqingfeng
我是这样改了一下:function addEvent (type, el, fn) { if (window.addEventListener) { // 在第一次调用时 根据特征进行重新定义 addEvent = function(type, el, fn) { el.addEventListener(type, fn, false); } } if (window.attachEvent) { addEvent = function(type, el, fn) { el.attachEvent('on' + type, fn); } } // me: 我觉得应该有调用这一句,此时调用的已经是重新定义过的addEvent 不会无限递归 addEvent(type, el, fn); }
妙,之前就这样写过,不过就与 const 无缘了。
改成自执行函数就可以把。
const addEvent = (function(type, el, fn) { if(window.addEventListener) { return function(type, el, fn) { el.addEventListener(type, fn, false); } } if(window.attachEvent) { return function(type, el, fn) { el.attachEvent('on' + type, fn); } } })();
这样就不是惰性函数了。
妙妙妙
需求
我们现在需要写一个 foo 函数,这个函数返回首次调用时的 Date 对象,注意是首次。
解决一:普通方法
问题有两个,一是污染了全局变量,二是每次调用 foo 的时候都需要进行一次判断。
解决二:闭包
我们很容易想到用闭包避免污染全局变量。
然而还是没有解决调用时都必须进行一次判断的问题。
解决三:函数对象
函数也是一种对象,利用这个特性,我们也可以解决这个问题。
依旧没有解决调用时都必须进行一次判断的问题。
解决四:惰性函数
不错,惰性函数就是解决每次都要进行判断的这个问题,解决原理很简单,重写函数。
更多应用
DOM 事件添加中,为了兼容现代浏览器和 IE 浏览器,我们需要对浏览器环境进行一次判断:
问题在于我们每当使用一次 addEvent 时都会进行一次判断。
利用惰性函数,我们可以这样做:
当然我们也可以使用闭包的形式:
当我们每次都需要进行条件判断,其实只需要判断一次,接下来的使用方式都不会发生改变的时候,想想是否可以考虑使用惰性函数。
重要参考
Lazy Function Definition Pattern
专题系列
JavaScript专题系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript专题系列预计写二十篇左右,主要研究日常开发中一些功能点的实现,比如防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里、递归、乱序、排序等,特点是研(chao)究(xi) underscore 和 jQuery 的实现方式。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。