diff --git a/src/prototype/lang/class.js b/src/prototype/lang/class.js index e0167f74f..e71da4bd1 100644 --- a/src/prototype/lang/class.js +++ b/src/prototype/lang/class.js @@ -139,11 +139,9 @@ var Class = (function() { var ancestor = this.superclass && this.superclass.prototype, properties = Object.keys(source); - for (var i = 0, length = properties.length; i < length; i++) { - var property = properties[i], value = source[property]; - if (ancestor && Object.isFunction(value) && - value.argumentNames()[0] == "$super") { - var method = value; + function wrap_function(property, method) + { + if (method.argumentNames()[0] == "$super") { value = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; })(property).wrap(method); @@ -163,8 +161,27 @@ var Class = (function() { value.toString = (function(method) { return function() { return method.toString.call(method); }; })(method); + + return value; + } else { + return method; + } + } + + // Copy the object properties, wrapping its descriptors as necessary + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], descriptor = Object.getOwnPropertyDescriptor(source, property); + + if (ancestor && Object.isFunction(descriptor.value)) { + descriptor.value = wrap_function(property, descriptor.value); + } + if (ancestor && Object.isFunction(descriptor.get)) { + descriptor.get = wrap_function(property, descriptor.get); } - this.prototype[property] = value; + if (ancestor && Object.isFunction(descriptor.set)) { + descriptor.set = wrap_function(property, descriptor.set); + } + Object.defineProperty(this.prototype, property, descriptor); } return this; @@ -177,3 +194,4 @@ var Class = (function() { } }; })(); + diff --git a/test/unit/tests/class.test.js b/test/unit/tests/class.test.js index bb41a468a..8f100df7c 100644 --- a/test/unit/tests/class.test.js +++ b/test/unit/tests/class.test.js @@ -220,4 +220,32 @@ suite("Class", function () { assert.equal("myValueOf", new Bar().valueOf() ); }); -}); \ No newline at end of file + test('test defined properties', function () { + // Note: we cannot use the `get prop() { ... }` and `set prop(value) { ... }` style property declarations here + // because they would cause a syntax error in browsers that do not support them. We work around this by + // by creating the class base prototype first, then add the property using `Object.defineProperty` and + // finally convert it to a Prototype class. + var Foo_base = { + value1: false, + value2: false + }; + Object.defineProperty(Foo_base, 'prop', { + get: function() { return this.value1; }, + set: function(value) { this.value2 = value; }, + configurable: true, + enumerable: true + }); + var Foo = Class.create(Foo_base); + + // Create object from class + var foo = new Foo(); + + // The value returned by the property `prop` should always be the same as the value of `value1` + foo.value1 = true; + assert.equal(true, foo.prop); + + // The value set using the property `prop` should always update the value of `value2` + foo.prop = true; + assert.equal(true, foo.value2); + }); +});