I tried to create this syntax for JavaScript:
var user = new Class({
name: 'No name',
sayHello: function () {
console.log(self.name);
}
});
user.sayHello(); // will print "undefined" because "self" isn't visible
And I created this implementation of Class:
function Class (_properties) {
var self = this;
// copy all properties and methods to this class
for (var _property_name in _properties) {
self[_property_name] = _properties[_property_name];
}
}
But the problem is in self that I wish to use in each class. The self.name will be undefined because self is a [Window object].
Question: how to modify my code to use self inside all functions in class instances? The self is needed to not drop context if the function will be called from external context.
Expected result (I mean that code above should be executed as code below):
function Class (_properties) {
var self = this;
// properties
self.name = 'No name';
// methods
self.sayHello = function sayHello () {
console.log(self.name);
};
}
var user = new Class();
user.sayHello();
The goal of my experiment is to use self that always is reference to current object. Because sometimes when we use calls like this $.get('...', function () { console.log(this); }) - this is set to function local scope. But I wish to use self (magic property) that always be a reference to object of current class (scope of current class).
So I finally figured out that you are talking about the self variable in the sayHello method that is giving you problems.
You need to google JavaScript scope this tutorial - it's not the most intuitive concept, but once you get it, everything will make sense.
In the mean time, it's worth pointing out that self is not a reserved word in JavaScript and doesn't do anything special, but this is.
So what you want, is something like this:
function Class (_properties) {
// copy all properties and methods to this class
for (var _property_name in _properties) {
this[_property_name] = _properties[_property_name];
}
}
var User = new Class({
name: 'No name',
sayHello: function () {
console.log(this.name);
}
});
var user1 = new User();
var user2 = new User();
The keyword new in front of the Class() function causes it to be executed as a constructor function meaning that, inside of the constructor function this will reference it's own scope.
Also, because the property sayHello is a function to be executed inside the scope that you created, you can use this.name - as this will refer to the scope that you created when you instantiated a new Class.
And just because this scope thing is no doubt confusing (don't worry, it probably will be for a while) - if you haven't created a scope (by using a constructor or object literal) you're working inside of the global scope which, in the case of the browser, is the Window object.
That's because you never created an object:
var options = {
name: 'No name',
sayHello: function () {
console.log(self.name);
}
}
var user1 = new Class(options);
var user2 = new Class(options);
"this" refers to the object owner of the current function. In this case would be windows object. At least you construct an object from the function Class.
If you want to avoid someone overwrite a property of Class.prototype is necessary to use hasOwnProperty in your for in
Related
please help solve the problem.
i make 3 objects:
level:
var Level = function() {
self = this;
this.cellsObserver = new CellsObserver();
console.log('co from level ' + this.cellsObserver);
this.fieldObj = new Field();
}
field:
var Field = function() {
self = this;
this.init();
};
Field.prototype = Object.create(Level.prototype);
Field.prototype = {
init: function() {
console.log('co from field ' + self.cellsObserver);
}
}
observer:
var CellsObserver = function(){
.............
}
in result console output follow:
co from level [object Object] co from field undefined
I do not understand why in the second case output 'undefined'. because I have appointed a parent:
Field.prototype = Object.create(Level.prototype);
tl;dr: there is no field cellsObserver neither in Field or Level.prototype object. And there is no such thing as a classical 'self' in Javascript.
The long story.
When you're trying to fetch object.property, the Javascript will look at the object trying to find property, then to the object.prototype (which is another object) and so on until it get to the null prototype in the Object.prototype reference.
So, when you're calling second time the this.cellsObserver in Level constructor function, it goes like this:
this is a just new constructed object (if it called with the new keyword), and there is cellsObserver in the property list, so it'll be fetched without any deep lookup.
Then,
Field.prototype = Object.create(Level.prototype);
This only means that Field.prototype now will refer to a new object, which properties are the same as in Level.prototype in that moment.
From your code, there are no non-standard properties in Level.prototype object (you didn't provide any).
Then,
self = this;
Here you just assigning a global variable called self a reference to the just created object or window object (it depends). If you wish to store a reference to the this object, you should var: var self = this. But you should remember that this self variable could be accessed only in the scope where is was declared or in a closure.
Then,
Field.prototype = {
init: function() {
console.log('co from field ' + self.cellsObserver);
}
}
First of all, here you just override the previos instruction (Field.prototype = Object.create(Level.prototype);). If you want to extend the Field.prototype object, you could do in the Object.create call in second argument, or just by accessing a property like that: Field.prototype.init = function(){...}.
Second. The self variable could contain anything when the init function will be executed. Just use this for the current object here.
Third. Let's try to guess what will happen when this.cellsObserver get evaluated inside this init function.
The this object is referring to the Field instance, and there is no cellsObserver property there, so we move to the Field.prototype object, which is defined above ({ init: function () {...}}), there is no cellsObserver either, so we move to the Object.prototype object, which is null. Ok, our lookup is failed, this.cellsObserver is undefined.
What would be it look like if the Field.prototype = Object.create(Level.prototype) wasn't been overridden by the following code? The this object will refer to the Field instance as early. The Field.prototype is referring to the object which is copy of the Level.prototype object, and there is no cellsObserver here either. So, we look to the Object.prototype, and that's it. There is no cellsObserver in any objects except in the instances on Level class, which isn't referenced in any way in the Field instances.
You can play with that here: http://www.objectplayground.com/ by pasting this code:
var Level = function() {
this.cellsObserver = new CellsObserver();
this.fieldObj = new Field();
}
var Field = function() {
this.init();
};
Field.prototype = Object.create(Level.prototype);
Field.prototype.init = function() {};
this.field = new Field();
You are overriding the Field.prototype. Just assign to Field.prototype.init.
I have a variable at some point of a JavaScript code. Now I would like to get the name of the function (aka scope) where that variable was declared. So for example if that variable is a field of an oject, I would like to get the name of the object's type.
Consider the following code:
function MyClass() {
this.name = "MyName";
this.age = 20;
}
var myVariable = new window.MyClass();
alert(getDeclaringScope(myVariable)) // should alert 'window'
alert(getDeclaringScope(myVariable.name)) // should alert 'MyClass
Is there any way to implement the getDeclaringScope function?
UPDATE
I wanted to use some technic like this to access to access a kind of "static" variable where meta information is stored for knockoutjs observable. A farly simplified example:
var META = {};
META["MyClass"] = {};
META["MyClass"]["MyArray"] = { ElementType: "MyOtherClass" };
function MyClass() {
this.MyArray = ko.observableArray();
}
function MyOtherClass() {
this.name = "a";
}
ko.observableArray.fn.addFromPlainObjects = function(plainItems) {
var elemType = .... here I wanted to get "MyOtherClass" from the META global variable
// create MyOtherClass and map the plain items to it... etc.
}
No.
The object has a reference to its constructor, but the constructor could be referenced from many objects, not just window in this case. It could be accessed directly with a variable (as opposed to a property):
var MyClass = window.MyClass;
var foo = new MyClass();
You can create a back-reference explicitly in your object model, as constructor functions are objects.
window.MyClass.backref = window;
Though this is most likely not what you want. I suspect you have a misunderstanding regarding what the scope of a variable is; a variable scope has nothing to do with object properties. As such, there is no notion of "declaring scope" that represents the object and object property from which a variable reference was retrieved, as you seem to conceptualize it.
You can use instanceof and constructor:
Eg.
myVariable instanceof MyClass; //true
myVariable.constructor;
// returns
function MyClass() {
this.name = "MyName";
this.age = 20;
}
Check: instanceof and constructor
My first ever Stackoverflow question (that I can remember). I'm slowly getting back into development after many years absence and experimenting with Node and Javascript.
I'm hoping this is really simple but I just can't see what I'm doing wrong.
I have 2 instances of a fairly simple javascript object.
When I pass one instance of that object to a function on the other things fall down.
The first time I reference a "self" value it's fine.
I then call a function on the passed-in object.
If I try to reference "self" again afterward, it seems that "self" has been overwritten by the passed-in object.
Here's the object source:
"use strict";
//simple object
module.exports.SimpleObject = function SimpleObject(inputName) {
try{
var self = this; //closure so we don't lose this reference in callbacks
self.name = inputName;
}
catch(err) {
console.log('Unable to create SimpleObject: '+err);
}
SimpleObject.prototype.getName = function() {
self = this;
return self.name;
}
SimpleObject.prototype.nestedOverwrite = function(anObject) {
self = this;
return "#1 self.name: "+self.name+" ObjectName: "+anObject.getName()+" #2 self.name: "+self.name;
}
return this;
}
And here's the test case:
var testSimpleObjectModule = require('./simpleObject');
var o0 = new testSimpleObjectModule.SimpleObject('test0');
var o1 = new testSimpleObjectModule.SimpleObject('test1');
console.log(o0.nestedOverwrite(o1));
console.log(o1.nestedOverwrite(o0));
Here's the output I get:
#1 self.name: test0 ObjectName: test1 #2 self.name: test1
#1 self.name: test1 ObjectName: test0 #2 self.name: test0
Hopefully you can see in each case, the first call to self.name is fine, the second has lost context.
any help greatly appreciated!
You're assigning functions to the prototype object that gets assigned to new SimpleObject every time the constructor is called. So the second time the constructor is called, the prototype is updated, and any previous object created using the prototype starts using the new function (with the new self value).
If you want those closures to have self, don't put them on the prototype, put them on this (more below).
But note that in the code you've quoted, there's no reason for the self variable. You can use prototype functions (which is more efficient) rather than creating functions for each instance. To do that, use this (without assigning it to self) and define the functions outside the constructor, like this:
"use strict";
//simple object
module.exports.SimpleObject = function SimpleObject(inputName) {
this.name = inputName;
};
SimpleObject.prototype.getName = function() {
return this.name;
};
SimpleObject.prototype.nestedOverwrite = function(anObject) {
return "#1 this.name: "+this.name+" ObjectName: "+anObject.getName()+" #2 this.name: "+this.name;
};
But if you're doing something with self that you aren't showing that means you really need it (e.g., that you can't trust the value of this when the functions are called &mdasdh; they're being used as callbacks/event handlers, etc.), here's how you do that:
"use strict";
//simple object
module.exports.SimpleObject = function SimpleObject(inputName) {
var self = this;
self.name = inputName;
self.getName = function() {
return self.name;
};
self.nestedOverwrite = function(anObject) {
return "#1 self.name: "+self.name+" ObjectName: "+anObject.getName()+" #2 self.name: "+self.name;
};
};
Side notes:
There's no reason to return this at the end of a constructor function.
Note that we don't want to do self = this; in each of the functions; the whole point of using self is that when the function is called, you don't want to rely on this having the correct value. (If yoU can rely on this being set, then use the prototype version of the functions.)
Never catch an exception in a constructor function and just say you couldn't do the construction, because by doing that, you allow the new Xyz expression to complete, and the un-initialized object is returned as the result of the expression. I've removed the try/catch from the above for that reason.
I recommend not relying on the horror that is automatic semicolon insertion. End expressions with semicolons (including when you're assigning a function to a variable or property).
Typically, calling this would work; but I am calling the method in another context, so this refers to that context rather than the 'parent' object.
Here's the code:
var someConfig = {
things: [
{
attr1: 'foo',
attr2: 'bar',
action: function() {
console.log(this);
}
}
]
}
function Constructor(config) {
var self = this;
self.name = 'test';
self.things = config.things;
$.each(self.things, function(i, thing) {
thing.action();
});
}
var obj = new Constructor(someConfig);
console.log(obj);
Here's a jsfiddle. The goal is to have both objects that are in the console to be the same, but this in the context of the action method returns the original object to which action belongs, rather than the constructor.
The only thing I can think of is to pass self into action, but I think there's a better way.
Firstly, a couple of points about your code. your config property is uniqueAction yet later you refer to it as config.action. When you call obj.action you need to call it as a function like: obj.action();
With that in mind, the following seems to do what you require...
var config = {
action: function() {
console.log(this.name);//<- IMPORTANT PART
}
}
function Constructor(config) {
var self = this;
self.name = 'test';
self.action = config.action;
}
var obj = new Constructor(config)
obj.action();
Notice that the console.log call now uses this.name instead of self.name.
Here is a working example
The only way I see is to tack the 'constructor' onto the config thing before calling the action.
Check out this Fiddle, which was forked off yours.
I've changed two things:
First, the action function no longer references this, but rather the property parent of this.
action: function() {
console.log(this.parent);
// ^^^^^^^
}
Second, each thing object receives a parent property, which references self.
$.each(self.things, function(i, thing) {
thing.parent = self;
thing.action();
});
Can functions passed into constructors have access to the constructor's other properties?
Yes, but only if you pass that object to the function (or implicitly use the this keyword).
What you did try was accessing the local self variable from a function that was declared outside the constructor, which is just impossible.
So just use
{
action: function() {
console.log(this.name);
// ^^^^
}
}
And when calling obj.action() the this will point to the instance.
I have an instance function in javascript and for naming conventions I have other instance function added as property of the first instance function object. It's better illustrated in the following working JavaScript.
var MyModule = (function() { // instance function
return function() {
console.log("ran MyModule");
}
}());
MyModule.RelatedModule = (function() { //second instance function is a property of first instance function object
return function() {
console.log("ran RelatedModule");
}
}())
var inst1 = new MyModule(), // logs "ran MyModule"
inst2 = new MyModule.RelatedModule(); // logs "ran RelatedModule"
This works as intended with no errors. What I'd like to do though is to create the function definition for MyModule after I've created the MyModule object, can anyone help me achieve this? I've illustrated my attempts below.
var MyModule = {}; // create object first, try to set a constructor on it later
MyModule.RelatedModule = (function() { // add properties to the MyModule object
return function() {
console.log("ran RelatedModule");
}
}())
// the following does not work, I'd like to set the `MyModule` constructor instance function and retain the `MyModule.RelatedModule` property
MyModule.constructor = (function() {
return function() {
console.log("ran MyModule");
}
}());
So, how do I retain the properties of an object and change it's constructor?
You're confusing a couple of concepts. In your first example, MyModule doesn't have a constructor, it is a constructor. That is, it's a function object that you intend to use with the new operator to create new objects. Those objects will have a constructor property that points back to MyModule (the function that created them). Changing this constructor property won't have any effect on MyModule; that property is just a pointer back to the function that instantiated the object.
In other words, you can't change MyModule's constructor. That's a meaningless statement.
Now, when you write:
var MyModule = {};
...you create a new object whose constructor property is Object():
console.log(MyModule.constructor) // prints Object()
Again, changing this property doesn't really do much (except obfuscate some useful book-keeping).
At this point MyModule is just a plain-old object. It's not a function at all. That's why you're getting the "not a function" error. Because it's not, but you're trying to use it as though it is. If you want that name to refer to a function (i.e. to a different object) then you're going to lose all references to any properties you previously set, because you're pointing at an entirely new object.
That's just the way it is.
Now, you could save a reference to the object that contains all those previously-set properties and copy them back into MyObject once you've pointed that name at a function. But I'm not sure what the point would be.
Everything lwburk says is correct, however, the below does what you were trying to accomplish, it does this by calling an init() method from MyModule.
var MyModule = function(){
return this.init();
};
MyModule.prototype.init = function(){};
MyModule.RelatedModule = (function() { // add properties to the MyModule object
return function() {
console.log("ran RelatedModule");
};
}());
MyModule.prototype.init = function(){
console.log("ran MyModule");
};
var inst1 = new MyModule(),
inst2 = new MyModule.RelatedModule();
First, there is a known browser bug where the constructor property (of the constructed object) correctly resolves using the prototype, but is not applied in object construction. So you need the following polyfill:
function NEW(clas, ...args)
{
let res = Object.setPrototypeOf({}, clas.prototype);
res.constructor.apply(res, args);
return res;
}
Second, you need to be setting MyModule.prototype.constructor instead of MyModule.constructor. The reason is that MyModule.constructor is the function that constructs new classes, not the function that constructs new objects. (See: Why does a class have a "constructor" field in JavaScript?)
In other words:
var MyModule = {}; // create object first, try to set a constructor on it later
MyModule.RelatedModule = (function() { // add properties to the MyModule object
return function() {
console.log("ran RelatedModule");
}
}())
// set the `MyModule` prototype.constructor instance function and retain the `MyModule.RelatedModule` property
MyModule.prototype = {
constructor() {
console.log("ran MyModule");
}}
var inst1 = NEW(MyModule), // logs "ran MyModule"
inst2 = NEW(MyModule.RelatedModule); // logs "ran RelatedModule"
The short answer is that you can't do that. You can however change the prototype of an object. Check out this answer for insight into how to rethink your approach to work with this constraint: Changing constructor in JavaScript