39

我正在从我的请求处理程序中调用一些数据库函数。所有这些函数都为处理错误执行单独的“错误回调”。例子:

function referralComplete(req, res) {
    /*getting id etc.*/
    db.startDatabaseConnection(function() {
        db.flagReferralAsDone(id, function(success) {
            db.endDatabaseConnection();
            /*doing stuff on success*/
        }, onError);
    }, onError);

    function onError(err, description) {
        logger.error(description + ": " + err);
        user.pageNotFound(req, res);
    }
}

我有几个与此类似的请求处理程序,它们都调用不同的数据库函数。目前,由于我需要并且在处理错误时,我已经复制onError()了它们中的每一个的范围,但我喜欢认为可能有一种方法可以在没有重复的情况下实现相同的目标。reqres

所以问题是,是否有可能以某种方式绑定reqres以便onError()我不必复制onError()到每个请求处理程序中?

4

1 回答 1

85

Binding is simple!

db.startDatabaseConnection(function(){
  // whatever
}, onError.bind(this, var1, var2));

You can learn more about binding by clicking this awesome link, even though the link is sort of long.

Here's a real basic demo

// a function
var something = function (a, b, c) {
  console.log(a, b, c);
};

// a binding of something with 3 defined args
var b = something.bind(null, 1, 2, 3);

// call b
b();
//=> 1 2 3

Behind the scenes, this is basically what's happening

// ES6
const myBind = (f, context, ...x) =>
  (...y) => f.call(context, ...x, ...y);

// ES5
var myBind = function(fn, context) {
  var x = Array.prototype.slice.call(arguments, 2);
  return function() {
    var y = Array.prototype.slice.call(arguments, 0); 
    return fn.apply(context, x.concat(y));
  };
};

var b = myBind(console.log, console, 1, 2, 3);

b();
// => 1 2 3

b(4,5,6)
// => 1 2 3 4 5 6 

Context?

Context allows you to dynamically change the this of your function. Note you can only bind the context of functions defined with the function keyword; arrow functions have a lexical this that cannot be manipulated. This is shown for sake of completeness, but I do advise against this kind of program. It's usually better to just use another function parameter instead of relying on dynamic function context, this. Supporting context switching like this is to enable object-oriented style in JavaScript. Unless you are using this style, I see no reason to pay attention to context.

const getCanvas = (id) =>
  document.getElementById(id).getContext('2d')

const draw = function (canvas, x = 0, y = 0)
{ canvas.beginPath()
  canvas.strokeStyle = this.color             // `this` refers to context!
  canvas.rect(x, y, this.width, this.height)  // `this` refers to context!
  canvas.stroke()
}

// create two contexts
const contextA =
  { color: 'blue', width: 10, height: 10 }
  
const contextB =
  { color: 'green', width: 10, height: 20 }

// bind the draw function to each context and the canvas
const drawA =
  draw.bind(contextA, getCanvas('main'))
  
const drawB =
  draw.bind(contextB, getCanvas('main'))

// call the bound drawing functions normally
// draw three blue squares
drawA(0, 0)
drawA(20, 0)
drawA(40, 0)

// and one green rect
drawB(80, 0)
<canvas id="main"></canvas>

Partial application

Similar to binding is Partial Application

In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.

Here we could make a very simple partial helper procedure which helps us accomplish this

const identity = x =>
  x

const partial = (f = identity, ...x) =>
  (...y) => f (...x, ...y)

const foo = (...all) =>
  console.log ('array of', all)

partial (foo, 1, 2, 3) (4, 5, 6)
// 'array of', [ 1, 2, 3, 4, 5, 6 ]


Currying

Currying is related to, but not the same as, binding or partial application

In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument.

const identity = x =>
  x

const curry = (f = identity, arity = f.length) => x =>
{
  const next = (xs = []) =>
    xs.length >= arity
      ? f (...xs)
      : x => next ([ ...xs, x ])
  return next ([ x ])
}

const foo = (a, b, c) =>
  console.log ('a', a, 'b', b, 'c', c)

curry (foo) (1) (2) (3)
// 'a' 1 'b' 2 'c' 3

curry (foo) ('choo') ('bye') ()
// 'a' 'choo' 'b' 'bye' 'c' undefined

于 2013-07-17T22:17:32.153 回答