const Greeters = []
for (var i = 0 ; i < 10 ; i++) {
  Greeters.push(function () { return console.log(i) })
}
Greeters[0]() // 10
Greeters[1]() // 10
Greeters[2]() // 10


//1  Greeters.push(function () { returnConsole. log(I)}. Bind (null, I)let
//3  Greeters.push((function(i){
        return function (i) {
             console.log(i)
     })(i)) 
//4  Greeters.push(console.log.bind(null, i))Copy the code
<! DOCTYPE html> <html> <head> <meta charset="UTF-8">
		<title></title>
		<script type="text/javascript">
			function sum() {
				var cur = Array.prototype.slice.call(arguments).reduce(function(a,b){
					returna+b; }, 0);function add() {
					return arguments.length==0 ? cur : (cur+=Array.prototype.slice.call(arguments).reduce(function(a,b){return a+b},0), add);
				};
				returnarguments.length==0 ? 0 : add; } console.log(sum()); //0 console.log(sum(2)(3)()); / / 5 console. The log (sum ()) (2, 3); / / 5 console. The log (sum,3,5 (2) ()); / / 10. The console log (sum (2,3,5) (5) the ()); / / 15 console. The log (sum (2,3,5) (5) (5, 5) ()); //25 </script> </head> <body> </body> </html>Copy the code
function add() {
	var a = arguments[0];
	if(arguments.length == 2) {
		return a + arguments[1];
	} else {
	        return function(b) {
			returna + b; } } } console.log(add(2, 3)); //5 console.log(add(2)(3)); //5 add(1)(2)(3) call method: var add =function(a){
    return function(b){
        return function(c){
            returna+b+c; }; }; }; add(1)(2)(3); The first call to add() initializes TMP, stores x in the TMP chain, and returns TMP to ensure that the second call is TMP. The following calculations are all in the call TMP, because TMP also returns itself, which guarantees that the call after the second time will also be TMP. In TMP, the parameter passed in is added to x stored in the chain and sum is paid, which guarantees the calculation. But after the calculation is complete, we return TMP, so we don't get the result of the calculation, we need the result of the calculation to be a number. So what do we do? First of all, in JavaScript, printing and adding will call toString or valueOf, respectively, So we override the TMP toString and valueOf methods to return sum;function add(x) {
    var sum = x;
    var tmp = function (y) {
        sum = sum + y;
        return tmp;
    };
    tmp.toString = function () {
        return sum;
    };
    returntmp; } console.log(add(1)(2)(3)); //6 console.log(add(1)(2)(3)(4)); / / 10Copy the code
sum(1) // 1
sum(1)(2) // 3
sum(1)(2)(3) // 6Copy the code

C: sum = sum

sum(1) // 1
sum(1)(2) // 3
sum(1)(2)(3) // 6
Copy the code

At first glance, isn’t this a function Corrification, little Case, and then I write:

function sum(item) {
    var cur = item;
    var inner = function(next) {
        return next == null ? cur : (cur += next, inner);
    };
    return item == null ? undefined : inner;
}Copy the code

Run:

perfect!

Then I looked at the answer, and looked at the title 😕 ❓

Well, they don’t have a final (). So the final answer is:

function sum(item) {
    var cur = item;
    var inner = function(next) {
        if(next ! = null) cur += next;return inner;
    };
    inner.toString = function() {
        return cur;
    }
    return inner;
}Copy the code

As above, rewrite toString for magic effect.





Recently, someone in the group posted the following question:

Implement a function whose results satisfy the following expectations:

add(1)(2) // 3
add(1, 2, 3)(10) // 16
add(1)(2)(3)(4)(5) // 15Copy the code

