7

I try to load some external .js files, and have some irresolvable namespace conflicts.

I had the idea of loading some of the files in their own context somehow, replacing the "this" from pointing at the window object to some custom namespace.

example:

first.js:

name = "first";

second.js:

name = "second";

It seems to me that this kind of trick can be very useful. Is it possible at all?

EDIT
seems that replacing "this" does not begin to solve the problem, as it is not the default context for identifier resolution in javascript. this is my test code:

var first = {};
var second = {};

(function(){name = "first";}).call(first);
(function(){name = "second";}).call(second);


document.write('name= '+name+' <br/>\n'); //prints "second"
document.write('first.name= '+first.name+' <br/>\n'); //prints "undefined"
document.write('second.name= '+second.name+' <br/>\n'); //prints "undefined

any ideas?

RESOLUTION
It is not possible. I ended up wiser than I was this morning, and I gave it up. I recommend these enlightening reading materials for anyone with a similar problem that might want to take a crack at it: http://jibbering.com/faq/notes/closures/
http://softwareas.com/cross-domain-communication-with-iframes

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Amir Arad
  • 6,724
  • 9
  • 42
  • 49

6 Answers6

3

I'm not clear on your reason for doing this; what are you using this for, exactly?

Wrapping the contents of your second.js in an anonymous function will prevent variables in that file from conflicting with global variables. If you really must have a this set to a particular object that isn't the global object, you could do something like

var differentThis = {};
(function() {
    // Contents of second.js go here
}).call(differentThis);

UPDATE

You can't do what you want. You seem to want to access the Variable object, which is the object to which a property is added when you declare a variable in JavaScript. In global code, the Variable object is the global object, so you can access it; within a function this is a property of the execution context that there is no way to access directly.

Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • I already tried that, and although the "this" keyword is properly replace, I found out that it is not the default context for identifier resolution. – Amir Arad Nov 08 '10 at 11:35
3

One idea I've had for doing it without needing modifications to your external JavaScript file is getting the contents of the JavaScript file in an AJAXy way (up to you how you do that) and then put it all in a function using the new Function(code) way, then initialise that with new:

surrogateWindow = new new Function(jsCode)();

Then surrogateWindow is the this of that code. I think that that idea should work.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • I know its been 9 years but this just saved my life. This works perfectly: var xhttp=new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { surrogate = new new Function(this.responseText)(); } }; xhttp.open("get", url, true); xhttp.send(); Once the async call is done I can reference "surrogate." and my function names and no more clashes. – Andrew Harris Apr 04 '19 at 05:50
  • Coming back to this almost ten years later, I say that on a certain style of code this will work—if it explicitly says `this.foo` instead of just `foo` or `window.foo`. But if not, then it won’t. Not sure why I didn’t specify this when I wrote the answer: I knew it at that time, without a doubt. I’m contemplating whether I neglected to consider it, having recently been fed too much Java. No idea. I don’t care to try to interpret the actions of me of ten years ago. – Chris Morgan Apr 07 '19 at 13:41
  • Always fun looking back on old code :) In my particular case I am loading a 3rd party libraries which uses a very old version of another 3rd party library that I use throughout my code, only I use a newer version. I've changed my code to do this to load the newer version into a surrogate and viola ... Personally I think this is a very neat and novel solution to a problem that comes up way to often. – Andrew Harris Apr 08 '19 at 22:43
2

Even though this is an old question, this answer may still be relevant for some:

When a js file is loaded it automatically gets the window's context. That is not possible to change. However, if you are trying to avoid conflicts between libraries that you are loading, and you don't have control over those libs, and they don't have a built-in "no-conflict" mechanism, then there is a nice trick - you can load those into a source-less iframe. This will make their context to be the window of the iframe, and you will still be able to access the iframe since there is no cross-domain issue here.

You can see this library as an example for use of this technique.

danbars
  • 300
  • 1
  • 3
  • 14
  • Ironically, the original problem was raised when I wanted to manage several JQuery versions :). I did end up doing some IFrame thing if I recall corecctly. – Amir Arad Jun 30 '14 at 16:47
1

You can load your file in an iframe, the file is not a .js but an HTML file, like:

<html>
<body>
  <script>
    var $ = parent.$, // you can share objects with the parent, eg: jQuery
      localObject = { // your local object definition
        name: 'first',
        showName: function(){
          $('div.name').html( this.name );
        }
      };
    //assign the local object to the custom namespace
    parent.customNamespace.object1 = localObject; 
  </script>
</body>
</html>

The trick is to use parent. to get the javascript objects available in the parent page.

Mic
  • 24,812
  • 9
  • 57
  • 70
  • This seems like a dirty but doable solution, but i can't use it for reasons I didn't specify in the original question - my code comes from outside the document's domain and so my IFrame will not be able to access the parent's internal state. – Amir Arad Nov 08 '10 at 11:26
  • I use it heavily in our app to cleanly separate the 3 codes: HTML, CSS and JS. I found it a not so dirty way of having includes browser side.As you are cross domains, I think the only way is make your server fetch the js files, transform and cache them, wrap them inside a function passing the this as context, and send them to the browser eg: `http://yourdomain.com/fetchjs?url=http://otherdomain.com/other.js&namespace=custom.object1` good luck. – Mic Nov 08 '10 at 13:37
  • Sadly, server side is also not an option. When I wrote "dirty but doable" I was thinking of the priorities I have to answer to - where the extra page load is considered very costly. I agree that code-and-order-wise this is the solution easiest to implement and maintain. Thanks. – Amir Arad Nov 08 '10 at 15:09
1

For the code you've written, I think you're misunderstanding some of the way classes work in JavaScript. In Java you can drop the this., but in JavaScript you can't. You'll always need to have this. there. So then your code becomes:

var first = {};
var second = {};

(function(){this.name = "first";}).call(first);
(function(){this.name = "second";}).call(second);


document.write('name= '+name+' <br/>\n'); //prints "undefined"
document.write('first.name= '+first.name+' <br/>\n'); //prints "first"
document.write('second.name= '+second.name+' <br/>\n'); //prints "second"

It would also be good to do it in a more normal class way. I'm not sure exactly what your situation is as I can't see all your code so you might be already doing it this way.

function Something(name) {
    this.name = name;
}

var first = new Something("first");
var second = new Something("second");

document.write('name= '+name+' <br/>\n'); //prints "undefined"
document.write('first.name= '+first.name+' <br/>\n'); //prints "first"
document.write('second.name= '+second.name+' <br/>\n'); //prints "second"
Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
-1

Well you could wrap the contents of the js files with something like this:

var externalInterfaceForYourObject = (function(){
  //code that defines your object

  //this should refer to the current anonymous function, and not the window object
  return this;
})();
Ramuns Usovs
  • 1,474
  • 11
  • 10