Implementation of knockout monitoring array

Source: Internet
Author: User
Tags diff empty extend min pack shallow copy

Knockout should be the most widely used MVVM framework in the blogosphere, but rarely introduces the implementation of its monitoring array. Recently tried to upgrade the Avalon of the array of surveillance, decided to study it a good, see if there is no reference to the place.

Ko.observablearray = function (initialvalues) {
    initialvalues = initialvalues | | [];
    
    if (typeof initialvalues!= ' object ' | |!) (' Length ' in Initialvalues ')
        throw new Error ("The argument passed when initializing a observable array must is an array , or null, or undefined. ");
    
    var result = ko.observable (initialvalues);
    Ko.utils.extend (result, ko.observablearray[' FN ']);
    Return Result.extend ({' Trackarraychanges ': true});

This is the factory method for knockout monitoring arrays, without using the new keyword, directly converting a regular array into a monitoring array. You can also do nothing and get an empty array of controls.

var Myobservablearray = Ko.observablearray ();    Initially an empty array
myobservablearray.push (' Some value ');            Adds the value and notifies OBS
    
//This observable array initially contains three objects
var anotherobservable Array = Ko.observablearray ([
    {name: "Bungle", type: "Bear"},
    {name: "George", type: "Hippo"},
    {name: "Zip" Py ", type:" Unknown "}
]);
Console.log (typeof Anotherobservablearray)//function

Although an array of monitoring, but its type is actually a function. This is where knockout is so unpleasant that the original string, number, Boolean, array, and so on are converted into functions to use.

Here's a ko.utils.extend method that is less than jquery's method of the same name, just a shallow copy, which loops over the attributes of one object to the other.

Extend:function (target, source) {
    if (source) {(
        var prop in source) {
            if (Source.hasownproperty prop )) {
                Target[prop] = Source[prop];
    }} return target;
},

Result is the function to be returned, and it is hung with many methods and properties. The first is the ko.observablearray[' fn '] expansion pack, and the second extension can actually be simplified to

Result.trackarraychanges = True

Let's take a look at the ko.observablearray[' FN '] expansion pack, the hardest of which is the implementation of Pop,push,shift and other methods

ko.observablearray[' fn '] = {' Remove ': function (valueorpredicate) {//value can be the original array or a monitoring function var underlyingarray = th
        Is.peek ()//Get the original array var removedvalues = []; var predicate = typeof Valueorpredicate = = "function" &&!ko.isobservable (valueorpredicate)?
        Valueorpredicate:function (value) {return value = = Valueorpredicate;
            };//make sure to convert to a function for (var i = 0; i < underlyingarray.length i++) {var value = Underlyingarray[i]; if (predicate (value)) {if (removedvalues.length = 0) {This.valuewillmut
                Ate ();//Start change} removedvalues.push (value);
            Underlyingarray.splice (i, 1);//removing element i--;
        } if (removedvalues.length) {//If not NULL, the description occurs and the valuehasmutated this.valuehasmutated () is invoked;
      Return removedvalues;//returns the removed element}, ' RemoveAll ': function (arrayofvalues) {  If you passed zero args, we remove everything if (arrayofvalues = = undefined) {//If nothing is passed, empty the array V
            Ar underlyingarray = This.peek ();
            var allvalues = underlyingarray.slice (0);
            This.valuewillmutate ();
            Underlyingarray.splice (0, underlyingarray.length);
            This.valuehasmutated ();
        return allvalues;
        //If it is an incoming null string, NULL, NaN if (!arrayofvalues) return []; Return this[' Remove ' (function (value) {//Otherwise call the Remove method above return Ko.utils.arrayIndexOf (arrayofvalues, value) &G
        t;= 0;
    });
        }, ' Destroy ': function (valueorpredicate) {The optimized version of the//remove method, does not immediately remove elements, just mark the var underlyingarray = This.peek (); var predicate = typeof Valueorpredicate = = "function" &&!ko.isobservable (valueorpredicate)?
        Valueorpredicate:function (value) {return value = = Valueorpredicate;
        };
        This.valuewillmutate (); for (var i = UndeRlyingarray.length-1; I >= 0;
            i--) {var value = Underlyingarray[i];
        if (predicate (value)) underlyingarray[i]["_destroy" = true;
    } this.valuehasmutated (); }, ' Destroyall ': function (arrayofvalues) {//removeall The optimized version of the method, do not immediately remove elements, just mark if (arrayofvalues = = undefined)/
    
        The pass is marked as destroy return this[' Destroy ' (function () {return true}); If you are passed an ARG, we interpret it as a array of entries to destroy if (!arrayofvalues) return
        [];
        Return this[' Destroy ' (function (value) {return ko.utils.arrayIndexOf (arrayofvalues, value) >= 0;
    });
        }, ' IndexOf ': function (item) {//Return index value var underlyingarray = this ();
    Return Ko.utils.arrayIndexOf (Underlyingarray, item);
        }, ' Replace ': function (Olditem, newitem) {//replace element var index = this[' indexOf ' in a location (Olditem); if (Index >= 0) {this.valuewillmutate ();
            This.peek () [index] = newitem;
        This.valuehasmutated ();
    
}
    }
}; Add a series of methods Ko.utils.arrayForEach with the same name as the native array ([Pop, push, reverse, shift, sort, splice, unshift], function (
        MethodName) {ko.observablearray[' fn '][methodname] = function () {var underlyingarray = This.peek ();
        This.valuewillmutate ();
        This.cachediffforknownoperation (Underlyingarray, MethodName, arguments);
        var methodcallresult = underlyingarray[methodname].apply (underlyingarray, arguments);
        This.valuehasmutated ();
    return methodcallresult;
};
    
}); Returns a true array Ko.utils.arrayForEach (["slice"], function (methodname) {ko.observablearray[' fn '][methodname] = function (
        ) {var Underlyingarray = this ();
    Return underlyingarray[methodname].apply (Underlyingarray, arguments);
}; });

Cachediffforknownoperation records how the elements are manipulated

    Target.cachediffforknownoperation = function (Rawarray, OperationName, args) {//Only run if we ' re currently
        Tracking changes for this observable array//And there aren ' t any pending deferred.
        if (!trackingchanges | | pendingnotifications) {return;
                var diff = [], arraylength = rawarray.length, argslength = Args.length,
    
        offset = 0; function Pushdiff (status, value, index) {return diff[diff.length] = {' Status ': status, ' Value ': Value, ' Index
        ': index};
            Switch (operationname) {case ' push ': offset = arraylength; Case ' Unshift ': for (var index = 0; index < argslength; index++) {Pushdiff (' added ')
                , Args[index], offset + index);
    
            } break;
            Case ' pop ': offset = arrayLength-1; CasE ' shift ': if (arraylength) {Pushdiff (' deleted ', rawarray[offset], offset);
    
            } break; Case ' splice '://Negative start index means ' from end of array '.
                After so we clamp to [0...arrayLength]. Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice var s
                        Tartindex = math.min (Math.max (0, Args[0] < 0 arraylength + args[0]: args[0]), arraylength), Enddeleteindex = Argslength = = 1? ArrayLength:Math.min (StartIndex + (args[1] | | 0), arraylength), Endaddindex = StartIndex + ARGSL Ength-2, Endindex = Math.max (Enddeleteindex, endaddindex), additions = [
                ], deletions = []; for (var index = startIndex, Argsindex = 2; index < endindex; ++index, ++argsindex) {if (index ; EnddelEteindex) Deletions.push (Pushdiff (' deleted ', Rawarray[index], index));
                if (Index < Endaddindex) Additions.push (Pushdiff (' added ', Args[argsindex], index);
                } ko.utils.findMovesInArrayComparison (deletions, additions);
    
            Break
        Default:return;
    } Cacheddiff = diff;
};
    
}; Ko.utils.findMovesInArrayComparison = function (left, right, limitfailedcompares) {if (Left.length && right.le
        Ngth) {var failedcompares, L, R, Leftitem, Rightitem; for (failedcompares = L = 0; (!limitfailedcompares | | failedcompares < limitfailedcompares) && (Leftitem = left[l]); ++l) {for (r = 0; rightitem = right[r]; ++r) {if (leftitem[' value '] = = = rightitem[' value ')
                    {leftitem[' moved '] = rightitem[' index ']; Rightitem[' moved '] = leftitem['Index '];         Right.splice (R, 1); This item is marked as moved;     So remove it from right list failedcompares = R = 0;
                Reset failed compares count because we ' re checking for consecutive failures;
        } Failedcompares + = r; }
    }
};

Back to the column page: http://www.bianceng.cnhttp://www.bianceng.cn/webkf/script/

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.