Native prototype extension from a module?

97 views
Skip to first unread message

Thomas Aylott / subtleGradient

unread,
Jan 4, 2010, 12:09:31 AM1/4/10
to CommonJS
I'm new to this list and I was unable to find anything in the list
archive or wiki that answered my question. If this has been handled
already I'll be happy with a RTSW (read the stinkin' wiki) + link.
Please forgive my pathetic google-fu. etc…
</ml-n00b-boilerplate>

Is it possible to extend native prototypes from within a module?
e.g.

// foo.js
String.prototype.foo = function(){ return "foo'd!"; };

// myapp.js
require('foo');
"bar".foo() // No fail


If so, is it likely to still be possible next week and 6 months from
now?
If not, how are you supposed to extend native prototypes in CommonJS?

Daniel Friesen

unread,
Jan 4, 2010, 8:54:57 AM1/4/10
to comm...@googlegroups.com
You're not supposed to. Globals in a module are not guaranteed to be
global, they could be frozen or only be a module specific copy.
Wes proposed a pattern:
require('some-prototyping-module').prototypeString(String);
"bar".foo();
((Actually it wasn't "prototypeString", I can't remember the method name
pattern he proposed.))

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Thomas Aylott / subtleGradient wrote:
> I'm new to this list and I was unable to find anything in the list
> archive or wiki that answered my question. If this has been handled
> already I'll be happy with a RTSW (read the stinkin' wiki) + link.

> Please forgive my pathetic google-fu. etc�


> </ml-n00b-boilerplate>
>
> Is it possible to extend native prototypes from within a module?
> e.g.
>
> // foo.js
> String.prototype.foo = function(){ return "foo'd!"; };
>
> // myapp.js
> require('foo');
> "bar".foo() // No fail
>
>
> If so, is it likely to still be possible next week and 6 months from
> now?
> If not, how are you supposed to extend native prototypes in CommonJS?
>

> --
>
> You received this message because you are subscribed to the Google Groups "CommonJS" group.
> To post to this group, send email to comm...@googlegroups.com.
> To unsubscribe from this group, send email to commonjs+u...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/commonjs?hl=en.
>
>
>

Thomas Aylott

unread,
Jan 4, 2010, 9:41:05 AM1/4/10
to comm...@googlegroups.com
That brings up another interesting question about modules, globals and closures.
If you have `String` in a closure in your module then it's possible that it will be a different String than the one in your main script that requires the module?
If each module gets its own environment then you could extent prototypes in your module for use in your module without effecting the rest of the universe.
That could be very awesome, yet possibly very confusing.

So, if `this == undefined` in a module, could something like the following work?

// footools.js
function FooTools(){
    this.String.prototype.foo = function(){ return "foo'd"; };
};
exports.setup = function(context){
FooTools.apply(context);
}
if (typeof this != 'undefined') exports.setup(this);

// app.js
require('footools').setup(this);
"bar".foo(); // no Fail

// web.html
<script src="mod.js"></script>
<script>"bar".foo(); /*no Fail*/</script>

http://gist.github.com/268543 for more along these lines.

Something similar to that could work in both a module or the web.
Or am I still missing something?

— Thomas Aylott
   SubtleGradient

On Mon, Jan 4, 2010 at 8:54 AM, Daniel Friesen <nadir.s...@gmail.com> wrote:
You're not supposed to. Globals in a module are not guaranteed to be
global, they could be frozen or only be a module specific copy.
Wes proposed a pattern:
require('some-prototyping-module').prototypeString(String);
"bar".foo();
((Actually it wasn't "prototypeString", I can't remember the method name
pattern he proposed.))

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Thomas Aylott / subtleGradient wrote:
> I'm new to this list and I was unable to find anything in the list
> archive or wiki that answered my question. If this has been handled
> already I'll be happy with a RTSW (read the stinkin' wiki) + link.
> Please forgive my pathetic google-fu. etc…

Wes Garland

unread,
Jan 4, 2010, 1:36:46 PM1/4/10
to comm...@googlegroups.com
1 - extending standard classes in modules is not generally a great idea, but is supported *in* CommonJS (although not necessarily *for* CommonJS)

2 - I have previously proposed that the module-user explicitly patch standard classes with a patch method on module exports which takes the standard class as an argument

3 - I have previously proposed an alternative for CommonJS extensions of standard classes which involves late-binding module require standards (google monkey-thunk -- coined word for thunking-in monkey-patches)

4 - Your question re. multiple instances of standard classes -- solved by 2 above. But each module does not get its own copy of the standard classes.  Sandboxes may.

5 - FWIW -- this == undefined is a terrible test.  undefined can be redefined. Use typeof.

6 - if you want to extend native prototypes in module non-conflicting ways, may I suggest namespacing your extensions instead? 

String.prototype.myModule = { method1: closure1, method2: closure2 };

Wes

--
Wesley W. Garland
Director, Product Development
PageMail, Inc.
+1 613 542 2787 x 102

Daniel Friesen

unread,
Jan 4, 2010, 3:36:03 PM1/4/10
to comm...@googlegroups.com
Wes Garland wrote:
> 5 - FWIW -- this == undefined is a terrible test. undefined can be
> redefined. Use typeof.
I don't think he should be making that test at all.

A) We never defined what `this` would be inside of a module, that's an
implementation detail, and heck it's perfectly valid behavior for a
CommonJS implementation to make `this === module`.
B) In ES5 strict mode `this` will always be undefined.
C) If it does manage to work, you end up with unexpected behavior, and
running that method twice for code that is written properly.

Side note, but he got it wrong anyways. You already noted to me those
arguments should be the standard classes, NOT a global. The global
object does not need to be accessible to the user as a variable.


> Wes
>
> --
> Wesley W. Garland
> Director, Product Development
> PageMail, Inc.
> +1 613 542 2787 x 102

Thomas Aylott

unread,
Jan 4, 2010, 4:37:40 PM1/4/10
to comm...@googlegroups.com
if (typeof this != 'undefined') exports.setup(this);

Ok, avoid `this`. Got it.

How is this version then?

It's horrifyingly verbose, but I guess that could be seen as a good thing for trying to find out what's doing what in a script.

Obviously this isn't final code. I'm sure a final solution would be much less ugly.

— Thomas Aylott
   SubtleGradient

Wes Garland

unread,
Jan 4, 2010, 5:26:29 PM1/4/10
to comm...@googlegroups.com
Daniel:

Thanks for catching the problem with 'this', can't believe that slipped by me. :)

Thomas:


On Mon, Jan 4, 2010 at 4:37 PM, Thomas Aylott <obli...@subtlegradient.com> wrote:
How is this version then?

Your setup pattern looks like the patch patter I proposed awhile back.  I think it's a fine solution.

I'm not sure what
if (typeof exports == 'undefined') var exports = {};

is intended for. I guess running in a non-CommonJS environment?

Wes
 

Thomas Aylott

unread,
Jan 4, 2010, 6:09:07 PM1/4/10
to comm...@googlegroups.com
Right.

In http://gist.github.com/268543#file_web_app.html you can see how I would use it from an html file in a browser.

http://gist.github.com/268543#file_commonjs_app.js is how I would embed from the main app in CommonJS and 

http://gist.github.com/268543#file_commonjs_mod.js is how I would embed from another module.

Can anyone think of a better way to do this?
I'm not too fond of all this verbosity, but I guess it's not that bad.
I'll update this thread if I come up with anything better that avoids as much "undefined" behavior as possible.

Also, can anyone think of anywhere where this might not work?

thanks.

— Thomas Aylott
   SubtleGradient
   MooTools
Reply all
Reply to author
Forward
0 new messages