改写函数实际上违背了FP的无副作用的精神

本文深入探讨了LazyFunction定义模式的潜在问题,包括其对内存泄漏的影响及对函数变量重新赋值所带来的副作用,通过具体示例说明了这些副作用可能导致的性能下降和其他潜在风险。
在[url=http://hax.iteye.com/blog/113565]上一篇帖子[/url]中,我讨论了Peter提出的[url=http://peter.michaux.ca/article/3556]Lazy Function Definition Pattern[/url],我指出了这个pattern并不能带来性能的提升,而所使用的closure也有可能造成内存泄漏。

当然内存泄露在任何closure中都可能存在。因此仅仅从不必要的使用closure加大了内存泄漏的风险这一点来说,说服力可能并不强。而且,在之后的回复中,FCKEditor的FredCK给出了一个[url=http://peter.michaux.ca/article/3556#comment-3661]不使用closure的改进方案[/url]。

但是我仍然觉得哪里不妥。

经过一些思考,我终于意识到关键的坏味道,其实就在于对函数变量的重新赋值。

我们知道纯FP是不带有副作用的,因此不可能允许函数名称与实际代码的重新绑定。所以正如我之前的回复所指出的,这个Lazy Definition模式,其实一点也不functional。

那么让我们看看,副作用有什么危害吧!

首先是Peter的样例代码:
[code]
var getScrollY = function() {

if (typeof window.pageYOffset == 'number') {

getScrollY = function() {
return window.pageYOffset;
};

} else if ((typeof document.compatMode == 'string') &&
(document.compatMode.indexOf('CSS') >= 0) &&
(document.documentElement) &&
(typeof document.documentElement.scrollTop == 'number')) {

getScrollY = function() {
return document.documentElement.scrollTop;
};

} else if ((document.body) &&
(typeof document.body.scrollTop == 'number')) {

getScrollY = function() {
return document.body.scrollTop;
}

} else {

getScrollY = function() {
return NaN;
};

}

return getScrollY();
}
[/code]


假设一位倒霉的开发者John以前有自己写过一个类似getScrollY的函数,但是名字叫做getPageYOffset,用在他自己的页面代码中。现在他知道了Peter的getScrollY,他听说Peter这个版本兼容性更好、性能更高,于是决定采用Peter的版本代替自己的版本。同时他又不想对原有代码做过多改动。于是很自然的,他打算在html head部分引入Peter的脚本,然后后面加上一句:
getPageYOffset = getScrollY;

John特地写了一个testcase,测试这样做的效果。当然,testcase肯定能通过。

但是,大家都知道这样做的结果了,getPageYOffset指向的是原初的getScrollY,因此每次调用getPageYOffset都等于是第一次调用getScrollY。这样非但不会有假想中的节约一次条件判断,反而会而反复的对getScrollY变量重新赋值。性能肯定大大下降。

这还不算,还可能更糟。

考虑FredCK的版本:
[code]
var getScrollY = function() {

if (typeof window.pageYOffset == 'number')
return (getScrollY = getScrollY.case1)();

var compatMode = document.compatMode;
var documentElement = document.documentElement;

if ((typeof compatMode == 'string') &&
(compatMode.indexOf('CSS') >= 0) &&
(documentElement) &&
(typeof documentElement.scrollTop == 'number'))
return (getScrollY = getScrollY.case2)();

var body = document.body ;
if ((body) &&
(typeof body.scrollTop == 'number'))
return (getScrollY = getScrollY.case3)();

return (getScrollY = getScrollY.case4)();
};

getScrollY.case1 = function() {
return window.pageYOffset;
};

getScrollY.case2 = function() {
return documentElement.scrollTop;
};

getScrollY.case3 = function() {
return body.scrollTop;
};

getScrollY.case4 = function() {
return NaN;
};
[/code]

对于这个版本,John一样做了testcase,调用了一次getPageYOffset,没问题,又通过了。

然而当John实际使用的时候,会发生什么?大家可以推导一下。记住,真实应用中,getPageYOffset可能被调用很多次。

所以,这就是我为什么不喜欢这个Pattern,因为开发者有了这样的负担:必须了解这个函数会否悄悄的变化,理解这种副作用可能带来的问题。
改写函数是指在定义函数时,使用相同的函数名但是修改了函数的功能或者参数。这样可以根据需要来定制函数的行为。下面是一些关于改写函数的例子和用法: 1. 改变函数的默认参数值:默认参数值只在函数定义阶段被赋值一次,在函数定义之后的改变对默认参数没有影响。例如: ```python m = 333 def func(x, y=m): # y的默认值为333 print(x, y) m = 44444 func(1) # 输出为1 333 ``` 2. 重写数据转换构造函数:可以在类中重写`__repr__()`和`__int__()`函数来改变数据类型的转换行为。例如: ```python class MyNumber: def __init__(self, value): self.data = value def __repr__(self): return 'MyNumber(%d)' % self.data def __int__(self): return self.data n1 = MyNumber(100) x = int(n1) print(n1) # 输出为MyNumber(100) print(bool(n1)) # 输出为True n2 = MyNumber(0) print(bool(n2)) # 输出为True ``` 以上是关于python改写函数的一些示例和用法。改写函数可以通过修改函数的参数或者功能来满足不同的需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [012.Python之文件修改与函数的基本使用](https://blog.youkuaiyun.com/m0_49141258/article/details/107423463)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Python函数的重写](https://blog.youkuaiyun.com/py_1995/article/details/84139141)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值