I'm currently writting a javascript script.
employe.name = Remi
employe2 = Object.create(Employe);
employe2.name = Vautrin
console.log(employe.name);//Vautrin instead of Remi
I was just wondering how to copy a full object. I've already tried :
//StackOverFlow
function clone(obj) {
if (obj == null || typeof (obj) != 'object')
return obj;
var temp = obj.constructor(); // changed
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = clone(obj[key]);
}
}
return temp;
}
This is the closure code :
var Employe = (function(){
return {
name = "Hello";
};
})();
But whenever I call a method, I get an error of non defined function.
Could you help me please ? :D
Second attempt
var EdT = (function() {
var _nbJoursTravaille = 0;
var _semaine ;
var _proprietaire;
return {
constructor: function(proprietaire) {
_proprietaire = proprietaire;
return this;
},
};
});
var Employe = (function() {
var _id;
var _nom;
var _prenom;
var _metier;
var _tel;
var _edT ;
return {
constructor: function(id, nom, prenom, metier, tel) {
_id = id;
_nom = nom;
_prenom = prenom;
_metier = metier;
_tel = tel;
_edT= Object.create(EdT).constructor(nom + " " + prenom);
return this;
},
//.... Some code
};
});
var employe = Object.create(Employe).constructor("id", "nom", "Rémi", "AlmostJsDev", "+33");
var employe2 =Object.create(Employe).constructor("id", "nom", "Rémi1234", "AlmostJsDev", "+33");
console.log(employe.getEdT().getProprietaire()); //Remi1234
Third Attempt
var EDT = (function(){
this.proprietaire;
return {
constructor : function(proprietaire){
this.proprietaire = proprietaire;
}
};
})();
console.log(employe.getEdT()._proprietaire);
It should be private ; instead it's public (seems legit) ... How to fix that ?
When you do
employe2.name = Vautrin
it will see if employe2 has a "name" property on itself. Since it could not find one, it creates a "name" property on the employe2 object, and hence the output of your code is "Remi" only.
The way Object.create(a,b) works is create a empty function, set it's prototype value to a, then create a new object using this function. Add the properties mentioned in b to the newly created object.
So when you wrote Object.create(employe);
It created a new object and set it's prototype to employe.
Read about how property lookups are handled for prototypes here.
Related
If I have this code
var node = function(n) {
var name = n;
var children = [];
var finished = false;
var failed = false;
this.getName = function() {
return name
};
this.downloadData = function(obj) {
};
this.getChildren = function() {
return children;
};
this.setChildren = function(c) {
Array.prototype.push.apply(children, c);
};
this.isFinished = function() {
return finished;
};
this.setFinished = function() {
finished = true;
}
this.isFailed = function() {
return failed;
}
this.setFailed = function() {
failed = true;
}
};
How can I convert this into an object like:
var a = new node("a");
var j = JSON.stringify(a);
result
{"name":"a","children":[],"finished":false,"failed":false}
thanks
This could be done by implementing the toJSON function.
If an object being stringified has a property named toJSON whose value
is a function, then the toJSON() method customizes JSON
stringification behavior: instead of the object being serialized, the
value returned by the toJSON() method when called will be serialized.
- Mozilla
eg:
var node = function(n) {
var name = n;
var children = [];
var finished = false;
var failed = false;
this.toJson = function toJson() {
return {"name":name ... };
}
}
You need object properties instead of variables.
So, instead of declaring var name = n;, you would declare this.name = n;. Which would make it look something like
var node = function(n) {
this.name = n;
this.children = [];
this.finished = false;
this.failed = false;
///other functions here
}
I'm writing a piece of code to easily save error logs in an object for debugging.
What I'm trying to achieve is to get the Object name from the function it was called from like so:
var MainObject = {
test : function() {
return MainObject.test.caller;
// When called from MainObject.testcaller,
// it should return MainObject.testcaller.
},
testcaller : function() {
return MainObject.test(); // Should return MainObject.testcaller, Returns own function code.
},
anothercaller : function() {
return MainObject.test(); // Should return MainObject.anothercaller, Returns own function code.
}
}
However when I run this code it returns the function code from MainObject.testcaller.
JSFiddle example
Is there any way this is possible?
Update
After looking at Rhumborl's answer, I discovered that assigning the value through another function would lead it to point back at the function name without the object itself.
Code:
(function (name, func) {
MainObject[name] = func;
})('invalid', function() {
return MainObject.test("blah");
});
// This now points at invalid() rather than MainObject.invalid()
Updated fiddle
There is a non–standard caller property of functions that returns the caller function, however that is a pointer to a function object and doesn't tell you the object it was called as a method of, or the object's name. You can get a reference to the function through arguments.callee.
There is also the obsolete arguments.caller, but don't use that. It also provides a reference to the calling function (where supported).
Once you have a reference to the calling function (if there is one), you then have the issue of resolving its name. Given that Functions are Objects, and objects can be referenced by multiple properties and variables, the concept of a function having a particular name is alluvial.
However, if you know that the function is a property of some object, you can iterate over the object's own enumerable properties to find out which one it is.
But that seems to be a rather odd thing to do. What are you actually trying to do? You may be trying to solve a problem that can be worked around in a much more robust and simpler way.
Edit
You can do what you want in a very limited way using the method described above for the case in the OP, however it is not robust or a general solution:
var mainObject = {
test : function() {
var obj = this;
var caller = arguments.callee.caller;
var global = (function(){return this}());
var fnName, objName;
for (var p in global) {
if (global[p] === obj) {
objName = p;
}
}
for (var f in obj) {
if (obj[f] === caller) {
fnName = f;
}
}
return objName + '.' + fnName;
},
testcaller : function() {
return mainObject.test();
},
anothercaller : function() {
return mainObject.test();
}
}
console.log(mainObject.testcaller()); // mainObject.testcaller
console.log(mainObject.anothercaller()); // mainObject.anothercaller
but it's brittle:
var a = mainObject.anothercaller;
console.log(a()); // mainObject.anothercaller
var b = {
foo : mainObject.anothercaller
}
console.log(b.foo()); // mainObject.anothercaller
Oops.
You can use this trick at http://www.eriwen.com/javascript/js-stack-trace/ which throws an error, then parses the stack trace.
I have updated it for the latest versions of Firefox, Chrome and IE. Unfortunately it doesn't work well on my IE9 (and I haven't tested it on Opera).
function getStackTrace() {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox/Chrome/IE11
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
var line = lines[i].trim();
if (line.match(/^at [A-Za-z0-9\.\-_\$]+\s*\(/)) {
// Chrome/IE: " at Object.MainObject.testcaller (url:line:char)"
var entry = line.substring(3, line.indexOf('(') - 1);
// Chrome appends "Object." to the front of the object functions, so strip it off
if (entry.indexOf("Object.") == 0) {
entry = entry.substr(7, entry.length);
}
callstack.push(entry);
} else if (line.match(/^[A-Za-z0-9\.\-_\$]+\s*#/)) {
// Firefox: "MainObject.testcaller#url:line:char"
callstack.push(line.substring(0, lines[i].indexOf('#')));
}
}
//Remove call to getStackTrace()
callstack.shift();
isCallstackPopulated = true;
} else if (window.opera && e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to getStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE9 and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf('')) || 'anonymous';
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
return callstack;
}
var MainObject = {
test: function (x) {
// first entry is the current function (test), second entry is the caller
var stackTrace = getStackTrace();
var caller = stackTrace[1];
return caller + "()";
},
testcaller: function () {
return MainObject.test(1, null);
}
}
function SomeFunction() {
return MainObject.test("blah");
}
document.body.innerHTML += '<b style="color: red">' + MainObject.testcaller() + '</b>';
document.body.innerHTML += '<div>Calling SomeFunction() returns: <b style="color: red">' + SomeFunction() + '</b></div>';
MainObject.test() should return: <b style="color: blue">MainObject.testcaller()</b>
<hr />
MainObject.test() returns:
Updated fiddle here
I have a 'model' class/prototype defined as below, it has subclass named 'given' which tries to access method 'getNodes()' of model class.
But it gives exception for 'this.getNodes' saying undefined.
var model = {
constructor: function(/*string*/ mode, /*string*/ name, /*object*/ properties) {
this._mode = mode;
this.beginX = 100;
this.beginY = 100;
this.nodeWidth = 200;
this.nodeHeight = 200;
this.x = this.beginX;
this.y = this.beginY;
this.lastNodeVisible = null;
this.ID = 1;
this.taskName = name;
this.properties = properties;
this.checkedNodes = new Array();
// this.model = #call_build_method;
/*
add subclasses with model accessors
*/
this.given = {
getNodes: this.getNodes,
setNodeName: this.setNodeName
};
},
getNodes: function() {
// Summary: returns an array containing the nodes in the given model
return #someobject;
},
}
I assume that you want to call a method in the parent class with the correct scope.
Here are two ways to do this, one using dojo hitch, and one without:
require([
"dojo/_base/lang"
],function(lang){
model = function(){
var obj = {
data: "ok",
getData4: function(){
return this.data;
}
};
obj.sub = {
getData5: lang.hitch(obj, obj.getData4),
getData6: function(){return obj.getData4.apply(obj,arguments);}
};
return obj;
};
m = new model();
console.log("call getData4: ", m.getData4()); // returns "ok"
console.log("call getData5: ", m.sub.getData5()); // returns "ok"
console.log("call getData6: ", m.sub.getData6()); // returns "ok"
});
You need to store this in variable in outter scope:
this.model = <SOMETHING>;
var self = this;
this.given = {
getNodes: function(){self.getNodes(self.model);}
// inside a function this is this.given
};
I'm running into a bizarre bug while trying to create a Dictionary object. Pretty basic stuff. However when I create 2 instances of the object, and then set some values on one, they appear on both. What am I doing wrong here?
function Dict() { }
Dict.prototype = {
items: { },
prop: function(key) {
return ':' + key;
},
get: function(key, def) {
var p = this.prop(key),
k = this.items;
return k.hasOwnProperty(p) ? k[p] : def;
},
set: function(key, value) {
var p = this.prop(key);
this.items[p] = value;
return value;
},
count: function() {
return Object.keys(this.items).length;
},
has: function(key) {
var p = this.prop(key);
return this.items.hasOwnProperty(p);
},
del: function(key) {
var p = this.prop(key),
k = this.items;
if(k.hasOwnProperty(p))
delete k[p];
},
keys: function() {
return Object.keys(this.items).map(function(key) {
return key.substring(1);
});
}
};
var a = new Dict();
var b = new Dict();
a.set('foo', 'bar');
console.log(a.keys());
console.log(b.keys());
You're defining items inside your prototype which means it will be shared by all instances. You need to set it inside the "constructor" function and remove it from the prototype.
function Dict() { this.items = []; }
I've created a JS Fiddle for you with the full source code at http://jsfiddle.net/brunomsilva/zaSY2/.
The items property is set on the prototype. The prototype is not cloned when creating an object, so items is the same on the two Dicts. Set items in the constructor so each object has its own:
function Dict() {
this.items = {};
}
Prototypes work because when you try to access an object's property, it first checks the object's own properties to see if it contains it. If so, that's the value. If it's not found there, it checks the prototype. If it's not there, it continues traversing the chain of prototypes until it finds the property. If it's still not found, it results in undefined. (for more detail, see the specification)
to define a class to use, try moving the function definitions to the prototype without replacing the prototype object, like so:
function Dict() {
this.items = {};
}
Dict.prototype.prop = function (key) {
return ':' + key;
};
Dict.prototype.get = function (key, def) {
var p = this.prop(key),
k = this.items;
return k.hasOwnProperty(p) ? k[p] : def;
};
Dict.prototype.set = function (key, value) {
var p = this.prop(key);
this.items[p] = value;
return value;
};
Dict.prototype.count = function () {
return Object.keys(this.items).length;
};
Dict.prototype.has =function (key) {
var p = this.prop(key);
return this.items.hasOwnProperty(p);
};
Dict.prototype.del =function (key) {
var p = this.prop(key),
k = this.items;
if (k.hasOwnProperty(p))
delete k[p];
};
Dict.prototype.keys = function () {
return Object.keys(this.items).map(function (key) {
return key.substring(1);
});
};
When I override the clone() method of a Backbone.Model, is there a way to call this overriden method from my implantation? Something like this:
var MyModel = Backbone.Model.extend({
clone: function(){
super.clone();//calling the original clone method
}
})
You'll want to use:
Backbone.Model.prototype.clone.call(this);
This will call the original clone() method from Backbone.Model with the context of this(The current model).
From Backbone docs:
Brief aside on super: JavaScript does not provide a simple way to call
super — the function of the same name defined higher on the prototype
chain. If you override a core function like set, or save, and you want
to invoke the parent object's implementation, you'll have to
explicitly call it.
var Note = Backbone.Model.extend({
set: function(attributes, options) {
Backbone.Model.prototype.set.apply(this, arguments);
...
}
});
You can also use the __super__ property which is a reference to the parent class prototype:
var MyModel = Backbone.Model.extend({
clone: function(){
MyModel.__super__.clone.call(this);
}
});
Josh Nielsen found an elegant solution for this, which hides a lot of the ugliness.
Just add this snippet to your app to extend Backbone's model:
Backbone.Model.prototype._super = function(funcName){
return this.constructor.prototype[funcName].apply(this, _.rest(arguments));
}
Then use it like this:
Model = Backbone.model.extend({
set: function(arg){
// your code here
// call the super class function
this._super('set', arg);
}
});
Working from the answers given by geek_dave and charlysisto, I wrote this to add this._super(funcName, ...) support in classes that have multiple levels of inheritance. It's worked well in my code.
Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) {
// Find the scope of the caller.
var scope = null;
var scan = this.__proto__;
search: while (scope == null && scan != null) {
var names = Object.getOwnPropertyNames(scan);
for (var i = 0; i < names.length; i++) {
if (scan[names[i]] === arguments.callee.caller) {
scope = scan;
break search;
}
}
scan = scan.constructor.__super__;
}
return scan.constructor.__super__[funcName].apply(this, _.rest(arguments));
};
A year later I've fixed some bugs and made things faster. Below is the code that I use now.
var superCache = {};
// Hack "super" functionality into backbone.
Backbone.View.prototype._superFn = Backbone.Model.prototype._superFn = function(funcName, _caller) {
var caller = _caller == null ? arguments.callee.caller : _caller;
// Find the scope of the caller.
var scope = null;
var scan = this.__proto__;
var className = scan.constructor.className;
if (className != null) {
var result = superCache[className + ":" + funcName];
if (result != null) {
for (var i = 0; i < result.length; i++) {
if (result[i].caller === caller) {
return result[i].fn;
}
}
}
}
search: while (scope == null && scan != null) {
var names = Object.getOwnPropertyNames(scan);
for (var i = 0; i < names.length; i++) {
if (scan[names[i]] === caller) {
scope = scan;
break search;
}
}
scan = scan.constructor.__super__;
}
var result = scan.constructor.__super__[funcName];
if (className != null) {
var entry = superCache[className + ":" + funcName];
if (entry == null) {
entry = [];
superCache[className + ":" + funcName] = entry;
}
entry.push({
caller: caller,
fn: result
});
}
return result;
};
Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) {
var args = new Array(arguments.length - 1);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i + 1];
}
return this._superFn(funcName, arguments.callee.caller).apply(this, args);
};
Then given this code:
var A = Backbone.Model.extend({
// className: "A",
go1: function() { console.log("A1"); },
go2: function() { console.log("A2"); },
});
var B = A.extend({
// className: "B",
go2: function() { this._super("go2"); console.log("B2"); },
});
var C = B.extend({
// className: "C",
go1: function() { this._super("go1"); console.log("C1"); },
go2: function() { this._super("go2"); console.log("C2"); }
});
var c = new C();
c.go1();
c.go2();
The output in the console is this:
A1
C1
A2
B2
C2
What's interesting is that class C's call to this._super("go1") scans the class hierarchy until it gets a hit in class A. Other solutions do not do this.
P.S. Uncomment the className entries of the class definitions to enable caching of the _super lookup. (The assumption is that these class names will be unique in the application.)
If you want just to call this._super(); without passing the function name as an argument
Backbone.Controller.prototype._super = function(){
var fn = Backbone.Controller.prototype._super.caller, funcName;
$.each(this, function (propName, prop) {
if (prop == fn) {
funcName = propName;
}
});
return this.constructor.__super__[funcName].apply(this, _.rest(arguments));
}
Better use this plugin:
https://github.com/lukasolson/Backbone-Super
I believe you can cache the original method (although not tested):
var MyModel = Backbone.Model.extend({
origclone: Backbone.Model.clone,
clone: function(){
origclone();//calling the original clone method
}
});
backbone._super.js, from my gists: https://gist.github.com/sarink/a3cf3f08c17691395edf
// Forked/modified from: https://gist.github.com/maxbrunsfeld/1542120
// This method gives you an easier way of calling super when you're using Backbone in plain javascript.
// It lets you avoid writing the constructor's name multiple times.
// You still have to specify the name of the method.
//
// So, instead of having to write:
//
// var Animal = Backbone.Model.extend({
// word: "",
// say: function() {
// return "I say " + this.word;
// }
// });
// var Cow = Animal.extend({
// word: "moo",
// say: function() {
// return Animal.prototype.say.apply(this, arguments) + "!!!"
// }
// });
//
//
// You get to write:
//
// var Animal = Backbone.Model.extend({
// word: "",
// say: function() {
// return "I say " + this.word;
// }
// });
// var Cow = Animal.extend({
// word: "moo",
// say: function() {
// return this._super("say", arguments) + "!!!"
// }
// });
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define(["underscore", "backbone"], function(_, Backbone) {
return factory(_, Backbone);
});
}
else if (typeof exports !== "undefined") {
var _ = require("underscore");
var Backbone = require("backbone");
module.exports = factory(_, Backbone);
}
else {
factory(root._, root.Backbone);
}
}(this, function(_, Backbone) {
"use strict";
// Finds the next object up the prototype chain that has a different implementation of the method.
var findSuper = function(methodName, childObject) {
var object = childObject;
while (object[methodName] === childObject[methodName]) {
object = object.constructor.__super__;
}
return object;
};
var _super = function(methodName) {
// Keep track of how far up the prototype chain we have traversed, in order to handle nested calls to `_super`.
this.__superCallObjects__ || (this.__superCallObjects__ = {});
var currentObject = this.__superCallObjects__[methodName] || this;
var parentObject = findSuper(methodName, currentObject);
this.__superCallObjects__[methodName] = parentObject;
// If `methodName` is a function, call it with `this` as the context and `args` as the arguments, if it's an object, simply return it.
var args = _.tail(arguments);
var result = (_.isFunction(parentObject[methodName])) ? parentObject[methodName].apply(this, args) : parentObject[methodName];
delete this.__superCallObjects__[methodName];
return result;
};
// Mix in to Backbone classes
_.each(["Model", "Collection", "View", "Router"], function(klass) {
Backbone[klass].prototype._super = _super;
});
return Backbone;
}));
In the case that you don't know what the parent class is exactly (multiple inheritance or you want a helper function) then you can use the following:
var ChildModel = ParentModel.extend({
initialize: function() {
this.__proto__.constructor.__super__.initialize.apply(this, arguments);
// Do child model initialization.
}
});
With helper function:
function parent(instance) {
return instance.__proto__.constructor.__super__;
};
var ChildModel = ParentModel.extend({
initialize: function() {
parent(this).initialize.apply(this, arguments);
// Do child model initialization.
}
});
Pass the parent class as an option during instantiation:
BaseModel = Backbone.Model.extend({
initialize: function(attributes, options) {
var self = this;
this.myModel = new MyModel({parent: self});
}
});
Then in your MyModel you can call parent methods like this
this.options.parent.method();
Keep in mind this creates a retain cycle on the two objects. So to let the garbage collector do it's job you would need to manually destroy the retain on one of the objects when finished with it. If you're application is pretty large. I would encourage you to look more into hierarchal setups so events can travel up to the correct object.
2 functions below, one requires you pass in the function name, the other can "discover" which function we want the super version of
Discover.Model = Backbone.Model.extend({
_super:function(func) {
var proto = this.constructor.__super__;
if (_.isUndefined(proto[func])) {
throw "Invalid super method: " + func + " does not exist in prototype chain.";
}
return proto[func].apply(this, _.rest(arguments));
},
_superElegant:function() {
t = arguments;
var proto = this.constructor.__super__;
var name;
for (name in this) {
if (this[name] === arguments.callee.caller) {
console.log("FOUND IT " + name);
break;
} else {
console.log("NOT IT " + name);
}
}
if (_.isUndefined(proto[name])) {
throw "Super method for: " + name + " does not exist.";
} else {
console.log("Super method for: " + name + " does exist!");
}
return proto[name].apply(this, arguments);
},
});
Here is how I would do this:
ParentClassName.prototype.MethodToInvokeName.apply(this);
so for your example this is:
Model.prototype.clone.apply(this)