163 lines
5.3 KiB
JavaScript
163 lines
5.3 KiB
JavaScript
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview Restricted class definitions for Closure.
|
|
*
|
|
* @author johnlenz@google.com (John Lenz)
|
|
*/
|
|
|
|
goog.provide('goog.labs.classdef');
|
|
|
|
|
|
/** @typedef {
|
|
{constructor:!Function}|
|
|
{constructor:!Function, statics:(Object|function(Function):void)}} */
|
|
goog.labs.classdef.ClassDescriptor;
|
|
|
|
|
|
/**
|
|
* Creates a restricted form of a Closure "class":
|
|
* - from the compiler's perspective, the instance returned from the
|
|
* constructor is sealed (no new properties may be added). This enables
|
|
* better type checking.
|
|
* - the compiler will rewrite this definition to a form that is optimal
|
|
* for type checking and optimization (initially this will be a more
|
|
* traditional form).
|
|
*
|
|
* @param {Function} superClass The superclass or null.
|
|
* @param {goog.labs.classdef.ClassDescriptor} def
|
|
* An object literal describing the
|
|
* the class. It may have the following properties:
|
|
* "constructor": the constructor function
|
|
* "statics": an object literal containing methods to add to the constructor
|
|
* as "static" methods or a function that will recieve the constructor
|
|
* function as its only parameter to which static properties can
|
|
* be added.
|
|
* all other properties are added to the prototype.
|
|
* @return {!Function} The class constructor.
|
|
*/
|
|
goog.labs.classdef.defineClass = function(superClass, def) {
|
|
// TODO(johnlenz): consider making the superClass an optional parameter.
|
|
var constructor = def.constructor;
|
|
var statics = def.statics;
|
|
// Wrap the constructor prior to setting up the prototype and static methods.
|
|
if (!constructor || constructor == Object.prototype.constructor) {
|
|
throw Error('constructor property is required.');
|
|
}
|
|
|
|
var cls = goog.labs.classdef.createSealingConstructor_(constructor);
|
|
if (superClass) {
|
|
goog.inherits(cls, superClass);
|
|
}
|
|
|
|
// Remove all the properties that should not be copied to the prototype.
|
|
delete def.constructor;
|
|
delete def.statics;
|
|
|
|
goog.labs.classdef.applyProperties_(cls.prototype, def);
|
|
if (statics != null) {
|
|
if (statics instanceof Function) {
|
|
statics(cls);
|
|
} else {
|
|
goog.labs.classdef.applyProperties_(cls, statics);
|
|
}
|
|
}
|
|
|
|
return cls;
|
|
};
|
|
|
|
|
|
/**
|
|
* @define {boolean} Whether the instances returned by
|
|
* goog.labs.classdef.defineClass should be sealed when possible.
|
|
*/
|
|
goog.define('goog.labs.classdef.SEAL_CLASS_INSTANCES', goog.DEBUG);
|
|
|
|
|
|
/**
|
|
* If goog.labs.classdef.SEAL_CLASS_INSTANCES is enabled and Object.seal is
|
|
* defined, this function will wrap the constructor in a function that seals the
|
|
* results of the provided constructor function.
|
|
*
|
|
* @param {!Function} ctr The constructor whose results maybe be sealed.
|
|
* @return {!Function} The replacement constructor.
|
|
* @private
|
|
*/
|
|
goog.labs.classdef.createSealingConstructor_ = function(ctr) {
|
|
if (goog.labs.classdef.SEAL_CLASS_INSTANCES &&
|
|
Object.seal instanceof Function) {
|
|
/** @this {*} */
|
|
var wrappedCtr = function() {
|
|
// Don't seal an instance of a subclass when it calls the constructor of
|
|
// its super class as there is most likely still setup to do.
|
|
var instance = ctr.apply(this, arguments) || this;
|
|
if (this.constructor === wrappedCtr) {
|
|
Object.seal(instance);
|
|
}
|
|
return instance;
|
|
};
|
|
return wrappedCtr;
|
|
}
|
|
return ctr;
|
|
};
|
|
|
|
|
|
// TODO(johnlenz): share these values with the goog.object
|
|
/**
|
|
* The names of the fields that are defined on Object.prototype.
|
|
* @type {!Array.<string>}
|
|
* @private
|
|
* @const
|
|
*/
|
|
goog.labs.classdef.OBJECT_PROTOTYPE_FIELDS_ = [
|
|
'constructor',
|
|
'hasOwnProperty',
|
|
'isPrototypeOf',
|
|
'propertyIsEnumerable',
|
|
'toLocaleString',
|
|
'toString',
|
|
'valueOf'
|
|
];
|
|
|
|
|
|
// TODO(johnlenz): share this function with the goog.object
|
|
/**
|
|
* @param {!Object} target The object to add properties to.
|
|
* @param {!Object} source The object to copy properites from.
|
|
* @private
|
|
*/
|
|
goog.labs.classdef.applyProperties_ = function(target, source) {
|
|
// TODO(johnlenz): update this to support ES5 getters/setters
|
|
|
|
var key;
|
|
for (key in source) {
|
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
|
|
// For IE the for-in-loop does not contain any properties that are not
|
|
// enumerable on the prototype object (for example isPrototypeOf from
|
|
// Object.prototype) and it will also not include 'replace' on objects that
|
|
// extend String and change 'replace' (not that it is common for anyone to
|
|
// extend anything except Object).
|
|
for (var i = 0; i < goog.labs.classdef.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
|
|
key = goog.labs.classdef.OBJECT_PROTOTYPE_FIELDS_[i];
|
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
};
|