I've noticed that when using bind on an object, you lose the ability to access the prototype.
function Foo(obj) {
this.fields = obj;
}
function Make(obj) {
return Foo.bind(Foo, obj);
}
var Test = Make({
name: 'Jeff'
});
console.log(Test.prototype);
Here's the same example not using bind:
function Foo(obj) {
this.fields = obj;
}
function Make(obj) {
return Foo;
}
var Test = Make({
name: 'Jeff'
});
console.log(Test.prototype);
Are there any ways around this?
So judging by the docs. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Bind ties a function to a specific object so that variables inside that object are always used. so when you're calling console.log in your example you're logging the prototype of the function Test. Not the object Test. So you get undefined.
In order to get the object you want try this:
console.log(new Test())
Related
I have a simple object and constructor in Traditional JavaScript as follows:
function Test() {
this.data={};
}
Test.prototype={
set value(something) {
},
get value() {
return data[property];
}
};
var test=new Test();
Inside the object is another object, initially with no own properties.
I would like to write a Setter for value which sets a property on the inner object (data).
test.value.whatever=23;
Is there any way I can do this?
I expect that the Setter function could then finish the job with something like this:
set value() {
// how do I get property & value?
data[property]=value;
},
Here you go
function Test() {
this.data = {};
}
Test.prototype={
set value(v) {
this.data.whatever = v;
},
get value() {
return this.data.whatever;
}
};
var test = new Test();
test.value = 'hi';
console.log(test.value) // 'hi'
console.log(test.data) // {whatever: 'hi'}
Remember to put the data property in the constructor. Otherwise, if you put it into the prototype, it will be a shared property among every instance.
OK, I have an answer: as suggested by #Bergi & #damianmr, I used (and had to learn about) a Proxy object.
Here is a working version:
function Test() {
this.dataStore={};
this.data=new Proxy(this,{
set(target,property,value) {
target.dataStore[property]=value;
},
get(target,property) {
return target.dataStore[property];
}
});
}
Test.prototype={
};
var test=new Test();
test.data.whatever=23;
test.data.etc=46;
alert(`${test.data.whatever} ${test.data.etc}`);
As you can see:
I have an object called dataStore, and a proxy called data
The Proxy is set in the constructor
This is a simple abstracted case, but it also works for the more complex project I’m working on.
The only shortcoming is that IE doesn’t support Proxy, and the polyfills I have seen don’t like new properties.
That just means I will need to supply a functional alternative for Legacy Browsers, and wait for them to die out …
I try to override a method and script is:
function wrapper(target) {
target.doABC = function () {
alert('in wrapper');
};
return target;
}
function Model() {
wrapper(this);
}
Model.prototype.doABC = function () {
alert('in Model');
};
var a = new Model();
a.doABC();
The result is 'in wrapper'. I don't know why?
Any JavaScript object has own and inherited properties. Own are those defined directly on the instance and inherited are taken from the prototype object.
When using a property accessor, JavaScript first searches in object's own properties list. If the property is not found, it searches in object's prototype chain.
In your example, the wrapper() method defines on object instance an own property doABC, which is a function that alerts 'in wrapper'. Even if the object has a prototype with the same property doAbc that alerts 'in Model', JavaScript anyway will use the own property.
function wrapper(target) {
// Define an own property "doABC"
target.doABC = function () {
alert('in wrapper');
};
return target;
}
function Model() {
wrapper(this);
}
// Define an inherited property "doABC"
Model.prototype.doABC = function () {
alert('in Model');
};
var a = new Model();
//Use the own property "doABC". The inherited "doABC" is ignored.
a.doABC();
As an addition, the own property can be removed using delete operator. After deletion, the object will use the inherited property.
// delete the own property "doABC"
delete a['doABC'];
// the inherited "doABC" will be used. Alerts "in Model"
a.doABC();
Check the complete working demo.
Let me see if I can explain:
You have two separate versions of doABC here.
Your target.doABC creates a function specific to that instance of your Model and each Model get its own doABC.
Because Model has a doABC, the JavaScript engine has no need to look 'up the chain' for something else, hence it will never look for the Model.prototype.doABC version.
You can see this by adding these lines:
Model.prototype.doXYZ = function () {
alert('in Model');
};
and calling
a.doXYZ();
Since a doesn't have its own doXYZ then, and only then, will it look up the chain and see the method in the prototype.
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.
When trying to test prototype functionality, I got this odd result:
Here is my first test:
<script>
function Hello() {
}
var a = new Hello();
Hello.prototype.name = "Fred";
alert(a.name);
</script>
And, here's the second one:
<script>
function Hello() {
}
var a = new Hello();
Hello.prototype = {
name : "Fred",
}
alert(a.name);
</script>
I can't understand why the first will return a alert with "Fred" and the second is "undefined" though these mean the same thing?
Could you help me with it?
Thank you.
When you define a function in JavaScript, the interpreter makes a special prototype property available on the function, which points to an object, in case you use that function as a constructor. The [[Prototype]] internal property points to this object when you create a new object using the constructor.
When you replace the prototype property with a new one, you are replacing that reference, and if you do it after you instantiate an object, you will find the prototype object appears to be stale (that object's [[Prototype]] is pointing to the original object that prototype pointed to).
Solutions
Only assign new properties directly on the prototype property.
var constructor = function() { };
constructor.prototype.someMethod = function() { };
Use an extend type function to extend the existing prototype property with your new object (in this example, I used Underscore's extend() function).
var constructor = function() { };
_.extend(constructor.prototype, { someMethod: function() { } });
Make sure after the constructor, assigning the prototype property is the very next step in your program (generally not recommended).
var constructor = function() { };
constructor.prototype = { someMethod: function() { } };
Your ordering is messed up. You need assign the object to the prototype before using the new operator:
function Hello() {
}
Hello.prototype = {
name : "Fred",
}
var a = new Hello();
alert(a.name);
Demo.
The two code snippets are not actually equal.
In the first script you only override Hello.prototype.name, while in the second script you override the whole content of Hello.prototype.
I am creating an AJAX API for a web service and I want to be able to call jQuery-like accessors.
jQuery seems to be able to execute 'jQuery' as a function, but also use it to directly access the object that is the result of the function EG:
jQuery();
jQuery.each({});
This is the trick that I can't seem to pull off:
myAPI('foo'); //output: 'foo'
myAPI('foo').changeBar(); //output: 'foo' 1
myAPI.changeBar(); //Error: not a function
I have seen the answers to similar questions, which are helpful, but don't really answer my question.
#8734115 - Really interesting, but you can't access the methods that were set by f.prototype.
#2953314 - Uses Multiple operations to create object instead of a single function.
here is my code:
(function(window) {
var h = function(foo) {
// The h object is actually just the init constructor 'enhanced'
return new h.fn.init(foo);
};
/**
* Methods defined at protoype.
*/
h.fn = h.prototype = {
constructor: h,
init: function(foo) {
console.log(foo);
return this;
},
splice : function () {},
length : 0,
bar : 0,
changeBar : function() {
this.bar++;
return this.bar;
}
};
h.fn.init.prototype = h.fn;
//Publish
window.myAPI =h;
}( window));
I'm sure I'm missing something simple :(
What jQuery is doing there is using jQuery as both a function and as a pseudo-namespace. That is, you can call jQuery: var divs = jQuery("div"); and you can use properties on it, e.g.: jQuery.each(...);.
This is possible because in JavaScript, functions are first-class objects, and so you can add arbitrary properties to them:
function foo() {
alert("Foo!");
}
foo.bar = function() {
alert("Bar!");
};
foo(); // "Foo!"
foo.bar(); // "Bar!"
That's literally all there is to it.
Within the call to bar, this will be the foo function (because this is determined entirely by how a function is called, not where it's defined). jQuery doesn't use this to refer to itself (usually it uses this to refer to DOM elements, sometimes to other things like array elements; when referring to itself, since it's a single thing, it just uses jQuery).
Now, you might want to ensure that your functions have proper names (whereas the function I assigned to bar above is anonymous — the property has a name, but the function does not). In that case, you might get into the module pattern:
var foo = (function() {
function foo() {
alert("Foo!");
}
function foo_bar() {
alert("Bar!");
}
foo.bar = foo_bar;
return foo;
})();
foo(); // "Foo!"
foo.bar(); // "Bar!"
That pattern also has the advantage that you can have private data and functions held within the scoping function (the big anonymous function that wraps everything else) that only your code can use.
var foo = (function() {
function foo() {
reallyPrivate("Foo!");
}
function foo_bar() {
reallyPrivate("Bar!");
}
function reallyPrivate(msg) {
alert(msg);
}
foo.bar = foo_bar;
return foo;
})();
foo(); // "Foo!"
foo.bar(); // "Bar!"
reallyPrivate("Hi"); // Error, `reallyPrivate` is undefined outside of the scoping function
In your code, you're assigning things to the prototype property of the function. That only comes into play when the function is called as a constructor function (e.g., via new). When you do that, the object created by new receives the function's prototype property as its underlying prototype. But that's a completely different thing, unrelated to what jQuery does where it's both a function and a pseudo-namespace.
You do not need any of that weirdness, to use stuff like $.each
you just attach functions to the function object instead
of the prototype object:
function Constructor() {
if (!(this instanceof Constructor)) {
return new Constructor();
}
}
Constructor.prototype = {
each: function() {
return "instance method";
}
};
Constructor.each = function() {
return "static method";
};
var a = Constructor();
a.each(); //"instance method"
Constructor.each(); //"static method"