FYI: Priveleged method gotcha

312 views
Skip to first unread message

George Jempty

unread,
Aug 13, 2003, 5:54:37 PM8/13/03
to
OK, wanted to make a more positive contribution to start than just
plonking somebody. Here's something I've discovered using "priveleged
methods" a la Douglas Crockford
(http://crockford.com/javascript/private.html):

If you want a constructor to utilize priveleged methods the methods must
be defined in the constructor ABOVE where they are called. Consider the
following:

function ExaminableField(element, examCriteria)
{
var formElement = element;
var name = element.name;
var required = true;

//must be defined before used privately
this.isRequired = function()
{
if (arguments.length == 1)
{
required = arguments[0]
}
return required;
}

if (!!examCriteria)
{
this.isRequired(examCriteria.required);
}

}

It had been causing an error when I had my if block above the definition
of this.isRequired. Thought this might help somebody, and/or contribute
to a proper convention.


Douglas Crockford

unread,
Aug 13, 2003, 6:23:03 PM8/13/03
to
> OK, wanted to make a more positive contribution to start than just
> plonking somebody. Here's something I've discovered using "priveleged
> methods" a la Douglas Crockford
> (http://crockford.com/javascript/private.html):
>
> If you want a constructor to utilize priveleged methods the methods must
> be defined in the constructor ABOVE where they are called.

It is generally a good thing to define things before using them. This is
particularly true in a dynamic language like JavaScript, where the WHEN of a
definition is significant. The critical thing is BEFORE. Often that means ABOVE,
but not always.

> if (!!examCriteria)

!! converts its operand to a boolean. If it was falsy, it produces false,
otherwise it produces true. But in an if, falsy values are ok, so the !! is
wasted. It is better to write

if (examCriteria)

Richard Cornford

unread,
Aug 13, 2003, 11:02:11 PM8/13/03
to
"George Jempty" <che...@highstream.net> wrote in message
news:Oiy_a.782$Q5....@fe01.atl2.webusenet.com...
<snip>

> Here's something I've discovered using "priveleged
> methods" a la Douglas Crockford
> (http://crockford.com/javascript/private.html):
>
>If you want a constructor to utilize priveleged methods the
>methods must be defined in the constructor ABOVE where they
>are called. Consider the following:
>
> function ExaminableField(element, examCriteria)
> {
> var formElement = element;
> var name = element.name;
> var required = true;
>
> //must be defined before used privately
> this.isRequired = function()
> {
> if (arguments.length == 1)
> {
> required = arguments[0]
> }
> return required;
> }
>
> if (!!examCriteria)

This double NOT (- !! -) is a nice construct as it produces a boolean
that represents the type-converted true-ness of its right-most operand.
Useful to flag the existence of a browser feature to avoid having to
have it type-converted for multiple subsequent tests:-

var useGetById = !!document.getElementById;

( compared with clunkier looking alternatives like:-
var useGetById = (document.getelementById)?true:false;
)

- but it is not needed here. The first - ! - will have to type-convert
its operand to a boolean value prior to inverting it, the second - ! -
will then invert that boolean and produce the value used to resolve
the - if - statement. However, if the identifier was used directly
within the if statement it would be type-converted to boolean to resolve
the - if - statement, so - if(examCriteria) - must always produce the
same results as - if(!!examCriteria) - but fractionally quicker.

> {
> this.isRequired(examCriteria.required);
> }
>
> }
>
>It had been causing an error when I had my if block above the
>definition of this.isRequired. Thought this might help
>somebody, and/or contribute to a proper convention.

Because the function assigned to - this.isRequired - is a function
expression it is evaluated inline as the constructor executes so there
would be no value assigned to - this.isRequired - prior to the execution
of the assignment.

In contrast, inner function definitions (private methods in class based
OO terminology) are evaluated during the creation of the "variable"
object as the script enters the execution context for the function
call[1]. So it should be possible to call such a function in code that
physically precedes its definition and also render that function object
privileged by assigning a reference to it to a public object member:-

function ExaminableField(element, examCriteria){
var formElement = element;
var name = element.name;
var required = true;

if(examCriteria){
_requed(examCriteria.required);
}

this.isRequired = _requed;

function _requed(){
if(arguments.length == 1){


required = arguments[0]
}
return required;
}
}

- Doing so seems unnecessary. I would just put up with having to write
the constructor in the required order if I wanted the method to be
privileged.

[1] The Mozilla/Gecko implementation seems to be a bit off spec (ECMA
262 3rd Edition) in this respect as it handles inner function
definitions contained within blocks, such as - if(x){ function doX(){
... } } - as if they were inline in some sense. It would be unusual to
be using inner function definitions in that way so there should be no
significant consequences.

Richard.


Andy Fish

unread,
Aug 14, 2003, 6:21:01 AM8/14/03
to
>
> This double NOT (- !! -) is a nice construct as it produces a boolean
> that represents the type-converted true-ness of its right-most operand.
> Useful to flag the existence of a browser feature to avoid having to
> have it type-converted for multiple subsequent tests:-
>
> var useGetById = !!document.getElementById;
>
> ( compared with clunkier looking alternatives like:-
> var useGetById = (document.getelementById)?true:false;
> )
>

I realise this is a bit OT but I find the double-not it very useful when
validating combinations of values. For instance, if the user can specify
either parameter a or parameter b but not both, we can write

if ( !!a != !!b) {
parameters are valid
}

obviously != transliterates into XOR. more elaborate validation rules are
easily expressed in this way when the longhand logic would be near
impossible to comprehend.

This also technique is also handy in C and java.

Andy


Richard Cornford

unread,
Aug 14, 2003, 8:17:53 AM8/14/03
to
"Andy Fish" <ajf...@blueyonder.co.uk> wrote in message
news:hoJ_a.1115$1l2.8...@news-text.cableinet.net...
<snip>
>... I find the double-not it very useful when

>validating combinations of values. For instance,
>if the user can specify either parameter a or
>parameter b but not both, we can write

> if ( !!a != !!b) {

<snip>

Consider:-
(best viewed with fixed width font)

a | b | a != b |
true | false | true |
false | true | true |
true | true | false |
false | false | false |

!a | !b | !a != !b |
false | true | true |
true | false | true |
false | false | false |
true | true | false |

!!a | !!b | !!a != !!b |
true | false | true |
false | true | true |
true | true | false |
false | false | false |

Obviously - "aString != "anotherString" - is true, while - !"aString" !=
!"anotherString" - is false as both strings will type-convert to boolean
true (and then be inverted to false by the - ! - operator). So at least
one type-conversion to boolean is needed to do the comparison when a and
b are not boolean to start with. But it looks like - !a != !b - will
always produce the same results as - !!a != !!b - as the result of -
!something - is always boolean.

Richard.


George Jempty

unread,
Aug 14, 2003, 11:41:11 AM8/14/03
to

I use !! because examCriteria, or any other variable for that matter,
might contain 0 or "", and I don't necessarily want these values to
evaluate as false.

George Jempty


George Jempty

unread,
Aug 14, 2003, 11:53:44 AM8/14/03
to
George Jempty wrote:
>
>
> I use !! because examCriteria, or any other variable for that matter,
> might contain 0 or "", and I don't necessarily want these values to
> evaluate as false.

Never mind....major brain fart


Lasse Reichstein Nielsen

unread,
Aug 14, 2003, 12:02:08 PM8/14/03
to
George Jempty <che...@highstream.net> writes:

> I use !! because examCriteria, or any other variable for that matter,
> might contain 0 or "", and I don't necessarily want these values to
> evaluate as false.

But they do.
!!0 === false
!!"" === false
!!null === false
!!undefined === false
!!NaN === false

The conversion performed by !! is exactly the same as the one
performed by the function Boolean or implicitly by a conditional (if,
while, do/while, ?:).

/L
--
Lasse Reichstein Nielsen - l...@hotpop.com
Art D'HTML: <URL:http://www.infimum.dk/HTML/randomArtSplit.html>
'Faith without judgement merely degrades the spirit divine.'

Yep

unread,
Aug 14, 2003, 1:37:25 PM8/14/03
to
"Richard Cornford" <Ric...@litotes.demon.co.uk> wrote in message news:<bheu3k$pkq$1$8300...@news.demon.co.uk>...

> [1] The Mozilla/Gecko implementation seems to be a bit off spec (ECMA
> 262 3rd Edition) in this respect as it handles inner function
> definitions contained within blocks, such as - if(x){ function doX(){
> ... } } - as if they were inline in some sense.

But they are! A function declaration is _illegal_ in a block
statement, therefore a function defined as you describe could be only
a function expression, not a function declaration, and should
therefore be processed as an expression. Guess which browser is a bit
off spec ;-)


Regards,
Yep.

Richard Cornford

unread,
Aug 14, 2003, 5:28:58 PM8/14/03
to
"Yep" <y-e....@em-lyon.com> wrote in message
news:d2d855ea.03081...@posting.google.com...

My understanding (and testing) of this subject is hindered by the fact
that it just does not make sense to put a function declaration in a
block, so it is not really important whether it is legal to do so or
not. I am however annoyed with myself for not pursuing the language
grammar when I was testing the extent to which browsers implement the
optimisation possibilities when crating function objects. Because you
are absolutely correct, the language grammar forbids a function
declaration form appearing in a block.

<quote>
ECMA Script 262 3rd Edition. Section 12.1 Block
Syntax
Block :
{ StatementListopt }

StatementList :
Statement
StatementList Statement
</quote>

A block is a statement and may _only_ contain statements.

<quote>
ECMA Script 262 3rd Edition. Section 14 Program
Syntax
Program :
SourceElements

SourceElements :
SourceElement
SourceElements SourceElement

SourceElement :
Statement
FunctionDeclaration
</quote>

A program consists of SourceElements and SourceElements are one of; a
statement or a function declaration. So a function declaration is not a
statement and as a result _cannot_ be placed within a block.

Which means that Opera and IE are incorrect in treating what appears to
be a function definition within a block as a function declaration, they
should either be treated as a function expression (with optional
identifier) or a syntax error (probably the former).

However, when testing the optimisation of the creation of function
objects the possibility that the block contained function code was being
treated as a function expression (with optional identifier) was
considered, but:-

<quote>
ECMA Script 262 3rd Edition. Section 13 Function Definition
Syntax
FunctionDeclaration :
function Identifier ( FormalParameterListopt ){FunctionBody }
FunctionExpression :
function Identifier opt ( FormalParameterListopt ){FunctionBody }
...

The production FunctionExpression :
function Identifier ( FormalParameterListopt ){
FunctionBody
} is evaluated as follows:

1. Create a new object as if by the expression new Object().
2. Add Result(1) to the front of the scope chain.
3. Create a new Function object as specified in 13.2 with
parameters specified by FormalParameterListopt and body
specified by FunctionBody. Pass in the scope chain of the
running execution context as the Scope.
4. Create a property in the object Result(1). The property's
name is Identifier, value is Result(3), and attributes
are { DontDelete, ReadOnly }.
5. Remove Result(1) from the front of the scope chain.
6. Return Result(3).
...
</quote>

When the function object is created by the evaluation of the function
expression the (optional) identifier is created as a property on a new
object added to the front of the current scope chain. That scope chain
is passed to the new function and the new Object is then removed from
the current scope chain. That means that the only scope that can resolve
the identifier for the function to a reference to the function object is
the scope within that function.

However:-

function funcTest(){
var x=[];
for (var i=0;i<2;i++){
function foo(){return arguments.callee;};
x[i] = foo;
x[i] = x[i]();
}
return (x[0]==x[1]);
}

alert(functionTest()) // alerts false on Mozilla (with no errors),
// true on IE and Opera.

- if Mozilla is treating - function foo(){return arguments.callee;}; -
as a function expression with optional identifier (which is the only
valid interpretation due to the grammar rules) then - foo - should not
be in the scope chain of the outer function when it executes - x[i] =
foo; - and foo should resolve to a (probably undefined) property of the
global object. Meaning - x[i] = x[i](); - should produce an error.

The accessibility of the inner function by its identifier gave the
impression that Mozilla was treating the inner function as a function
declaration rather than as an expression. it now seems unlikely that the
inner function is not being treated (correctly) as an expression but
that still leaves Mozilla a bit off spec on the scope chain ;-)

Richard.


Richard Cornford

unread,
Aug 15, 2003, 1:42:29 AM8/15/03
to
"Richard Cornford" <Ric...@litotes.demon.co.uk> wrote in message
news:bhguur$apj$1$8302...@news.demon.co.uk...
<snip>
> ... . it now seems unlikely that the
> inner function is not being treated (correctly) ...
<snip>

The - not - in "not being treated" should not have been their. The final
paragraph should have read:-

The accessibility of the inner function by its identifier gave the
impression that Mozilla was treating the inner function as a function

declaration rather than as an expression. It now seems unlikely that
the inner function is being treated (correctly) as an expression but

Yep

unread,
Aug 15, 2003, 5:41:26 AM8/15/03
to
"Richard Cornford" <Ric...@litotes.demon.co.uk> wrote in message news:<bhguur$apj$1$8302...@news.demon.co.uk>...

> When the function object is created by the evaluation of the function
> expression the (optional) identifier is created as a property on a new
> object added to the front of the current scope chain. That scope chain
> is passed to the new function and the new Object is then removed from
> the current scope chain. That means that the only scope that can resolve
> the identifier for the function to a reference to the function object is
> the scope within that function.

<snip excellent illustration />

I see what you mean, but ISTM that this is an ECMA inconsistency, and
as such I won't throw the stone to Mozilla guys for making such an
"exception" in not respecting the specs :-)

Check also the amusing
var foo = function bar(){ alert("Guess who I am");}
foo(); bar();
which is OK in IE but not in Mozilla and Opera for which "bar" is
undefined (now the behavior you're expecting is correct for Mozilla,
the identifier is really lost).

It's possible to check, however, that the identifier isn't really lost
and is indeed kept in the scope passed to the function. Check the
following in Mozilla:
var dw=function(s){document.write(s+"<br>")}
var foo = function bar(){
//foo.__parent__ is the local scope of foo
dw(foo.__parent__.bar) //now it's found!
dw(foo.__parent__.foo) //not found, foo is in global scope

//foo.__parent__.__parent__ is the global scope
dw(foo.__parent__.__parent__.foo); //found, OK
dw(foo.__parent__.__parent__.bar); //not found, "OK"
}
foo()


Cheers,
Yep.

Douglas Crockford

unread,
Aug 15, 2003, 8:53:16 AM8/15/03
to
> Check also the amusing
> var foo = function bar(){ alert("Guess who I am");}
> foo(); bar();
> which is OK in IE but not in Mozilla and Opera for which "bar" is
> undefined (now the behavior you're expecting is correct for Mozilla,
> the identifier is really lost).

And check this out in IE:

<html><head><script>
var foo = function bar(){ alert(bar.ok);}
foo.ok = 'correct';
foo();
</script></head></html>

IE puts the function name in the wrong scope. It should go in the inner scope,
but instead is put in the outer scope.

http://www.crockford.com/javascript/private.html

Yep

unread,
Aug 15, 2003, 1:46:08 PM8/15/03
to
"Douglas Crockford" <nos...@laserlink.net> wrote in message news:<bhil5o$na6$1...@sun-news.laserlink.net>...

> And check this out in IE:
>
> <html><head><script>
> var foo = function bar(){ alert(bar.ok);}
> foo.ok = 'correct';
> foo();
> </script></head></html>
>
> IE puts the function name in the wrong scope. It should go in the inner scope,
> but instead is put in the outer scope.

Actually IE seems to evaluate the "function bar" twice, the first time
as a function declaration, the second time as a function expression
(where it doesn't add the identifier to the inner scope chain - it
even does nothing with the identifier); as a result foo and bar aren't
even equal.

Add
bar.ok = "Hello?"
at the beginning of your script to have an illustration.


Regards,
Yep.

Reply all
Reply to author
Forward
0 new messages