1
0
Fork 0

Began adding protected/private member support

- No longer adding ANY properties to prototype
- protected/private members no longer part of the public access level
closure/master
Mike Gerwitz 2011-01-27 22:35:40 -05:00
parent 8c7ad787c8
commit 38c16048cb
4 changed files with 97 additions and 17 deletions

View File

@ -212,10 +212,9 @@ var extend = ( function( extending )
base = args.pop() || Class,
prototype = new base();
var properties = {},
members = member_builder.initMembers(
prototype, prototype, prototype
),
var properties = {},
members = member_builder.initMembers( prototype ),
prop_init = member_builder.initMembers(),
abstract_methods =
util.clone( getMeta( base.__cid ).abstractMethods )
@ -236,8 +235,10 @@ var extend = ( function( extending )
{
properties[ name ] = value;
// build a new property, passing in the other members to compare
// against for preventing nonsensical overrides
member_builder.buildProp(
members, null, name, value, keywords
prop_init, null, name, value, keywords, members
);
},
@ -291,7 +292,7 @@ var extend = ( function( extending )
// set up the new class
var new_class = createCtor( abstract_methods );
attachPropInit( prototype, properties );
attachPropInit( prototype, prop_init );
new_class.prototype = prototype;
new_class.constructor = new_class;
@ -470,6 +471,8 @@ function setupProps( func, abstract_methods, class_id )
*/
function attachPropInit( prototype, properties )
{
var prop_pub = properties[ 'public' ];
util.defineSecureProp( prototype, '__initProps', function()
{
// first initialize the parent's properties, so that ours will overwrite
@ -482,11 +485,11 @@ function attachPropInit( prototype, properties )
// initialize each of the properties for this instance to
// ensure we're not sharing prototype values
for ( prop in properties )
for ( prop in prop_pub )
{
// initialize the value with a clone to ensure that they do
// not share references (and therefore, data)
this[ prop ] = util.clone( properties[ prop ] );
this[ prop ] = util.clone( prop_pub[ prop ] );
}
});
}

View File

@ -62,11 +62,12 @@ exports.initMembers = function( mpublic, mprotected, mprivate )
*
* @return {undefined}
*/
exports.buildMethod = function( members, meta, name, value, keywords )
exports.buildMethod = function( members, meta, name, value, keywords, cmp )
{
var prev = scanMembers( members, name );
var prev;
if ( prev )
// search for any previous instances of this member
if ( prev = scanMembers( members, name ) )
{
// disallow overriding properties with methods
if ( !( prev instanceof Function ) )
@ -124,13 +125,15 @@ exports.buildMethod = function( members, meta, name, value, keywords )
* @param {*} value property value
*
* @param {Object.<string,boolean>} keywords parsed keywords
* @param {Object=} cmp optional second member object to scan
*
* @return {undefined}
*/
exports.buildProp = function( members, meta, name, value, keywords )
exports.buildProp = function( members, meta, name, value, keywords, cmp )
{
// disallow overriding methods with properties
if ( scanMembers( members, name ) instanceof Function )
if ( scanMembers( members, name, cmp ) instanceof Function )
{
throw new TypeError(
"Cannot override method '" + name + "' with property"
@ -226,11 +229,12 @@ function getMemberVisibility( members, keywords )
*
* @param {{public: Object, protected: Object, private: Object}} members
*
* @param {string} name member to locate
* @param {string} name member to locate
* @param {Object=} cmp optional second member object to scan
*
* @return {*} member, if located, otherwise undefined
*/
function scanMembers( members, name )
function scanMembers( members, name, cmp )
{
var i = visibility.length,
member = null;
@ -244,6 +248,14 @@ function scanMembers( members, name )
}
}
// if a second comparison object was given, try again using it instead of
// the original members object
if ( cmp !== undefined )
{
return scanMembers( cmp, name );
}
// nothing was found
return undefined;
}

View File

@ -63,7 +63,7 @@ for ( var prop in sub_props )
{
assert.equal(
sub_props[ prop ],
SubFoo.prototype[ prop ],
SubFoo()[ prop ],
"Subtype contains its own properties: " + prop
);
}
@ -139,7 +139,7 @@ assert.equal(
);
assert.notEqual(
SubOther.prototype.newFoo,
SubOther().newFoo,
undefined,
"Subtype should contain extended members"
);

View File

@ -0,0 +1,65 @@
/**
* Tests class member visibility (public, private, protected)
*
* Copyright (C) 2010 Mike Gerwitz
*
* This file is part of ease.js.
*
* ease.js is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Mike Gerwitz
* @package test
*/
var common = require( './common' ),
assert = require( 'assert' ),
Class = common.require( 'class' ),
pub = 'foo',
prot = 'bar',
priv = 'baz',
// new anonymous class instance
foo = Class.extend( {
'public pub': pub,
'protected peeps': prot,
'private parts': priv,
})();
( function testPublicMembersAreAccessbileExternally()
{
assert.equal(
foo.pub,
pub,
"Public properties are accessible via public interface"
);
} )();
( function testProtectedAndPrivateMembersAreNotAccessibleExternally()
{
assert.equal(
foo.peeps,
undefined,
"Protected properties are inaccessible via public interface"
);
assert.equal(
foo.parts,
undefined,
"Private properties are inaccessible via public interface"
);
} )();