[CodeWars][JS]實現鏈式加法

迷路的約翰發表於2015-07-02

在知乎上看到這樣一個問題:http://www.zhihu.com/question/31805304;

簡單地說就是實現這樣一個add函式:

add(x1)(x2)(x3)...(xn) == x1 + x2 + x3 + ... + xn // true;

正好發現在codewars上也有這道題,那不妨一塊刷了吧。

 

Kata Description

level:5 kyu

We want to create a function that will add numbers together when called in succession.

add(1)(2);
// returns 3

 We also want to be able to continue to add numbers to our chain.

add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10
add(1)(2)(3)(4)(5); // 15

 and so on...

Test cases:

Test.expect(add(1) == 1);
Test.expect(add(1)(2) == 3);
Test.expect(add(1)(2)(3) == 6);

 

Solution

首先,第一個示例似乎表示add(1)(2)應該返回數字3,但如此一來add(1)(2)(3)豈不是將函式呼叫應用在了一個數字上面?

我們再來看這道題提供的Test Cases, 注意cases裡使用的是'=='而不是'===',也就是說返回值不需要是一個數字,只需要是一個能進行型別轉換以變成數字的‘值’就可以了。而這道題中,因為add可以連續呼叫,所以這個‘值’應該是一個函式。

 那麼我們要解決的第一個問題: 怎樣能使得函式轉化為數字?

 

問題一 怎樣將函式與數字聯絡起來

1.valueOf() 方法。

顧名思義,這個方法返回的是呼叫這個物件的‘值’。

預設情況下一個函式呼叫valueOf()方法,返回值是函式本身

var fn = function(num){return ++num;};
fn(1); // 2
(fn.valueOf())(1); // 2

然而這並沒有什麼卵用。

 

在這個題目裡,我們需要重寫這個方法:

var fn = function () {}; 
fn.valueOf = function(){ return 233;}; 
console.log(fn==233); // true 
console.log(fn===233); //false 

 以上我們可以發現,當 == 對函式物件進行型別轉化的時候,會讀取函式的valueOf的返回值,這樣就達到了我們的目的。

 

實際上Function本身並沒有valueOf方法,該方法繼承自Object.prototype.valueOf();

也就是說,不僅僅是函式,當我們需要對任何物件與數字進行比較的時候,都能夠使用這個方法。

var o = {};
o.valueOf = function(){return 233;};
console.log(o == 233) // true
console.log(o === 233) // false

另外,與Function不同,Number,String這些物件,都有著自己的valueOf方法,而非繼承於Object。

所以我們可以猜測,==符進行型別轉化的時候,可能都是先讀取物件的valueOf的返回值進行比較。

 

2. toString()方法。

除了用上面的valueOf方法以外,toString也可以達到同樣的目的。

var fn = new Function;
fn.toString = function() { return 233;};
console.log(fn==233); // true
console.log(fn===233); //false

 

和valueOf不同的是,Function中的toString並非繼承自Object,它使用的是自己的toString方法。

對於函式而言,toString的返回值就像是呼叫函式的‘名字’。修改了toString, 就好像給函式換了個‘稱號’:

var fn = function(){};
console.log(fn); // function()

var fn2 = function(){};
fn2.toString = function(){return 233;};
console.log(fn2) // 233

 

預設呼叫toString返回的值是該函式的字串形式,就像對這段程式碼進行了反編譯。

var fn = function(){
  //Can you see me?
   //Oh..
  return 233;
};
var fnStr = fn.toString();
console.log(fnStr);

輸出

'function (){
  //Can you see me?
   //Oh..
  return 233;
}'

 嗯沒錯連函式裡的註釋,換行,空格什麼的都包括進來了。

另外MDN的文件說toString()還可以接受一個縮排的引數,不過我試了試好像沒有什麼用。

 

3. toString() 與 valueOf()

既然上面兩種方法都可以,那麼在這個題目裡,如果同時使用了這兩種方法,那種的優先順序更高呢?

這個簡單,我們來試一試:

var fn = function(){};
fn.toString = function(){return 'toString wins';};
fn.valueOf = function() {return 'valueOf wins';}
fn == 'toString wins'; // false
fn == 'valueOf wins'; // true

看來是valueOf()等級更高啊。

也就是說,使用==符對物件與普通資料進行比較的時候,先讀取物件的‘value’,如果value不是普通資料型別,再讀取‘string’,如果還不是,那就輸出false。當然,在讀取過程中,如果讀取到了普通資料型別並且判斷為不等,那就直接輸出fasle,不會進行下一步的比較。

 

問題二 然後呢

上面的問題解決後這個題也就解決得差不多了。

首先我們的add函式應該返回一個函式,並且我們需要對這個函式的valueOf(或者toString)進行重寫:

var add=function(num){
 var inner = function(){}; inner.valueOf
=function(){}; return inner; };

 

然後用一個temp變數來儲存累加的結果吧,temp同時也應該是內部函式的valueOf的返回值:

var add=function(num){
  var temp = num;
  var inner = function(num2){};
  inner.valueOf=function(){
return temp;
};
return inner; };

 

 最後補全內部函式的主體部分,因為這個函式要能夠不停地被呼叫,所以inner函式的返回值應該是它本身:

var add=function(num){
  var temp = num;
  var inner = function(num2){
    temp+=num2;
    return inner;
  };
  inner.valueOf=function(){
return temp;
};
return inner; };

 

到此大功告成,submit, pass!

到這裡這道題告一段落,不過呢我發現上面的解決方案裡完美的解決方案還有一定距離,在我提交程式碼並看了排名靠前的程式碼後……

比如temp這個變數是必須的嗎?

程式碼裡出現了兩次 return inner;是不是有些冗餘的感覺呢?

……

 

相關文章