JavaScript Layout Functions

I am reading a book that contains the following example:

var composition1 = function(f, g) {
  return function(x) {
    return f(g(x));
  }
};

Then the author writes: "... a naive implementation of the composition, because it does not take into account the execution context ..."

Thus, a preferred function is as follows:

var composition2 = function(f, g) {
  return function() {
    return f.call(this, g.apply(this, arguments));
  }
}; 

The following is an example:

var composition2 = function composition2(f, g) {
    return function() {
        return f.call(this, g.apply(this, arguments));
    }
};

var addFour = function addFour(x) {
    return x + 4;
};

var timesSeven = function timesSeven(x) {
    return x * 7;
};

var addFourtimesSeven2 = composition2(timesSeven, addFour);
var result2 = addFourtimesSeven2(2);
console.log(result2);

Can someone explain to me why the compos2 function is preferred (maybe with an example)?

EDIT:

In the meantime, I tried to use the methods as arguments, as suggested, but that didn't work. The result was NaN:

var composition1 = function composition1(f, g) {
    return function(x) {
        return f(g(x));
    };
};

var composition2 = function composition2(f, g) {
    return function() {
        return f.call(this, g.apply(this, arguments));
    }
};

var addFour = {
    myMethod: function addFour(x) {
        return x + this.number;
    },
    number: 4
};

var timesSeven = {
    myMethod: function timesSeven(x) {
        return x * this.number;
    },
    number: 7
};

var addFourtimesSeven1 = composition1(timesSeven.myMethod, addFour.myMethod);
var result1 = addFourtimesSeven1(2);
console.log(result1);

var addFourtimesSeven2 = composition2(timesSeven.myMethod, addFour.myMethod);
var result2 = addFourtimesSeven2(2);
console.log(result2);
+4
source share
3 answers

This is just the answer to what composition2actually is:

composition2 , this . , 60 data.a data.b:

'use strict';

var multiply = function(value) {
    return value * this.a;
}
var add = function(value) {
    return value + this.b;
}

var data = {
    a: 10,
    b: 4,
    func: composition2(multiply, add)
};

var result = data.func(2);
// uses 'data' as 'this' inside the 'add' and 'multiply' functions
// (2 + 4) * 10 = 60

- ( ):

'use strict';

function Foo() {
    this.a = 10;
    this.b = 4;
}
Foo.prototype.multiply = function(value) {
    return value * this.a;
};
Foo.prototype.add = function(value) {
    return value + this.b;
};


var foo = new Foo();

var func = composition2(foo.multiply, foo.add);
var result = func(2); // Uncaught TypeError: Cannot read property 'b' of undefined

composition2 (this) - undefined ( - , .apply, .call obj.func())), this undefined .

, , :

'use strict';
var foo = new Foo();

var data = {
    a: 20,
    b: 8,
    func: composition2(foo.multiply, foo.add)
}

var result = data.func(2); 
// uses 'data' as 'this'
// (2 + 8) * 10 = 200 :)

:

'use strict';

var multiply = function(value) {
    return value * this.a;
};
var add = function(value) {
    return value + this.b;
};


var a = 20;
var b = 8;

var func = composition2(multiply, add);

// All the same
var result1 = this.func(2);
var result2 = func.call(this, 2);
var result3 = func.apply(this, [2]);
+1

composition1 , , g()

:

var composition1 = function(f, g) {
  return function(x1, x2, x3) {
    return f(g(x1, x2, x3));
  }
};

. , , Function.prototype.apply.

f.call(...) this, Caramiriel.

0

.

-. - ( , in, result out this ).

. arguments , , /, , g() .

, , , , .
, ;

( : - - , JS- )

partial application, currying JS, : , , ; , .

-, , configs-first, data-last.
:

//a transformer: value in, lowercased string out
var toLowerCase = function(str){
    return String(str).toLowerCase();
}

//the original function expects 3 arguments, 
//two configs and the data to process.
var replace = curry(function(needle, heystack, str){
    return String(str).replace(needle, heystack);
});

//now I pass a partially applied function to map() that only 
//needs the data to process; this is really composable
arr.map( replace(/\s[A-Z]/g, toLowerCase) );

//or I create another utility by only applying the first argument
var replaceWhitespaceWith = replace(/\s+/g);
//and pass the remaining configs later
arr.map( replaceWhitespaceWith("-") );

, , , ( )

var prepend = a => b => String(a) + String(b);  //one by one
var substr = (from, to) => value => String(str).substr(from, to);  //or grouped

arr.map( compose( prepend("foo"), substr(0, 5) ) );
arr.map( compose( prepend("bar"), substr(5) ) );
//and the `to`-argument is undefined; by intent

I’m not going to ever call such functions with all arguments, all I want to pass to them is their configs and get a function that performs the task on the transmitted data / value.

Instead, substr(0, 5, someString)I would always write someString.substr(0, 5), so why make any effort so that the last argument (data) is applicable on the first call?

0
source

All Articles