作者:dendoink
連接:https://juejin.im/post/6844903645222273037
來源:掘金
逐步接收參數的過程:
可以理解為提前接收部分參數,延遲執行,不立即輸出結果,而是返回一個接受剩餘參數的函數。因為這樣的特性,也被稱為部分計算函數。
實現add(1)(2, 3)(4)() = 10
的效果
- 傳入參數,代碼不執行結果,而是先記憶起來
- 當傳入空參數及代表執行
完整代碼:
function currying(fn) {
let allArgs = [];
return function next() {
let args = [].slice.call(arguments);
if (args.length > 0) {
allArgs = allArgs.concat(args);
return next;
} else {
return fn.apply(null, allArgs);
}
}
}
let add = currying(function () {
let sum = 0;
for (let i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
});
function currying(fn) {
// 因為外面的add會指向next函數,且next函數裡面引用到了allArgs
let allArgs = [];
return function next() {
// 將arguments對象轉成數組([]不代表任何意義只是為了調用slice方法)
// 所以也可以這樣 Array.prototype.slice.call(arguments)
let args = [].slice.call(arguments);
// 當傳入參數數量大於零就推進allArgs數組裡面
if (args.length > 0) {
allArgs = allArgs.concat(args);
return next;
} else {
return fn.apply(null, allArgs);
}
}
}
從此例子可以得出明顯的規範
來實現 add(1)(2, 3)(4)('5')
透過隱式調用
valueOf
和toString
方式結束延後執行
function currying(fn) {
let allArgs = [];
function next() {
let args = [].slice.call(arguments);
allArgs = allArgs.concat(args);
return next;
}
// 字符類型(一旦調用到這方法及立刻執行函數)
next.toString = function () {
return fn.apply(null, allArgs);
};
// 數值類型(一旦調用到這方法及立刻執行函數)
next.valueOf = function () {
return fn.apply(null, allArgs);
}
return next;
}
let add = currying(function () {
let sum = 0;
for (let i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
});
console.log(add(1)(2, 3)(4)('5'));
是一個泛型化的過程。它使得被反柯里化的函數,可以接收更多參數。目的是創建一個更普適性的函數,可以被不同的對象使用。有鳩占鵲巢的效果。
我們透過反柯里化成功讓非Toast類的obj成功調用了Toast私人方法
// 創建Toast類
function Toast(option) {
this.prompt = '';
}
Toast.prototype = {
constructor: Toast,
// Toast類的私有方法
show: function () {
console.log(this.prompt);
}
};
// 新對象
let obj = {
prompt: '新對象'
};
// 反柯里化函數
function unCurrying(fn) {
return function () {
// 將arguments類型轉成數組
let args = [].slice.call(arguments);
let that = args.shift();
return fn.apply(that, args);
}
}
var objShow = unCurrying(Toast.prototype.show);
objShow(obj); // 輸出 '新對象'
Function.prototype.unCurrying = function(){
var self = this;
return function(){
return Function.prototype.call.apply(self, arguments);
}
}
// 使用
var objShow = Toast.prototype.show.unCurrying();
objShow(obj);
解析 :
// 先來看看這個
function foo() {
console.log(this.name);
}
let obj = {
name: 'Mike'
}
Function.prototype.call.apply(foo, [obj]) // 'Mike'
// 1. 因為call也是函數所以當然有apply方法
// 2. 先看後面apply(foo.[obj]),可以把他想像成callFunction.apply(foo,[obj])
// 3. 我們將foo綁定給callFunction的this(call會執行他),並把[obj]當成參數綁定給他
// 4. 所以其實會跟這個類似
foo.call(obj) // 'Mike'
// 5. 至於我們為何要做這麼複雜的事情呢?
// 因為這裡我們只能透過this(即self)獲得函數,因此必須透因此必須透過apply或者是call執行函數
目的:
透過Object.prototype.toString 判斷類型
基本寫法
var fn = function () { };
var val = 1;
if (Object.prototype.toString.call(fn) == '[object Function]') {
console.log(`${fn} is function.`);
}
if (Object.prototype.toString.call(val) == '[object Number]') {
console.log(`${val} is number.`);
}
反柯里化寫法:
Function.prototype.unCurrying = function () {
var self = this;
return function () {
return Function.prototype.call.apply(self, arguments);
}
}
var fn = function () { };
var val = 1;
var toString = Object.prototype.toString.unCurrying();
// 不需要再透過綁定
if (toString(fn) == '[object Function]') {
console.log(`${fn} is function.`);
}
if (toString(val) == '[object Number]') {
console.log(`${val} is number.`);
}
function nodeListen(node, eventName) {
return function (fn) {
node.addEventListener(eventName, function () {
fn.apply(this, Array.prototype.slice.call(arguments));
}, false);
}
}
var bodyClickListen = nodeListen(document.body, 'click');
bodyClickListen(function () {
console.log('first listen');
});
bodyClickListen(function () {
console.log('second listen');
});