What order are knockout js observable extensions executed?

At the bottom of the knockoutjs docs for extending observables it states...

More than one extender can be applied in a single call to the .extend method of an observable.

this.firstName = ko.observable(first).extend({ required: "Please enter a first name", logChange: "first name" });

In this case, both the required and logChange extenders would be executed against our observable.

... I want to know what order the extensions will be executed in. Will it always execute in the same order? What definesd the order?

Answers:

Answer

If the order is important, you can call extend multiple times:

this.firstName = ko.observable(first)
    .extend({ required: "Please enter a first name" })
    .extend({ logChange: "first name" });

That being said, browsers generally process an object's properties in the same order they were defined. Thus your original method will have the same effect as this one.

Answer

The order is undefined in the current Knockout (3.4.0 as of this writing), because JavaScript object properties had no defined order prior to ES2015, and even in ES2015, for-in and Object.keys have no order.

The current Knockout loops through those extenders like this:

function applyExtenders(requestedExtenders) {
    var target = this;
    if (requestedExtenders) {
        ko.utils.objectForEach(requestedExtenders, function(key, value) {
            var extenderHandler = ko.extenders[key];
            if (typeof extenderHandler == 'function') {
                target = extenderHandler(target, value) || target;
            }
        });
    }
    return target;
}

Its objectForEach function uses for-in:

function objectForEach(obj, action) {
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            action(prop, obj[prop]);
        }
    }
}

So, there is no defined order in which the extenders will run (even on ES2015) in the current Knockout.

In theory, a future version of Knockout could use the new Object.getOwnPropertyNames instead:

function applyExtenders(requestedExtenders) {
    var target = this;
    if (requestedExtenders) {
        Object.getOwnPropertyNames(requestedExtenders).forEach(function(key) {
            var extenderHandler = ko.extenders[key];
            if (typeof extenderHandler == 'function') {
                target = extenderHandler(target, requestedExtenders[key]) || target;
            }
        });
    }
    return target;
}

getOwnPropertyNames respects the newly-defined order of JavaScript properties, listed in §9.1.12 of the spec. In your example, that would be required, then logChange, because object initializers create properties in lexical order, and the order of "own" properties with names that are not all-numeric and not Symbols is the order of creation (but see the spec for details).

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.