Is it possible to somehow pass the scope of a function to another?
For example,
function a(){
var x = 5;
var obj = {..};
b(<my-scope>);
}
function b(){
//access x or obj....
}
I would rather access the variables directly, i.e., not using anything like this.a or this.obj, but just use x or obj directly.
The only way to truly get access to function a's private scope is to declare b inside of a so it forms a closure that allows implicit access to a's variables.
Here are some options for you.
Direct Access
Declare b inside of a.
function a() {
var x = 5,
obj = {};
function b(){
// access x or obj...
}
b();
}
a();
If you don't want b inside of a, then you could have them both inside a larger container scope:
function container() {
var x, obj;
function a(){
x = 5;
obj = {..};
b();
}
function b(){
// access x or obj...
}
}
container.a();
These are the only ways you're going to be able to use a's variables directly in b without some extra code to move things around. If you are content with a little bit of "help" and/or indirection, here are a few more ideas.
Indirect Access
You can just pass the variables as parameters, but won't have write access except to properties of objects:
function a() {
var x = 5,
obj = {};
b(x, obj);
}
function b(x, obj){
// access x or obj...
// changing x here won't change x in a, but you can modify properties of obj
}
a();
As a variation on this you could get write access by passing updated values back to a like so:
// in a:
var ret = b(x, obj);
x = ret.x;
obj = ret.obj;
// in b:
return {x : x, obj : obj};
You could pass b an object with getters and setters that can access a's private variables:
function a(){
var x = 5,
obj = {..},
translator = {
getX : function() {return x;},
setX : function(value) {x = value;},
getObj : function() {return obj;},
setObj : function(value) {obj = value;}
};
b(translator);
}
function b(t){
var x = t.getX(),
obj = t.getObj();
// use x or obj...
t.setX(x);
t.setObj(obj);
// or you can just directly modify obj's properties:
obj.key = value;
}
a();
The getters and setters could be public, assigned to the this object of a, but this way they are only accessible if explicitly given out from within a.
And you could put your variables in an object and pass the object around:
function a(){
var v = {
x : 5,
obj : {}
};
b(v);
}
function b(v){
// access v.x or v.obj...
// or set new local x and obj variables to these and use them.
}
a();
As a variation you can construct the object at call time instead:
function a(){
var x = 5,
obj = {};
b({x : x, obj: obj});
}
function b(v){
// access v.x or v.obj...
// or set new local x and obj variables to these and use them.
}
a();
Scope is created by functions, and a scope stays with a function, so the closest thing to what you're asking will be to pass a function out of a() to b(), and that function will continue to have access to the scoped variables from a().
function a(){
var x = 5;
var obj = {..};
b(function() { /* this can access var x and var obj */ });
}
function b( fn ){
fn(); // the function passed still has access to the variables from a()
}
While b() doesn't have direct access to the variables that the function passed does, data types where a reference is passed, like an Object, can be accessed if the function passed returns that object.
function a(){
var x = 5;
var obj = {..};
b(function() { x++; return obj; });
}
function b( fn ){
var obj = fn();
obj.some_prop = 'some value'; // This new property will be updated in the
// same obj referenced in a()
}
what about using bind
function funcA(param) {
var bscoped = funcB.bind(this);
bscoped(param1,param2...)
}
No.
You're accessing the local scope object. The [[Context]].
You cannot publicly access it.
Now since it's node.js you should be able to write a C++ plugin that gives you access to the [[Context]] object. I highly recommend against this as it brings proprietary extensions to the JavaScript language.
You can't "pass the scope"... not that I know of.
You can pass the object that the function is referring to by using apply or call and send the current object (this) as the first parameter instead of just calling the function:
function b(){
alert(this.x);
}
function a(){
this.x = 2;
b.call(this);
}
The only way for a function to access a certain scope is to be declared in that scope.
Kind'a tricky.
That would lead to something like :
function a(){
var x = 1;
function b(){
alert(x);
}
}
But that would kind of defeat the purpose.
As others have said, you cannot pass scope like that. You can however scope variables properly using self executing anonymous functions (or immediately executing if you're pedantic):
(function(){
var x = 5;
var obj = {x:x};
module.a = function(){
module.b();
};
module.b = function(){
alert(obj.x);
};
}());
a();
I think the simplest thing you can do is pass variables from one scope to a function outside that scope. If you pass by reference (like Objects), b has 'access' to it (see obj.someprop in the following):
function a(){
var x = 5;
var obj = {someprop : 1};
b(x, obj);
alert(x); => 5
alert(obj.someprop); //=> 'otherval'
}
function b(aa,obj){
x += 1; //won't affect x in function a, because x is passed by value
obj.someprop = 'otherval'; //change obj in function a, is passed by reference
}
You can really only do this with eval. The following will give function b function a's scope
function a(){
var x = 5;
var obj = {x};
eval('('+b.toString()+'())');
}
function b(){
//access x or obj....
console.log(x);
}
a(); //5
function a(){
this.x = 5;
this.obj = {..};
var self = this;
b(self);
}
function b(scope){
//access x or obj....
}
function a(){
var x = 5;
var obj = {..};
var b = function()
{
document.println(x);
}
b.call();
}
Have you tried something like this:
function a(){
var x = 5;
var obj = {..};
b(this);
}
function b(fnA){
//access x or obj....
fnA.obj = 6;
}
If you can stand function B as a method function A then do this:
function a(){
var x = 5;
var obj = {..};
b(this);
this.b = function (){
// "this" keyword is still === function a
}
}
Related
I am looking for a fancy way to prevent a closure from inheriting surrounding scrope. For example:
let foo = function(t){
let x = 'y';
t.bar = function(){
console.log(x); // => 'y'
});
};
there are only two ways I know of preventing sharing scope:
(1) Use shadow variables:
let foo = function(t){
let x = 'y';
t.bar = function(x){
console.log(x); // => '?'
});
};
(2) Put the function body somewhere else:
let foo = function(t){
let x = 'y';
t.bar = createBar();
};
My question is - does anyone know of a 3rd way to prevent closures from inheriting scope in JS? Something fancy is fine.
The only thing that I think could possibly work is vm.runInThisContext() in Node.js.
Let's use our imaginations for a second, and imagine JS had a private keyword, which meant the variable was private only to that function's scope, like this:
let foo = function(t){
private let x = 'y'; // "private" means inaccessible to enclosed functions
t.bar = function(){
console.log(x); // => undefined
});
};
and IIFE won't work:
let foo = function(t){
(function() {
let x = 'y';
}());
console.log(x); // undefined (or error will be thrown)
// I want x defined here
t.bar = function(){
// but I do not want x defined here
console.log(x);
}
return t;
};
You can use block scope
let foo = function(t) {
{
// `x` is only defined as `"y"` here
let x = "y";
}
{
t.bar = function(x) {
console.log(x); // `undefined` or `x` passed as parameter
};
}
};
const o = {};
foo(o);
o.bar();
This technique works:
Create helper function to run a function in an isolated scope
const foo = 3;
it.cb(isolated(h => {
console.log(foo); // this will throw "ReferenceError: foo is not defined"
h.ctn();
}));
you might also have some luck with the JavaScript with operator
Given the following:
include.js
module.exports = function() {
...
return {
func: function(val) {
return Function('return ' + val + ';');
}
}
}()
running.js
var outer = function() {
var include = require('./include.js');
var x = include.func('eq');
console.log(x(5, 5));
}
outer()
...where would I put function eq(x, y){ return x === y; } such that this would work? I'm currently getting an eval at <anonymous> on the line that calls the function; x(5,5) in this case.
It doesn't like when eq is in include.js or when it's in running.js ~ I know this is example code is taken from my project and made pretty ambiguous...but, if it's possible, where would that function go?
OR
...would it be better to define an object of functions where the keys are the name of the function?
defaultFuncs = {
'eq': function(x, y){ return x === y; }
}
The parent scope of functions created via new Function is the global scope, not any local or module scope. So
global.eq = function(a,b) { return a==b };
function func(name) { return Function("return "+name+";"); }
var x = func("eq");
var equals = x();
equals(5, 5) // true
should work.
...would it be better to define an object of functions where the keys are the name of the function?
Definitely yes.
Is it possible to have a self executing function which is an objects property value assign values to other properties in the object?
e.g. - what I would like to do is this:
var b={
c:'hi',
d:null,
e:new function(){this.d=5}
};
But the "this" inside the new function seems to refer to b.e. Is it possible to access the b.e parent (i.e. b) from inside the function?
This is how you do it.
Often called the module pattern (more info)
var b = function () {
var c = 'hi';
var d = null;
return {
c : c,
d : d,
e : function () {
// this function can access the var d in the closure.
d = 5;
}
}
}();
You can access the value within the function, you just need to get rid of the new, i.e.
e: function () {
this.d = 5;
}
var f = function() {
this.m = '10' ;
f1 = function(){
alert(m)
}
}
o = new f()
o.m
f1()
Is this the right way to call nested function f1 from the above example
I am assuming you wanted f1 to be a method of f, in which case you need to add it as a property (as you've done for m):
var f = function() {
this.m = '10';
this.f1 = function(){
alert(this.m); //Notice that this has changed to `this.m`
};
}; //Function expressions should be terminated with a semicolon
You can then call the method on an instance of f:
o = new f();
o.f1(); //Alerts '10'
Here's a working example.
The way you have it currently will result in f1 leaking into the global scope (since it's declared without the var statement).
Side note: it's usually preferrable to make your methods properties of the prototype. This results in a single copy of the function in memory, instead of a copy for each instance:
var f = function() {
this.m = '10';
};
f.prototype.f1 = function() {
alert(this.m);
};
With your code, the function is an inner function and not callable from the outside. If you call it inside a constructor, you have to assign f1 to the object:
this.f1 = function() {
alert(m);
}
Then you can call:
o = new f()
o.f1() //=> alerts 10
Is there a way to change the root object in JavaScript?
For example, in browsers, the root object is "window". So
X = 5;
console.log(Y);
is the same as:
window.X = 5;
console.log(window.Y);
What I want to do now, is changing this root object, so when I do the following:
X = 6;
Reason why I need this:
In Node.js applications, every part of the program can access the global object. That's a big problem because every script that is executed by a Node.js webserver can add new variables to it. They will be there until the webserver is restarted. I want to avoid this by changing the global object.
Update
I've tested the following code and got a really interesting result.
What did you expect of the following code?
var X = {A:"a",B:"b"};
with(X){
A = 5;
C = 7;
}
for(a in X){
console.log(a+" is "+X[a]);
}
/*
Expected Console Output:
A is 5
B is b
C is 7
Real Console Output:
A is 5;
B is b;
*/
Is there a way to get output as I expected it?
Update
I've now tested the module system with the following code.
//program.js
var t = require("./module.js");
t.Test();
console.log(A);
//module.js
A = 5;
exports.Test = function(){
console.log("hello world!");
}
The output was:
hello world!
5
This tells me, that the variable "A" defined in module.js was added to the global object of program.js. The module does not solve my problem, either.
There is the with statement, but it is not recommended and forbidden in strict mode.
It is better to refer to the variable holding the object explicitly.
In response to updated question:
with will search up the scope chain until it finds an object with a matching property or gets to window. It is no good for defining new properties on an object.
var X = { A: 5, B: 8, C: 7};
with(X){
console.log(A, B, C);
}
If you're talking about variables, JavasScript has function scope.
X = 5; // global variable
console.log( window.X ); // 5
(function() {
var X = 6; // declare a local variable by using the "var" keyword
console.log( X ); // 6
})();
console.log( window.X ); // 5
Otherwise, you can create an Object, and add properties to it.
X = 5;
console.log( window.X ); // 5
var obj = {};
obj.X = 6;
console.log( obj.X ); // 6
console.log( window.X ); // 5
EDIT: Adding another possible solution that could be used.
You could invoke an anonymous function, but set the context of the function to your X object. Then this in the function will refer to X.
var X = {};
(function(){
this.A = 5;
this.B = 8;
this.C = 7;
}).call(X);
for(a in X){
console.log(a+" is "+X[a]);
}
The .call() method (as well as the .apply() method) allow you to explicitly set the thisArgof a calling context. The first argument you pass will be howthis` is defined in the context of the invocation.
Or just pass X in as an argument.
var X = {};
(function(X){
X.A = 5;
X.B = 8;
X.C = 7;
})(X);
for(a in X){
console.log(a+" is "+X[a]);
}
Though the simplest is to simply reference it (as I noted in my answer above).
var X = {};
X.A = 5;
X.B = 8;
X.C = 7;
for(a in X){
console.log(a+" is "+X[a]);
}
or use a module pattern:
/****** I'm guessing at the use of "global" here ********/
global.myNamespace = (function(global,undefined) {
// define the object to be returned
var X = {};
// define private local variables
var a_local = 'some value';
var another_local = 'some other value';
// define private functions
function myFunc() {
// do something with local variables
}
// give the return object public members
X.someProperty = 'some value';
X.anotherProperty = 'another value';
X.publicFunc = function() {
//do something with the local variables
// or public properties
};
X.anotherFunc = function() {
//do something with the local variables
// or public properties
};
// return the object
return X;
})(global);
console.log(myNamespace);
I found a way in EcmaScript 6 to adjust with(context) { ... }, so that any new variables we assign will go into the context object, not the global / window object.
Thanks to this article Metaprogramming in ES6: Part 3 - Proxies for teaching me about the ES6 Proxy feature.
In the proxy:
We override has to return true, so our context appears to have all properties, and when we set any variable it will go to the object.
We override get to get the property from our context, or if it isn't really there, we get the property from an up object (which defaults to the global window).
I know that with is frowned upon, but this technique enables to create mutable extensible modules where we can access members conveniently as foo rather than Module.foo, and I don't think it is unsafe or ambiguous.
function scope(x, up=window) {
return new Proxy(x, {
has: (o, k) => true,
get: (o, k) => k in o ? o[k] : up[k]
});
}
var Test = {};
with (scope(Test)) {
x = 1;
y = 2;
add_x = function(y) {
console.log(x + y);
}
}
Test.add_x(10);
with (scope(Test)) {
x = 3;
add_y = function(x) {
console.log(x + y);
}
}
Test.add_x(20);
Test.y = 5;
Test.add_y(30);