📄 base.rst
字号:
.. title:: MochiKit.Base - functional programming and useful comparisonsName====MochiKit.Base - functional programming and useful comparisonsSynopsis========:: myObjectRepr = function () { // gives a nice, stable string representation for objects, // ignoring any methods var keyValuePairs = []; for (var k in this) { var v = this[k]; if (typeof(v) != 'function') { keyValuePairs.push([k, v]); } }; keyValuePairs.sort(compare); return "{" + map( function (pair) { return map(repr, pair).join(":"); }, keyValuePairs ).join(", ") + "}"; }; // repr() will look for objects that have a repr method myObjectArray = [ {"a": 3, "b": 2, "repr": myObjectRepr}, {"a": 1, "b": 2, "repr": myObjectRepr} ]; // sort it by the "a" property, check to see if it matches myObjectArray.sort(keyComparator("a")); expectedRepr = '[{"a": 1, "b": 2}, {"a": 3, "b": 2}]'; assert( repr(myObjectArray) == expectedRepr ); // get just the "a" values out into an array sortedAValues = map(itemgetter("a"), myObjectArray); assert( compare(sortedAValues, [1, 3]) == 0 ); // serialize an array as JSON, unserialize it, expect something equivalent myArray = [1, 2, "3", null, undefined]; assert( objEqual(evalJSON(serializeJSON(myArray)), myArray) );Description===========:mochiref:`MochiKit.Base` is the foundation for the MochiKit suite.It provides:- An extensible comparison facility (:mochiref:`compare`, :mochiref:`registerComparator`)- An extensible programmer representation facility (:mochiref:`repr`, :mochiref:`registerRepr`)- An extensible JSON [1]_ serialization and evaluation facility (:mochiref:`serializeJSON`, :mochiref:`evalJSON`, :mochiref:`registerJSON`)- A simple adaptation facility (:mochiref:`AdapterRegistry`)- Convenience functions for manipulating objects and Arrays (:mochiref:`update`, :mochiref:`setdefault`, :mochiref:`extend`, etc.)- Array-based functional programming (:mochiref:`map`, :mochiref:`filter`, etc.)- Bound and partially applied functions (:mochiref:`bind`, :mochiref:`method`, :mochiref:`partial`)Python users will feel at home with :mochiref:`MochiKit.Base`, as thefacilities are quite similar to those available as part of Python and the Python standard library.Dependencies============None.Overview========Comparison----------The comparators (operators for comparison) in JavaScript are deeply broken,and it is not possible to teach them new tricks.MochiKit exposes an extensible comparison facility as a simple:mochiref:`compare(a, b)` function, which should be used in lieu ofJavaScript's operators whenever comparing objects other than numbersor strings (though you can certainly use :mochiref:`compare` for those, too!).The :mochiref:`compare` function has the same signature and return value as asort function for ``Array.prototype.sort``, and is often used in that context.Defining new comparators for the :mochiref:`compare` function to use is doneby adding an entry to its :mochiref:`AdapterRegistry` with the:mochiref:`registerComparator` function.Programmer Representation-------------------------JavaScript's default representation mechanism, ``toString``, is notoriousfor having terrible default behavior. It's also very unwise to change thatdefault, as other JavaScript code you may be using may depend on it.It's also useful to separate the concept of a "string representation" and a"string representation for programmers", much like Python does with its strand repr protocols.:mochiref:`repr` provides this programmer representation for JavaScript,in a way that doesn't require object prototype hacking: using an:mochiref:`AdapterRegistry`.Objects that implement the repr protocol can either implement a ``.repr()``or ``.__repr__()`` method, or they can simply have an adapter setup togenerate programmer representations. By default, the registry providesnice representations for ``null``, ``undefined``, ``Array``, and objects orfunctions with a ``NAME`` attribute that use the default ``toString``. Forobjects that ``repr`` doesn't already understand, it simply defaults to``toString``, so it will integrate seamlessly with code that implementsthe idiomatic JavaScript ``toString`` method!To define a programmer representation for your own objects, simply adda ``.repr()`` or ``.__repr__()`` method that returns a string. Forobjects that you didn't create (e.g., from a script you didn't write, or a built-in object), it is instead recommended that you create an adapterwith :mochiref:`registerRepr`.JSON Serialization------------------JSON [1]_, JavaScript Object Notation, is a widely used serialization formatin the context of web development. It's extremely simple, lightweight, andfast. In its essence, JSON is a restricted subset of JavaScript syntaxsuitable for sending over the wire that can be unserialized with a simpleeval. It's often used as an alternative to XML in "AJAX" contexts because itis compact, fast, and much simpler to use for most purposes.To create a JSON serialization of any object, simply call:mochiref:`serializeJSON()` with that object. To unserialize a JSON string,simply call :mochiref:`evalJSON()`with the serialization.In order of precedence, :mochiref:`serializeJSON` coerces the given argumentinto a JSON serialization:1. Primitive types are returned as their JSON representation: ``undefined``, ``string``, ``number``, ``boolean``, ``null``.2. If the object has a ``__json__`` or ``json`` method, then it is called with no arguments. If the result of this method is not the object itself, then the new object goes through rule processing again (e.g. it may return a string, which is then serialized in JSON format).3. If the object is ``Array``-like (has a ``length`` property that is a number, and is not a function), then it is serialized as a JSON array. Each element will be processed according to these rules in order. Elements that can not be serialized (e.g. functions) will be replaced with ``undefined``.4. The ``jsonRegistry`` :mochiref:`AdapterRegistry` is consulted for an adapter for this object. JSON adapters take one argument (the object), and are expected to behave like a ``__json__`` or ``json`` method (return another object to be serialized, or itself).5. If no adapter is available, the object is enumerated and serialized as a JSON object (name:value pairs). All names are expected to be strings. Each value is serialized according to these rules, and if it can not be serialized (e.g. methods), then that name:value pair will be skipped.Adapter Registries------------------MochiKit makes extensive use of adapter registries, which enable you toimplement object-specific behaviors for objects that you do not necessarilywant to modify, such as built-in objects. This is especially useful becauseJavaScript does not provide a method for hiding user-defined properties from``for propName in obj`` enumeration.:mochiref:`AdapterRegistry` is simply an encapsulation for an ordered list of"check" and "wrap" function pairs. Each :mochiref:`AdapterRegistry` instanceshould perform one function, but may have multiple ways to achieve thatfunction based upon the arguments. One way to think of it is as a poor man'sgeneric function, or multiple dispatch (on arbitrary functions, not just type!).Check functions take one or more arguments, and return ``true`` if theargument list is suitable for the wrap function. Check functions shouldperform "cheap" checks of an object's type or contents, before the"expensive" wrap function is called.Wrap functions take the same arguments as check functions and do someoperation, such as creating a programmer representation or comparingboth arguments.Convenience Functions---------------------Much of :mochiref:`MochiKit.Base` is there to simply remove the grunt work ofdoing generic JavaScript programming.Need to take every property from one object and set them on another? Noproblem, just call :mochiref:`update(dest, src)`! What if you just wanted toupdate keys that weren't already set? Look no further than:mochiref:`setdefault(dest, src[, ...])`.Want to return a mutable object, but don't want to suffer the consequencesif the user mutates it? Just :mochiref:`clone(it)` and you'll get acopy-on-write clone. Cheaper than a copy!Need to extend an ``Array`` with another array? Or even an ``Array``-likeobject such as a ``NodeList`` or the special ``arguments`` object? Even if youneed to skip the first few elements of the source ``Array``-like object, it'sno problem with :mochiref:`extend(dstArray, srcArrayLike[, skip])`!Wouldn't it be convenient to have all of the JavaScript operators wereavailable as functions somewhere? That's what the :mochiref:`operators` tableis for, and it even comes with additional operators based on the:mochiref:`compare` function.Need to walk some tree of objects and manipulate or find something in it?A DOM element tree perhaps? Use :mochiref:`nodeWalk(node, visitor)`!There's plenty more, so check out the `API Reference`_ below.Functional Programming----------------------Functional programming constructs such as :mochiref:`map` and:mochiref:`filter` can save you a lot of time, because JavaScript iteration iserror-prone and arduous. Writing less code is the best way to prevent bugs,and functional programming can help you do that.:mochiref:`MochiKit.Base` ships with a few simple Array-based functionalprogramming constructs, namely :mochiref:`map` and :mochiref:`filter`, andtheir "extended" brethren, :mochiref:`xmap` and :mochiref:`xfilter`.:mochiref:`map(func, arrayLike[, ...])` takes a function and an ``Array``-likeobject, and creates a new ``Array``. The new ``Array`` is the result of``func(element)`` for every element of ``arrayLike``, muchlike the ``Array.prototype.map`` extension in Mozilla. However,:mochiref:`MochiKit.Base` takes that a step further and gives you the fullblown Python version of :mochiref:`map`, which will take several``Array``-like objects, and calls the function with one argument per given``Array``-like, e.g.:: var arrayOne = [1, 2, 3, 4, 5]; var arrayTwo = [1, 5, 2, 4, 3]; var arrayThree = [5, 2, 1, 3, 4]; var biggestElements = map(objMax, arrayOne, arrayTwo, arrayThree); assert( objEqual(biggestElements, [5, 5, 3, 4, 5]) );:mochiref:`filter(func, arrayLike[, self])` takes a function and an``Array``-like object, and returns a new ``Array``.This is basically identical to the ``Array.prototype.filter``extension in Mozilla. self, if given, will beused as ``this`` in the context of func when called.:mochiref:`xmap` and :mochiref:`xfilter` are just special forms of:mochiref:`map` and :mochiref:`filter` that accept a function as the firstargument, and use the extra arguments as the ``Array``-like. Not terriblyinteresting, but a definite time-saver in some cases.If you appreciate the functional programming facilities here,you should definitely check out :mochiref:`MochiKit.Iter`, which providesfull blown iterators, :mochiref:`MochiKit.Iter.range`,:mochiref:`MochiKit.Iter.reduce`, and a near-complete port of Python'sitertools [2]_ module, with some extra stuff thrown in for good measure!Bound and Partial Functions---------------------------JavaScript's method-calling special form and lack of bound functions (functionsthat know what ``this`` should be) are one of the first stumbling blocks thatprogrammers new to JavaScript face. The :mochiref:`bind(func, self)` methodfixes that right up by returning a new function that calls func with the right``this``.In order to take real advantage of all this fancy functional programming stuff,you're probably going to want partial application. This allows you to createa new function from an existing function that remembers some of the arguments.For example, if you wanted to compare a given object to a slew of other objects, you could do something like this:: compareWithOne = partial(compare, 1); results = map(compareWithOne, [0, 1, 2, 3]); assert( objEqual(results, [-1, 0, 1, 1]) );One of the better uses of partial functions is in :mochiref:`MochiKit.DOM`,which is certainly a must-see for those of you creating lots of DOM elementswith JavaScript!API Reference=============Errors------:mochidef:`NotFound`: A singleton error raised when no suitable adapter is foundConstructors------------:mochidef:`AdapterRegistry`: A registry to facilitate adaptation. All ``check``/``wrap`` function pairs in a given registry should take the same number of arguments.:mochidef:`AdapterRegistry.prototype.register(name, check, wrap[, override])`: ``name``: a unique identifier used to identify this adapter so that it may be unregistered. ``check``: function that should return ``true`` if the given arguments are appropriate for the ``wrap`` function. ``wrap``: function that takes the same parameters as ``check`` and does the adaptation. Every ``wrap``/``check`` function pair in the registry should have the same number of arguments. ``override``: if ``true``, the ``check`` function will be given highest priority. Otherwise, the lowest.:mochidef:`AdapterRegistry.prototype.match(obj[, ...])`: Find an adapter for the given arguments by calling every ``check`` function until one returns ``true``. If no suitable adapter is found, throws :mochiref:`NotFound`.:mochidef:`AdapterRegistry.prototype.unregister(name)`: Remove a named adapter from the registry:mochidef:`NamedError`: Convenience constructor for creating new errors (e.g. :mochiref:`NotFound`)Functions---------:mochidef:`arrayEqual(self, arr)`: Compare the arrays ``self`` and ``arr`` for equality using ``compare``
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -