📄 core.js
字号:
/** * @fileoverview * Provides low-level core functionality. Requires nothing. * <p> * Provides core APIs for creating object-oriented and event-driven JavaScript code. Features include: * <ul> * <li>Provides API for declaring JavaScript classes which includes support for * specifying abstract and virtual properties and validating subtypes to such * specification.</li> * <li>Provides a "Method Wrapper" function (Core.method()) to create a function which will invoke * a member function of a specific object instance (enabling invocation with the "this pointer" set * appropriately).</li> * <li>Provides event/listener management framework. Event-listeners which invoke * methods of an object instance may be created using the Core.method() function.</li> * <li>Provides a "Large Map" useful for managing an associative array that is frequently modified * and will exist for a long period of time. This object is unfortunately necessary due to * issues present in certain clients (Internet Explorer 6 memory leak / performance degradation).</li> * <li>Provides array manipulation utilities.<li> * <li>Provides some simple debugging utilities, e.g., a pseudo-console output.</li> * <li>Does not provide any web-specific functionality.</li> * </ul> *//** * Namespace for core functionality. * @namespace */Core = { /** * Creates a duplicate copy of a function by wrapping the original in a closure. * * @param f the function * @return an effectively identical copy */ _copyFunction: function(f) { return function() { f.apply(this, arguments); }; }, /** * Creates an empty function. */ _createFunction: function() { return function() { }; }, /** * Creates a new class, optionally extending an existing class. * This method may be called with one or two parameters as follows: * <p> * <code>Core.extend(definition)</code> * <code>Core.extend(baseClass, definition)</code> * <p> * Each property of the definition object will be added to the prototype of the returned defined class. * Properties that begin with a dollar-sign (<code>$</code>) will be processed specially: * <p> * <ul> * <li>The <code>$construct</code> property, which must be a function, will be used as the constructor. * The <code>$load</code> property, which must be a function, f provided, will be used as a static initializer, * executed once when the class is *defined*. The this pointer will be set to the class when * this method is executed.</li> * <li>The <code>$static</code> property, an object, if provided, will have its properties installed as class variables.</li> * <li>The <code>$abstract</code> property, an object or <code>true</code>, if provided, will define methods that * must be implemented by derivative classes. If the value is simply <code>true</code>, the object will be marked as * abstract (such that it does not necessarily need to provide implementations of abstract methods defined in its * base class.)</li> * <li>The <code>$virtual</code> property, an object, if provided, defines methods that will be placed into the prototype * that may be overridden by subclasses. Attempting to override a property/method of the superclass that * is not defined in the virtual block will result in an exception. Having the default behavior NOT allow * for overriding ensures that namespacing between super- and sub-types if all internal variables are instance * during <code>Core.extend()</code>.</li> * </ul> * <p> * Use of this method enables a class to be derived WITHOUT executing the constructor of the base class * in order to create a prototype for the derived class. This method uses a "shared prototype" architecture, * where two objects are created, a "prototype class" and a "constructor class". These two objects share * the same prototype, but the "prototype class" has an empty constructor. When a class created with * this method is derived, the "prototype class" is used to create a prototype for the derivative. * <p> * This method will return the constructor class, which contains an internal reference to the * prototype class that will be used if the returned class is later derived by this method. * * @param {Function} baseClass the base class * @param {Object} definition an associative array containing methods and properties of the class * @return the constructor class */ extend: function() { // Configure baseClass/definition arguments. var baseClass = arguments.length == 1 ? null : arguments[0]; var definition = arguments.length == 1 ? arguments[0] : arguments[1]; var x, name; // Perform argument error checking. if (arguments.length == 2) { if (typeof(baseClass) != "function") { throw new Error("Base class is not a function, cannot derive."); } } if (!definition) { throw new Error("Object definition not provided."); } // Create the constructor class. var constructorClass; if (definition.$construct) { // Definition provides constructor, provided constructor function will be used as object. constructorClass = definition.$construct; // Remove property such that it will not later be added to the object prototype. delete definition.$construct; } else { // Definition does not provide constructor. if (baseClass) { // Base class available: copy constructor function from base class. // Note: should function copying not be supported by a future client, // it is possible to simply create a new constructor which invokes the base // class constructor (using closures and Function.apply()) to achieve the // same effect (with a slight performance penalty). constructorClass = Core._copyFunction(baseClass); } else { // No base class: constructor is an empty function. constructorClass = Core._createFunction(); } } // Create virtual property storage. constructorClass.$virtual = {}; // Store reference to base class in constructor class. constructorClass.$super = baseClass; if (baseClass) { // Create class with empty constructor that shares prototype of base class. var prototypeClass = Core._createFunction(); prototypeClass.prototype = baseClass.prototype; // Create new instance of constructor-less prototype for use as prototype of new class. constructorClass.prototype = new prototypeClass(); } // Assign constructor correctly. constructorClass.prototype.constructor = constructorClass; // Add abstract properties. if (definition.$abstract) { constructorClass.$abstract = {}; if (baseClass && baseClass.$abstract) { // Copy abstract properties from base class. for (x in baseClass.$abstract) { constructorClass.$abstract[x] = baseClass.$abstract[x]; } } if (definition.$abstract instanceof Object) { // Add abstract properties from definition. for (x in definition.$abstract) { constructorClass.$abstract[x] = true; constructorClass.$virtual[x] = true; } } // Remove property such that it will not later be added to the object prototype. delete definition.$abstract; } // Copy virtual property flags from base class to shared prototype. if (baseClass) { for (name in baseClass.$virtual) { constructorClass.$virtual[name] = baseClass.$virtual[name]; } } // Add virtual instance properties from definition to shared prototype. if (definition.$virtual) { Core._inherit(constructorClass.prototype, definition.$virtual, constructorClass.$virtual); for (name in definition.$virtual) { constructorClass.$virtual[name] = true; } // Remove property such that it will not later be added to the object prototype. delete definition.$virtual; } // Add toString and valueOf manually, as they will not be iterated // by for-in iteration in Internet Explorer. if (definition.hasOwnProperty("toString")) { constructorClass.prototype.toString = definition.toString; } if (definition.hasOwnProperty("valueOf")) { constructorClass.prototype.valueOf = definition.valueOf; } // Remove properties such that they will not later be added to the object prototype. delete definition.toString; delete definition.valueOf; // Add Mixins. if (definition.$include) { // Reverse order of mixins, such that later-defined mixins will override earlier ones. // (Mixins will only be added if they will NOT override an existing method.) var mixins = definition.$include.reverse(); Core._processMixins(constructorClass, mixins); // Remove property such that it will not later be added to the object prototype. delete definition.$include; } // Store $load static initializer and remove from definition so it is not inherited in static processing. var loadMethod = null; if (definition.$load) { loadMethod = definition.$load; // Remove property such that it will not later be added to the object prototype. delete definition.$load; } // Process static properties and methods defined in the '$static' object. if (definition.$static) { Core._inherit(constructorClass, definition.$static); // Remove property such that it will not later be added to the object prototype. delete definition.$static; } // Process instance properties and methods. Core._inherit(constructorClass.prototype, definition, constructorClass.$virtual); // If class is concrete, verify all abstract methods are provided. if (!constructorClass.$abstract) { this._verifyAbstractImpl(constructorClass); } // Invoke static constructors. if (loadMethod) { // Invoke $load() function with "this" pointer set to class. loadMethod.call(constructorClass); } return constructorClass; }, /** * Retrieves a value from an object hierarchy. * * Examples: * Given the following object 'o': <code>{ a: { b: 4, c: 2 }}</code> * <ul> * <li><code>Core.get(o, ["a", "b"]) will return <code>4</code>.</li> * <li><code>Core.get(o, ["a", "c"]) will return <code>2</code>.</li> * <li><code>Core.get(o, ["a", "d"]) will return <code>null</code>.</li> * <li><code>Core.get(o, ["a"]) will return <code>{ b: 4, c: 2 }</code>.</li> * <li><code>Core.get(o, ["b"]) will return <code>null</code>.</li> * <li><code>Core.get(o, ["d"]) will return <code>null</code>.</li> * </ul> * * @param object an arbitrary object from which the value should be retrieved * @param {Array} path an array of object property names describing the path to retrieve * @return the value, if found, or null if it does not exist */ get: function(object, path) { for (var i = 0; i < path.length; ++i) { object = object[path[i]]; if (!object) { return null; } } return object; }, /** * Determines if the specified propertyName of the specified object is a virtual * property, i.e., that it can be overridden by subclasses. */ _isVirtual: function(virtualProperties, propertyName) { switch (propertyName) { case "toString": case "valueOf": return true; } return virtualProperties[propertyName]; }, /** * Installs properties from source object into destination object. * <p> * In the case where the destination object already has a property defined * and the "virtualProperties" argument is provided, the "virtualProperties" * collection will be checked to ensure that property is allowed to be * overridden. If "virtualProperties" is omitted, any property may be * overridden. * * @param destination the destination object * @param soruce the source object * @param virtualProperties (optional) collection of virtual properties from base class. */ _inherit: function(destination, source, virtualProperties) { for (var name in source) { if (virtualProperties && destination[name] !== undefined && !this._isVirtual(virtualProperties, name)) { // Property exists in destination as is not marked as virtual. throw new Error("Cannot override non-virtual property \"" + name + "\"."); } else { destination[name] = source[name]; } } }, /** * Creates a new function which executes a specific method of an object instance. * Any arguments passed to the returned function will be passed to the method. * The return value of the method will be returned by the function. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -