1.パラメータの再利用
// 普通函数验证
function check(regExp, text) {
return regExp.test(text);
}
console.log(check(/^\d+$/g, '123'));
// true
console.log(check(/^\d+$/g, '2d'));
// false
console.log(check(/^[a-z]+$/g, 'text'));
// true
console.log(check(/^[a-z]+$/g, '3d'));
// false
// --------------------------------------------------------------------------
// Currying后
function curryingCheck(regExp) {
return function(regExp) {
return reg.test(regExp);
}
}
let hasNumber = curryingCheck(/^\d+$/g);
let hasLetter = curryingCheck(/^[a-z]+$/g);
console.log(hasNumber('159'));
// true
console.log(hasNumber('2d'));
// false
console.log(hasLetter('3d'));
// false
console.log(hasLetter('text'));
// true
この例は通常のチェックです。通常は直接チェック関数を呼び出すだけで済みます。ただし、数値があるかどうかをチェックする必要がある場所が多数ある場合は、実際には最初のパラメータ reg を再利用する必要があります。 hasNumber や hasLetter などの関数を直接呼び出してパラメータを再利用し、呼び出しをより便利にします。
2. 事前確認(Lazy機能)
// 方案一
let on = function(element, event, handler) {
if (document.addEventListener) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
};
} else {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
};
};
};
// 方案二
// ()(); => ~function() {.. ..}();
let on = (function() {
if (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);
};
};
};
})();
// 方案三
// 换一种写法可能比较好理解一点,
// 上面就是把isSupport这个参数给先确定下来了
let on = function(isSupport, element, event, handler) {
isSupport = isSupport || document.addEventListener;
if (isSupport) {
return element.addEventListener(event, handler, false);
} else {
return element.attachEvent('on' + event, handler);
};
};
プロジェクトに取り組むプロセスでは、いくつかの DOM 操作をカプセル化することが非常に一般的です。上記の最初の記述方法も比較的一般的ですが、2 番目の記述方法を見てください。最初の記述方法と比較すると、それは self -実行して新しい関数を返します。これは実際には、毎回判断することを避けるために、どのメソッドが実行されるかを事前に決定することを意味します。
3. 遅延動作
Function.prototype.bind = function (context) {
let that = this,
args = Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(context, args);
};
};
jsでよく使われるバインドはCurryingで実装されています。
4. 予備包装
let currying = function(fn) {
// args获取第一个方法内的全部参数
let args = Array.prototype.slice.call(arguments, 1);
return function() {
// 将后面方法里的全部参数和args进行合并
var newArgs = args.concat(Array.prototype.slice.call(arguments));
// 把合并后的参数通过apply作为fn的参数并执行
return fn.apply(this, newArgs);
};
};
はクロージャを通じて予備パラメータを保存し、次にスプライシングのために残りの
arguments
を取得し、最後にcurrying
を必要とする関数を実行します。ただし、戻り値はもう 1 つのパラメータによってのみ拡張できるという欠陥があります。currying(a)(b);
この場合、複数パラメータの呼び出しはサポートされません。
5. 再帰的カプセル化
// 支持多参数传递
function progressCurrying(fn, args) {
let that = this;
let len = fn.length;
let args = args || [];
return function() {
let _args = Array.prototype.slice.call(arguments);
Array.prototype.push.apply(args, _args);
// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
return progressCurrying.call(that, fn, _args);
}
// 参数收集完毕,则执行fn
return fn.apply(this, _args);
};
}
実際には、予備的に再帰呼び出しが追加されており、パラメータの数が初期の fn.length 未満である限り、再帰は実行され続けます。
6. 典型的な面接の質問
計算結果が次の期待を満たすように add メソッドを実装します。
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
let _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
let _adder = function() {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
};
return _adder;
}
console.log(add(1)(2)(3)); // 6
console.log(add(1, 2, 3)(4)); // 10
console.log(add(1)(2)(3)(4)(5)); // 15
console.log(add(2, 6)(1)); // 9