Forcing the context

I have this class where I have a private property and a public access method:

Person = function () { this.Name = "asd"; var _public = new Object(); _public.Name = function (value) { if (value == undefined) { //Get return this.Name } else { this.Name = value; //Set } }; return _public; }; 

I want to force the context in _public.Name to access this.Name .

I know the closure method, but I want to see if I can force the context.

I found a technique for this, expanding the Function object:

 Function.prototype.setScope = function (scope) { var f = this; return function () { f().apply(scope); } } 

And my class will look like this:

 Person = function () { this.Name = "asd"; var _public = new Object(); _public.Name = function (value) { if (value == undefined) { return this.Name } else { this.Name = value; } }.setScope(this); return _public; }; 

So, I can set the context correctly, but I cannot pass value and cannot, however, return this.Name .

+4
source share
3 answers

Not

 f().apply(scope); 

only

 f.apply(scope); 

(No () after f .) You want to use the apply function for the f function, rather than calling the f function and access the apply by its return value.

To pass the arguments received by your function to setScope , add the following:

 f.apply(scope, arguments); 

arguments is an implicit argument for all functions, which is a pseudo-array of the actual arguments passed to the function at run time. apply takes any array-like thing as its second parameter to indicate the arguments used when invoking the base function.

I would also return the return value:

 return f.apply(scope, arguments); 

So, setScope becomes:

 Function.prototype.setScope = function (scope) { var f = this; return function () { return f.apply(scope, arguments); } } 

Living example

Note that the normal name of this function and the name it has in the new ECMAScript5 standard , bind (section 15.3. 4.5; ECMAScript5 bind also allows you to curry arguments that are not executed by this implementation). setScope is a particularly unfortunate name because it does not set the scope, it sets the context.

Having said all this, there is no reason why you need setScope in your Person constructor. You can simply do this:

 Person = function () { var self = this; this.Name = "asd"; var _public = new Object(); _public.Name = function (value) { if (value == undefined) { return self.Name; } else { self.Name = value; } }; return _public; }; 

Real time example

But using bind (aka setScope ) can be useful in places where you do not want a new close over the context in which you do it.


Off topic . The way you specify Person will break certain things that people can expect, for example:

 var p = new Person(); alert(p instanceof Person); // Expect "true", but in your case will be "false" 

... because you are replacing the new object created for you, but returning another object from your constructor (which overrides the default value).

Instead of creating a new object and returning it to your constructor, allow the object constructed for you by new as an object (and therefore the Person relation is supported), but you can still get really private variables and use accessors:

 function Person() { // Private variable var name = "asd"; // Accessor function this.Name = function(value) { if (typeof value === "undefined") { return name; } name = value; }; } 

Living example

As you can see, it is much simpler and it keeps instanceof communication. Please note that we do not qualify our references to name inside name at all, and therefore we use a local variable in a constructor call in which our name function was closed, which is closed above it.

I also took the liberty of giving the constructor function a name, because I am not a fan of anonymous functions . I should have indicated the accessory and name:

 function Person() { // Private variable var name = "asd"; // Accessor function this.Name = Person_Name; function Person_Name(value) { if (typeof value === "undefined") { return name; } name = value; } } 

Off-topic 2 : the overwhelming convention in JavaScript code is to use the initial caps for function names only for constructor functions (e.g. Person ), and not for other kinds of functions (e.g. name ). Of course, you can do whatever you want, but I thought I was mentioning the convention as it makes it easier for other people to read your code.


It is worth noting: All of these methods lead to the fact that each individual Person object has its own copy of the accessor function. If there are many such objects, this could be a memory problem. If only a little, then good.

+8
source

First, I think the right way to do this is to use the β€œclose” method, since the syntax is simpler and easier to understand and makes more sense, and most object-oriented code written in Javascript is written that way. One more note: in your method, the "private" element can be accessed from the outside by accessing Person.Name (instead of (new Person()).Name ).

At the same time, it seems that you want something like Prototype.JS bind method , which allows you to bind a reference to a function as a method to call a specific object, and also pass all arguments correctly (including resolving predefined arguments).

Look at the source of Prototype.JS for a full implementation, but a simple implementation of this semantics might look like this:

 Function.prototype.bind = function(context) { var callee = this; var args = Array.prototype.slice.call(arguments,1); return function() { var newargs = args.concat(Array.prototype.slice.call(arguments,0)); return callee.apply(context, newargs); }; }; 
+1
source

It’s hard to understand what you are trying to achieve. But if I assume that you are trying to create a Person class with a name method in order to get / set the person's name, here is my suggestion:

 function Person() { this._name = undefined; // not required but is better than assigning a fake name return this; } Person.prototype.name = function( _name ) { if ( _name === undefined ) return this._name; // get return this._name = _name; // set } 

Notice that I have defined the name function with the first letter in lowercase. This is standard practice in JavaScript, where only constructors are commonly used. To use this class, follow these steps:

 person = new Person(); person.name( "Ermes Enea Colella" ); alert( person.name ); // displays "Ermes Enea Colella" 

There is no need to associate any context with this method, so you can search for something else. If you can clarify your need, I will gladly edit my answer.

Hope this helps.

+1
source

All Articles