My code is as follows,
class myClass
{
constructor()
{
this.memberVar1="";
this.memberVar2="";
}
memberFunction1()
{
// memberFunction1 code
}
}
var myObj = {
objVar1 : someJsonObject,
objVar2 : new myClass()
}
Instead of calling the memberFunction and memberVar1,memberVar2 like
myObj.objVar2.memberVar1;
myObj.objVar2.memberFunction1();
I wish to call them as follows
myObj.memberVar1;
myObj.memberFunction1();
Is there any way i can restructure myObj to achieve this ?
Thanks in advance
Functions are first class objects in Javascript, and a member of a Javascript class is just an object, so there's nothing stopping you setting the reference that myObj.memberFunction1 to whatever you like, including someJsonObject.memberFunction1, however you need to be aware that the behavior of this will be different depending on how you set the reference. This may be important to make your memberFunction behave correctly depending on context.
class myClass
{
constructor()
{
this.memberVar1="";
this.memberVar2="";
}
memberFunction1()
{
// memberFunction1 code
}
}
let someJsonObject = {
memberFunction1: function(source) {
if (this === myObj) { console.log(`${source}: "this" is myObj`) }
else if (this === someJsonObject) { console.log(`${source}: "this" is someJsonObject`) }
else { console.log('JavaScript interpreter is insane') }
}
}
var myObj = {
objVar1 : someJsonObject,
objVar2 : new myClass(),
memberFunction1: someJsonObject.memberFunction1 // the assignment will cause "this" to point to myObj
}
console.log('==== setting the reference (changes this) ====')
myObj.memberFunction1('myObj')
someJsonObject.memberFunction1('someJsonObject')
myObj.objVar1.memberFunction1('myObj.objVar1') // myObj.objVar1 is === someJsonObject
myObj.keepThis = myObj.memberFunction1 // You can hang onto the "myObj" referenced version of the function if you want
console.log('==== binding to someJsonObject ====')
myObj.memberFunction1 = myObj.memberFunction1.bind(someJsonObject) // using bind will make "this" point to whatever you like (i.e someJsonObject)
myObj.memberFunction1('myObj')
someJsonObject.memberFunction1('someJsonObject')
myObj.objVar1.memberFunction1('myObj.objVar1')
myObj.keepThis('myObj.keepThis')
This is one way of doing it:
const someObj = new myClass();
var myObj = {
objVar1 : {},
memberFunction1 : someObj.memberFunction1
}
Then call it like you want:
myObj.memberFunction1()
EDIT:
If you want to merge N number of members you can use Object.create like this:
const instance = new myClass();
const myObj = Object.create(instance);
myObj.memberFunction1()
It seems like I don't understand prototypes correctly once again.
If you call a method on an object, and it doesn't exist - does it not check the prototype for the method can run it?
like
Array.prototype.myArrayMagic = function () { ... }
Then on any array you can call arr.myArrayMagic() ?
That was my understanding, but I am now running into issue where calling a prototype method wont work unless I explicitly call it through .prototype
Also: I am restricted to ES5 code
The base module setup
function Wall() {}
Wall.prototype = {
shouldShowWall: function() {
return true;
},
show: function () {
if (!this.shouldShowWall()) {
return;
}
// Do other stuff here
}
}
module.exports = Wall;
The child object
function OtherWall() {
this.item = 'my_tracking_id',
this.minutes = 30
// ...
}
OtherWall.prototype = {
constructor: Object.create(Wall.prototype),
/**
* Override wall.prototype.shouldShowWall method for deciding if the wall should be shown at this time
*/
shouldShowWall: function() {
// return true or flase here
}.bind(this)
}
module.exports = OtherWall;
So following other answers and trying to understand as best as possible, this should be set up in a way where I can call
new OtherWall().show();
but it doesn't work, it only works if I call
new OtherWall().prototype.show();
Is this normal, or have I set it up wrong?
What do I need to change to get it to work with OtherWall().show();
If you call a method on an object, and it doesn't exist - does it not check the prototype for the method can run it?
Yes, it does (like with any property), but this code is incorrect:
OtherWall.prototype = {
constructor: Object.create(Wall.prototype)
// ...
That won't make Wall.prototype the prototype of OtherWall.prototype. To do that, do this:
OtherWall.prototype = Object.create(Wall.prototype);
Then add properties on OtherWall.prototype, starting with constructor:
// But keep reading, I wouldn't do it quite like this
OtherWall.prototype.constructor = OtherWall;
...then shouldShowWall, etc.
My answer here may also be useful, it shows creating a "subclass" using ES5 syntax (contrasting it with ES2015's class syntax).
Another couple of notes:
1. This looks like it's probably not what you intend:
/**
* Override wall.prototype.shouldShowWall method for deciding if the wall should be shown at this time
*/
shouldShowWall: function() {
// return true or flase here
}.bind(this)
The this in scope there isn't an instance of OtherWall, it's whatever this is outside that object literal. If you want to bind shouldShowWall to the instance, you'll need to do that in the constructor.
2. I'd suggest making methods non-enumerable, like ES2015's class syntax does, and the same for the constructor property like JavaScript always has. When you create a property via simple assignment, the property is enumerable.
3. I think you have a typo in OtherWall, you've used a , at the end of the first statement instead of a ;. Technically, it works, because of the comma operator, but I don't think you were using the comma operator on purpose. :-)
Here's an example applying all of the above; Object.defineProperty is an ES5 method (I'm out of practice writing ES5 code, but I think I've avoided all ES2015+ stuff here):
function assignNonEnumerable(target, source) {
Object.keys(source).forEach(function (key) {
Object.defineProperty(target, key, {
value: source[key],
configurable: true,
writable: true,
enumerable: false, // This is the default, but showing it here for emphasis
});
});
}
function Wall() {
}
assignNonEnumerable(Wall.prototype, {
shouldShowWall: function () {
return true;
},
show: function () {
console.log("show called");
if (!this.shouldShowWall()) {
return;
}
// Do other stuff here
},
});
function OtherWall() {
this.item = "my_tracking_id";
this.minutes = 30;
// If you want to bind it to the instance:
this.shouldShowWall = this.shouldShowWall.bind(this);
}
OtherWall.prototype = Object.create(Wall.prototype);
assignNonEnumerable(OtherWall.prototype, {
constructor: OtherWall,
shouldShowWall: function () {
// You can't bind the instance here, see the constructor above
console.log("shouldShowWall called");
return Math.random() < 0.5;
},
});
new OtherWall().show();
I am new and going on javascript way. At the point of Closure. I can create object with new keyword and without new keyword, it is so weird me.
Here code:-
function RankingManage(rank) {
var rank = rank;
function rankInc() {
rank++;
}
function rankDec() {
rank--;
}
return {
makeInc: function () {
rankInc();
},
makeDec: function () {
rankDec();
},
showRank: function () {
return rank;
}
}
}
var Rank = new RankingManage(80);
var Rank2 = RankingManage(80);
Rank.makeInc();
Rank2.makeInc();
console.log(Rank.showRank());//Output:- 81
console.log(Rank2.showRank());//Output:- 81
This has nothing to do with closures.
Using new sets the value of this for the function to a new object and causes it to return this by default.
You are using a return statement to explicitly return an object, so you return that object instead of this.
Because you're creating and returning an object (with the return { ... }), you wouldn't normally call RankingManage via new. You're not using the object that the new operator creates, and you're overriding the default result of the new expression by returning a non-null object from the function. So both ways work, but it doesn't make sense to use new with your function as currently written.
Your current function is absolutely fine, just call it wihtout new. (And since it's not a constructor function — a function you call with new — you'd start it with a lower-case letter, by convention, not an upper-case one. So rankingManage.)
If you wanted to require new when calling RankingManage, you could use the object that new creates via the this keyword:
function RankingManage(rank) {
var rank = rank;
function rankInc() {
rank++;
}
function rankDec() {
rank--;
}
this.makeInc = rankInc;
this.makeDec = rankDec;
this.showRank = function() {
return rank;
};
}
We don't use a return in that, because (again) the default result of the new RankingManage expression is a reference to the object that new created.
But again, your existing function is fine.
Usually, things are done a little bit differently when you're defining an object in this way.
public function MyObject(value) {
this.value = value;
this.doSomething = function() { console.log("Hello!"); };
}
var myObject = new MyObject(10);
var notObject = MyObject(10);
myObject.doSomething(); // Prints Hello!
notObject.doSomething(); // Prints out "can not get property of undefined"
You will now see a change in behaviour, because new prevents the default behaviour of adding values to window and adds them to an object that gets returned.
JSFiddle: https://jsfiddle.net/LLx3376n/
Suppose we've the following function:
function Car(color){
this.wheels = 4;
this.color = color;
}
What exactly new operator does (new Car('red');) could be stated as follows:
Create an empty object and make the this inside the function to point to.
Invoke the function. (Note that this points to that empty object).
That empty object will be populated with properties: wheels and color.
Implicitly returns that object. (If you have explicit return statement, that object of this will be discarded.)
At the end, your returned object is of this structure:
{
wheels: 4,
color: 'red'
}
I admit this question is getting to the limits of what I know of JavaScript & jQuery, and there is probably a more proper way to state my question (which would help in finding an existing solution), but if you can bear with me, this is what I'm after.
I have an existing object class I've defined. I'm making a jQuery ajax call using getJSON, and I want my callback parameter (which is an object) to be classed as my custom object, so that I can access that class' methods from it.
So I have some object class
function Boo() {
this.param1;
this.param2;
this.yah = function() {
...
}
}
and then I have something elsewhere of the sort
$.getJSON(url,function(new_instance) {
//from my php source this passed object is already loaded with param1, param2...
alert(new_instance.param1); //no probs
//but i want to be able to then call
new_instance.yah();
});
In other words, I want new_instance to be considered an instance of Boo(). I know in stuff like ActionScript you have to class the incoming parameters for exactly this reason, dunno what flexibility I have in JS.
I thought maybe about having an intermediate function that takes in the incoming object and creates/populates a new instance of Boo() but not sure if there is a more clever method.
Many thanks!!
Do not define methods in the constructor function, you are
defining them over and over again every time the costructor
is called. Move them over to the prototype:
Boo.prototype = {
yah: function() {
},
bah: function() {
}
...
};
a little helper function:
function coerceTo( proto, values ) {
var r = Object.create( proto );
for( var key in values ) {
r[key] = values[key];
}
return r;
}
Depending on browser, Object.create might not be available, so:
if (!Object.create) {
Object.create = function (o) {
if (arguments.length > 1) {
throw new Error('Object.create implementation only accepts the first parameter.');
}
function F() {}
F.prototype = o;
return new F();
};
}
Usage:
new_instance = coerceTo( Boo.prototype, new_instance );
new_instance instanceof Boo //true
new_instance.yah();
What you can do:
$.getJSON(url,function(newObjData) {
var newObj = $.extend(new Boo(), newObjData);
newObj.yah();
});
Also consider moving your Boo methods to object prototype so the methods don't get recreated for each Boo instance:
var Boo = function() {
this.param1;
this.param2;
}
Boo.prototype.yah = function() {
console.log(this.param1);
}
This is something which has been bugging me with the Google Chrome debugger and I was wondering if there was a way to solve it.
I'm working on a large Javascript application, using a lot of object oriented JS (using the Joose framework), and when I debug my code, all my classes are given a non-sensical initial display value. To see what I mean, try this in the Chrome console:
var F = function () {};
var myObj = new F();
console.log(myObj);
The output should be a single line which you can expand to see all the properties of myObj, but the first thing you see is just ▶ F.
My issue is that because of my OO framework, every single object instantiated gets the same 'name'. The code which it looks is responsible for this is like so:
getMutableCopy : function (object) {
var f = function () {};
f.prototype = object;
return new f();
}
Which means that in the debugger, the initial view is always ▶ f.
Now, I really don't want to be changing anything about how Joose instantiates objects (getMutableCopy...?), but if there was something I could add to this so that I could provide my own name, that would be great.
Some things that I've looked at, but couldn't get anywhere with:
> function foo {}
> foo.name
"foo"
> foo.name = "bar"
"bar"
> foo.name
"foo" // <-- looks like it is read only
Object.defineProperty(fn, "name", { value: "New Name" });
Will do the trick and is the most performant solution. No eval either.
I've been playing around with this for the last 3 hours and finally got it at least somewhat elegant using new Function as suggested on other threads:
/**
* JavaScript Rename Function
* #author Nate Ferrero
* #license Public Domain
* #date Apr 5th, 2014
*/
var renameFunction = function (name, fn) {
return (new Function("return function (call) { return function " + name +
" () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
};
/**
* Test Code
*/
var cls = renameFunction('Book', function (title) {
this.title = title;
});
new cls('One Flew to Kill a Mockingbird');
If you run the above code, you should see the following output to your console:
Book {title: "One Flew to Kill a Mockingbird"}
Combine usage of computed property name to dynamically name a property, and inferred function naming to give our anonymous function that computed property name:
const name = "aDynamicName"
const tmp = {
[name]: function(){
return 42
}
}
const myFunction= tmp[name]
console.log(myFunction) //=> [Function: aDynamicName]
console.log(myFunction.name) //=> 'aDynamicName'
One could use whatever they want for 'name' here, to create a function with whatever name they want.
If this isn't clear, let's break down the two pieces of this technique separately:
Computed Property Names
const name = "myProperty"
const o = {
[name]: 42
}
console.log(o) //=> { myProperty: 42 }
We can see that the property name assigned on o was myProperty, by way of computed property naming. The []'s here cause JS to lookup the value inside the bracket, and to use that for the property name.
Inferred Function Naming
const o = {
myFunction: function(){ return 42 }
}
console.log(o.myFunction) //=> [Function: myFunction]
console.log(o.myFunction.name) //=> 'myFunction'
Here we use inferred function naming. The language looks at the name of wherever the function is being assigned to, & gives the function that inferred name.
We can combine these two techniques, as shown in the beginning. We create an anonymous function, which gets it's name via inferred function naming, from a computed property name, which is the dynamic name we wanted to create. Then we have to extract the newly created function from the object it is embedded inside of.
Example Using Stack Trace
Naming a supplied anonymous function
// Check the error stack trace to see the given name
function runAnonFnWithName(newName, fn) {
const hack = { [newName]: fn };
hack[newName]();
}
runAnonFnWithName("MyNewFunctionName", () => {
throw new Error("Fire!");
});
Although it is ugly, you could cheat via eval():
function copy(parent, name){
name = typeof name==='undefined'?'Foobar':name;
var f = eval('function '+name+'(){};'+name);
f.prototype = parent;
return new f();
}
var parent = {a:50};
var child = copy(parent, 'MyName');
console.log(child); // Shows 'MyName' in Chrome console.
Beware: You can only use names which would be valid as function names!
Addendum: To avoid evaling on every object instantiation, use a cache:
function Cache(fallback){
var cache = {};
this.get = function(id){
if (!cache.hasOwnProperty(id)){
cache[id] = fallback.apply(null, Array.prototype.slice.call(arguments, 1));
}
return cache[id];
}
}
var copy = (function(){
var cache = new Cache(createPrototypedFunction);
function createPrototypedFunction(parent, name){
var f = eval('function '+name+'(){};'+name);
f.prototype = parent;
return f;
}
return function(parent, name){
return new (cache.get(name, parent, typeof name==='undefined'?'Foobar':name));
};
})();
This won't totally solve your problem, but I would suggest overriding the toString method on the class's prototype. For instance:
my_class = function () {}
my_class.prototype.toString = function () { return 'Name of Class'; }
You'll still see the original class name if you enter an instance of my_class directly in the console (I don't think it's possible to do anything about this), but you'll get the nice name in error messages, which I find very helpful. For instance:
a = new my_class()
a.does_not_exist()
Will give the error message: "TypeError: Object Name of Class has no method 'does_not_exist'"
If you want to dynamically create a named function. You can use new Function to create your named function.
function getMutableCopy(fnName,proto) {
var f = new Function(`function ${fnName}(){}; return ${fnName}`)()
f.prototype = proto;
return new f();
}
getMutableCopy("bar",{})
// ▶ bar{}
Similar to #Piercey4 answer, but I had to set the name for the instance as well:
function generateConstructor(newName) {
function F() {
// This is important:
this.name = newName;
};
Object.defineProperty(F, 'name', {
value: newName,
writable: false
});
return F;
}
const MyFunc = generateConstructor('MyFunc');
const instance = new MyFunc();
console.log(MyFunc.name); // prints 'MyFunc'
console.log(instance.name); // prints 'MyFunc'
normally you use window[name] like
var name ="bar";
window["foo"+name] = "bam!";
foobar; // "bam!"
which would lead you to a function like:
function getmc (object, name) {
window[name] = function () {};
window[name].prototype = object;
return new window[name]();
}
but then
foo = function(){};
foobar = getmc(foo, "bar");
foobar; // ▶ window
foobar.name; // foo
x = new bar; x.name; // foo .. not even nija'ing the parameter works
and since you can't eval a return statement (eval("return new name()");), I think you're stuck
I think this is the best way to dynamically set the name of a function :
Function.prototype.setName = function (newName) {
Object.defineProperty(this,'name', {
get : function () {
return newName;
}
});
}
Now you just need to call the setName method
function foo () { }
foo.name; // returns 'foo'
foo.setName('bar');
foo.name; // returns 'bar'
foo.name = 'something else';
foo.name; // returns 'bar'
foo.setName({bar : 123});
foo.name; // returns {bar : 123}
Based on the answer of #josh, this prints in a console REPL, shows in console.log and shows in the debugger tooltip:
var fn = function() {
return 1917;
};
fn.oldToString = fn.toString;
fn.toString = function() {
return "That fine function I wrote recently: " + this.oldToString();
};
var that = fn;
console.log(that);
Inclusion of fn.oldToString() is a magic which makes it work. If I exclude it, nothing works any more.
With ECMAScript2015 (ES2015, ES6) language specification, it is possible to dynamically set a function name without the use of slow and unsafe eval function and without Object.defineProperty method which both corrupts function object and does not work in some crucial aspects anyway.
See, for example, this nameAndSelfBind function that is able to both name anonymous functions and renaming named functions, as well as binding their own bodies to themselves as this and storing references to processed functions to be used in an outer scope (JSFiddle):
(function()
{
// an optional constant to store references to all named and bound functions:
const arrayOfFormerlyAnonymousFunctions = [],
removeEventListenerAfterDelay = 3000; // an auxiliary variable for setTimeout
// this function both names argument function and makes it self-aware,
// binding it to itself; useful e.g. for event listeners which then will be able
// self-remove from within an anonymous functions they use as callbacks:
function nameAndSelfBind(functionToNameAndSelfBind,
name = 'namedAndBoundFunction', // optional
outerScopeReference) // optional
{
const functionAsObject = {
[name]()
{
return binder(...arguments);
}
},
namedAndBoundFunction = functionAsObject[name];
// if no arbitrary-naming functionality is required, then the constants above are
// not needed, and the following function should be just "var namedAndBoundFunction = ":
var binder = function()
{
return functionToNameAndSelfBind.bind(namedAndBoundFunction, ...arguments)();
}
// this optional functionality allows to assign the function to a outer scope variable
// if can not be done otherwise; useful for example for the ability to remove event
// listeners from the outer scope:
if (typeof outerScopeReference !== 'undefined')
{
if (outerScopeReference instanceof Array)
{
outerScopeReference.push(namedAndBoundFunction);
}
else
{
outerScopeReference = namedAndBoundFunction;
}
}
return namedAndBoundFunction;
}
// removeEventListener callback can not remove the listener if the callback is an anonymous
// function, but thanks to the nameAndSelfBind function it is now possible; this listener
// removes itself right after the first time being triggered:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
e.target.removeEventListener('visibilitychange', this, false);
console.log('\nEvent listener 1 triggered:', e, '\nthis: ', this,
'\n\nremoveEventListener 1 was called; if "this" value was correct, "'
+ e.type + '"" event will not listened to any more');
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to prove that deanonymized functions -- even when they have the same 'namedAndBoundFunction'
// name -- belong to different scopes and hence removing one does not mean removing another,
// a different event listener is added:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
console.log('\nEvent listener 2 triggered:', e, '\nthis: ', this);
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to check that arrayOfFormerlyAnonymousFunctions constant does keep a valid reference to
// formerly anonymous callback function of one of the event listeners, an attempt to remove
// it is made:
setTimeout(function(delay)
{
document.removeEventListener('visibilitychange',
arrayOfFormerlyAnonymousFunctions[arrayOfFormerlyAnonymousFunctions.length - 1],
false);
console.log('\nAfter ' + delay + 'ms, an event listener 2 was removed; if reference in '
+ 'arrayOfFormerlyAnonymousFunctions value was correct, the event will not '
+ 'be listened to any more', arrayOfFormerlyAnonymousFunctions);
}, removeEventListenerAfterDelay, removeEventListenerAfterDelay);
})();
I have not seen anyone mention the use of ES6 Proxies. Which in my opinion solve this problem beautifully. So here it is.
function shadow(object, secondObject) {
return new Proxy(object, {
get(target, prop, receiver) {
if (secondObject.hasOwnProperty(prop)) return secondObject[prop];
return Reflect.get(...arguments);
}
})
}
let t=function namedFunction(a,b,c){return a+b+c;}
console.log(t.name)//read only property
let f=shadow(t,{name:"addition"})
console.log(f.name)