这是一个由于对 JavaScript 函数上下文理解不够深入而遇到的坑。
背景
在表单验证中,利用高阶函数,抽象一个可以返回特定验证逻辑的验证函数:
// 正则匹配
const getParser = (type) => {
switch (type) {
case 'chname':
return /^([\u4e00-\u9fa5]+|([a-zA-Z]+\s?)+)$/g;
case 'email':
return /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
case 'phone':
return /^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/;
case 'password':
return /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])[a-zA-Z0-9!@#$%^&*()]{8,16}$/;
default:
return () => false;
}
};
const getMatch = (value) => `${value}`.match;
const isValid = (value, type) => getMatch(value)(getParser(type));
isValid('12345678901', 'phone');
问题
执行 getMatch(value)(getParser(type))
的时候,无法完成验证,程序陷入停止,无法往下执行。
原因
在执行形如 var a = obj.func
的代码的时候,我们以为得到的变量 a
的上下文是 obj
,然后执行 a()
的时候,就会跟执行 obj.func()
一样;然而实际上,得到的 a
只是一个单纯的函数,并不会自动绑定 obj
为上下文。
再来看上面的代码的最后一句:
const isValid = (value, type) => getMatch(value)(getParser(type));
通过 getMatch(value)
得到一个 match
方法,但是与预期不同,此时得到的 match
方法的上下文并不是 value
,因而会执行失败。
解决办法
知道了原因,很明显解决办法就是为要使用的 match
方法绑定上下文。在 Javascript 中,有很多种方法可以实现:
/// bind
const getMatch = (value) => `${value}`.match.bind(value);
const isValid = (value, type) => getMatch(value)(getParser(type));
/// call
const getMatch = (value) => `${value}`.match;
const isValid = (value, type) => getMatch(value).call(value, getParser(type));
/// apply
const getMatch = (value) => `${value}`.match;
const isValid = (value, type) => getMatch(value).apply(value, [getParser(type)]);
结论
JavaScript 的面向对象特性,比想象中还要弱一些。根本没有什么 “类方法” ,所有形如 obj.func1()
的点操作方法执行,只不过是将 obj
作为上下文去执行 func1
函数罢了。需要特别注意的是,只有在点操作之后马上执行,才会有上下文,给人造成是 “调用了对象 obj
里面的 func1
方法” 的假象;如果如上文描述那样进行了赋值操作 func2 = obj.func1
,则在执行 func2
的时候就不会有什么上下文了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。