For a functional programming [Ruan Yifeng functional programming introductory tutorial: http://www.ruanyifeng.com/blog/2017/02/fp-tutorial.html 】 curious amateurs front-end for himself and began to try once, High order functions and array.prototype.reduce () are used.

Higher-order function: A higher-order function means that it takes another function as an argument. In javascript, functions are first-class citizens, allowing functions to be passed as arguments or return values. It has a fancy name: functional programming!

The following solution is obtained:

function add() {
    var args = Array.prototype.slice.call(arguments);
 
    return function() {
        var arg2 = Array.prototype.slice.call(arguments);
        return args.concat(arg2).reduce(function(a, b){
            returna + b; }); }}Copy the code

Check it out and find it wrong:

add(1)(2) // 3 add(1, 2)(3) // 6 add(1)(2)(3) // Uncaught TypeError: add(...) (...). is not afunction(...).Copy the code

The above solution is only true in the case of add()(). When the chaining operation has more than two or fewer arguments, the result cannot be returned.

And that’s one of the hard parts of this problem, how do you return both a value and a function when you add()?

Later, by rewriting the valueOf or toString methods of the function, we can get one of these solutions:

function add () {
    var args = Array.prototype.slice.call(arguments);
 
    var fn = function () {
        var arg_fn = Array.prototype.slice.call(arguments);
        return add.apply(null, args.concat(arg_fn));
    }
 
    fn.valueOf = function () {
        return args.reduce(function(a, b) {
            returna + b; })}return fn;
}
Copy the code

Huh? When I first saw this solution, I was stunned. Because I felt that fn.valueof () was never called at all, but verified the result:

Add (1) / / 1 add (1, 2) (3) / / 6 add (1) (2) (3) (4) (5) / / 15Copy the code

Amazing right! Fn.valueof = function() {} Why is this so? At what point in the function is this method executed? Just listen to me step by step.

The valueOf and toString

Let’s take a quick look at these two methods:

Object.prototype.valueOf()

In MDN parlance, the valueOf() method returns the original valueOf the specified object.

JavaScript calls the valueOf() method to convert an object to a valueOf its primitive type (numeric, string, and Boolean). But we rarely need to call this function ourselves; valueOf methods are usually called automatically by JavaScript.

With that in mind, we’ll talk more about what we mean by automatic invocation.

Object.prototype.toString()

The toString() method returns a string representing the object.

Each object has a toString() method, which is called automatically when the object is represented as a text value or when the object is referenced as a desired string.

Keep in mind that valueOf() and toString() will call themselves in certain situations.

The original type

In addition to Object and Symbol, there are several primitive types of javascript:

  • Number
  • String
  • Boolean
  • Undefined
  • Null

Objects are converted to these types in JavaScript for comparison and various operations, as described below:

String conversion

When an operation or operation requires a String, the String conversion of Object is triggered, for example:

var obj = {name: 'Coco'};
var str = '123' + obj;
console.log(str);  // 123[object Object]
Copy the code

Conversion rules:

  1. iftoStringMethod exists and returns the original type, returntoStringResults.
  2. iftoStringMethod does not exist or does not return a primitive typevalueOfMethod, ifvalueOfMethod exists and returns “primitive type” data, returnsvalueOfResults.
  3. Otherwise, an error is thrown.

The above example is actually:

var obj = {name: 'Coco'};
var str = '123' + obj.toString();
Copy the code

Where obj.toString() has the value “[object object]”.

Suppose an array:

var arr = [1, 2];
var str = '123'+ arr; console.log(str); / / 1231, 2Copy the code

Above + arr actually calls + arr.tostring ().

However, we can override the object’s toString, valueOf methods ourselves:

var obj = {
    toString: function() {
        console.log('Called obj.tostring');
        return '111'; } } alert(obj); // obj.tostring // 111 is calledCopy the code

Above alert(obj), obj will automatically call its own obj.toString() method to convert to the primitive type. If we don’t override its toString method, it will print [object object]. Here we override toString, It also returns a primitive string of type 111, so it finally alerts 111.

The toString method needs to exist and return a primitive type, so if it does not return a primitive type, the valueOf method will continue to look for the object:

If the toString() method is not available, valueOf() will be called.

var obj = {
    toString: function() {
        console.log('Called obj.tostring');
        return {};
    },
    valueOf: function() {
        console.log('obj.valueof' is called.)
        return '110'; } } alert(obj); // obj.tostring is called // obj.valueof is called // 110Copy the code

As you can see from the results, when toString is not available, the system tries the valueOf method again. If the valueOf method exists, and returns primitive type (String, Number, Boolean) data, the result of valueOf is returned.

What if neither toString nor valueOf returns a primitive type? Look at this example:

 var obj = {
    toString: function() {
        console.log('Called obj.tostring');
        return {};
    },
    valueOf: function() {
        console.log('obj.valueof' is called.)
        return{}; } } alert(obj); ValueOf // Uncaught TypeError: Cannot convert object to Primitive ValueCopy the code

You can see that if both the toString and valueOf methods are unavailable, the system returns an error.

Number type conversion

String conversions can also be converted to Number:

  • Call the Number() function to force the Number type conversion
  • Calling an argument like math.sqrt () requires a method of type Number
  • obj == 1When comparing
  • obj + 1When you perform the operation

ValueOf (); toString (); toString (); toString ();

  1. ifvalueOfExists and returns data of the original typevalueOfResults.
  2. iftoStringExists and returns data of the original typetoStringResults.
  3. Otherwise, an error is thrown.

Follow these steps and try them separately:

 var obj = {
    valueOf: function() {
        console.log('call the valueOf');
        return5; } } console.log(obj + 1); Call valueOf // 6Copy the code
var obj = {
    valueOf: function() {
        console.log('call the valueOf');
        return {};
    },
    toString: function() {
        console.log('call the toString');
        return10; } } console.log(obj + 1); // Call toString // 11Copy the code
var obj = {
    valueOf: function() {
        console.log('call the valueOf');
        return {};
    },
    toString: function() {
        console.log('call the toString');
        return{}; } } console.log(obj + 1); // Uncaught TypeError: Cannot convert object to Primitive ValueCopy the code

Boolean conversion

When will Boolean conversions occur:

  • Boolean comparison
  • If (obj), while(obj), etc

In short, all values are true except for the following six:

  • undefined
  • null
  • 0
  • 0 or + 0
  • NaN
  • “(empty string)
Boolean(undefined) //false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean(' ') / /falseCopy the code

The Function conversion

Ok, so finally, back to where we started, let’s talk about the transformation of functions.

We define a function as follows:

function test() {
    var a = 1;
    console.log(1);
}
Copy the code

What happens if we just call test instead of test()?





As you can see, we have reprinted the test function we defined. In fact, we call the test function itselfvalueOfMethods:



Let’s rewrite the valueOf method of the test function.

 test.valueOf = function() {
    console.log('Call valueOf method');
    return 2;
}
 
test; // Call valueOf // 2Copy the code

Similar to the Number conversion, if a function’s valueOf method returns something other than a primitive type, it continues to find its toString method:

test.valueOf = function() {
    console.log('Call valueOf method');
    return {};
}
 
test.toString= function() {
    console.log('Call toString method');
    return 3;
}
 
test; // Call valueOf // call toString // 3Copy the code

Looking back at my answer to the question at the beginning of the text, I used the trick of the function calling the valueOf method itself and rewrote that method. Let’s change it a little bit, and the deformation is as follows:

 function add () {
    console.log('to add');
    var args = Array.prototype.slice.call(arguments);
 
    var fn = function () {
        var arg_fn = Array.prototype.slice.call(arguments);
        console.log('call fn');
        return add.apply(null, args.concat(arg_fn));
    }
 
    fn.valueOf = function () {
        console.log('call the valueOf');
        return args.reduce(function(a, b) {
            returna + b; })}return fn;
}Copy the code

When add is called once, it returns the fn function, which returns fn.valueof ();

add(1); // Enter add // call valueOf // 1Copy the code

In fact, it is equivalent to:

 [1].reduce(function(a, b) {
    returna + b; / / 1})Copy the code

When the chain is called twice:

add(1)(2); // Add // call fn // add // call valueOf // 3Copy the code

When the chain is called three times:

add(1)(2)(3); // Enter add // call fn // enter add // call fn // enter add // call valueOf // 6Copy the code

And you can see that there’s actually a kind of loop here. Only the last call can actually call valueOf, and the previous operations are all merged parameters. Recursive call itself, since the last call returns a FN function, fn.valueOf of the function is finally called, and the reduce method is used to sum all parameters.

In addition to overwriting the valueOf method, you can also overwrite the toString method, so if you prefer, the following works:

 function add () {
    var args = Array.prototype.slice.call(arguments);
 
    var fn = function () {
        var arg_fn = Array.prototype.slice.call(arguments);
        return add.apply(null, args.concat(arg_fn));
    }
 
    fn.toString = function() {
        return args.reduce(function(a, b) {
            returna + b; })}return fn;
}Copy the code

If you overwrite either valueOf() or toString(), the overridden method will be called first. If you overwrite both, valueOf() will be queried first, just like the String conversion rule. Query the toString() method if the valueOf() method returns a non-primitive type.