binding/applying constructors in JavaScript

前端 未结 2 1587
后悔当初
后悔当初 2020-12-31 08:46

I was looking for solutions for calling Javascript constructors with an arbitrary number of arguments, and found some good SO posts, which led me to believe that these three

相关标签:
2条回答
  • 2020-12-31 09:30

    The previously accepted answer was incorrect. You can use bind, call and apply with constructors to create new constructors just fine -- the only problem in your test is that you've forgotten that bind.apply and bind.call are applying and calling bind, not the constructor itself, so you gave the wrong arguments.

    f = Date.bind(null, 2000,0,1)
    g = Function.bind.call(Date, null, 2000, 0, 1)
    h = Function.bind.apply(Date, [ null, 2000, 0, 1 ])
    
    new f() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
    new g() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
    new h() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
    

    All three are instanceof Date.

    Call's arguments are the execution context followed by the arguments to apply. Apply's arguments are the execution context and an array of arguments. Bind's arguments are the execution context followed by the arguments to bind.

    So the arguments to apply, for example, are the context for which to apply bind (Date) followed by an array which is the arguments for bind (so the first array member is the bind's context argument). This is why it's confusing to call or apply bind; it feels strange to supply context arguments to both.

    Note that, when using bind with constructors, the context argument is always ignored because 'new' explicitly creates a new context. I use null when the context argument is irrelevant to keep that clear, but it can be anything.

    Meanwhile, apply and call in these examples do need to know that the context in which they are to apply/call bind is the Date function. I switched 'Date' to 'Function' where possible to help illuminate what is actually supplying context where. When we call apply or call on Date.bind, we are really calling apply or call on the bind method unattached to the Date object. The bind method in such a case could come from any function at all. It could be Number.bind.call(Date, null, 2000, 0, 1) and the result would be exactly the same.

    If it's not obvious why, consider the difference between the following examples:

    context.method();
    

    and

    var noLongerAMethod = context.method;
    noLongerAMethod();
    

    In the second case, the method has been divorced from its original context (...unless it was previously bound) and will behave differently if it was relying on 'this' internally. When we pull bind off any given function as a property, rather than executing it directly, it is simply another pointer to the generic bind method on Function.prototype.

    Personally I don't think I've ever needed to call or apply bind, and it's hard to imagine a situation for which it would be a good solution, but binding constructors to create new constructors is something I've found very useful on occasion. In any case it's a fun puzzle.

    0 讨论(0)
  • 2020-12-31 09:34

    bind and apply / call only works work invocation to function but not constructor, so basically with native methods you cannot do this, one way is to write a bindConstruct method but it may involves extra complexity:

    function bindConstruct(fn) {
        // since constructor always accepts a static this value
        // so bindConstruct cannot specify this
        var extraArgs = [].slice.call(arguments, 1);
    
        // create a 'subclass' of fn
        function sub() {
            var args = extraArgs.concat([].slice.call(arguments));
            fn.apply(this, args);
        }
        sub.prototype = fn.prototype;
        sub.prototype.constructor = sub;
    
        return sub;
    }
    

    This, actually, creates a subclass to your constructor.

    Then your code:

    var MyClass = function(x, y) {
        console.log(arguments);
        console.log(x + y);
    }
    var BindedMyClass = bindConstruct(MyClass, 1, 2, 3);
    var c = new BindedMyClass(4, 5);
    console.log(c instanceof MyClass);
    console.log(c instanceof BindedMyClass);
    

    You may also write this function to Function.prototype or as an extension to the native bind function.

    0 讨论(0)
提交回复
热议问题