So, I have an element prototype function called hasClass.
Element.prototype.hasClass = function (className)
{
if (className && typeof className === "string")
{
if (this.classList)
{
return this.classList.contains(className);
}
else
{
return new RegExp(className).test("" + this.className + "");
}
}
};
Now, since I have to define multiple functions and the code will become pretty messy then I thought, why not use Object.defineProperties function; same function with the function:
Object.defineProperties(Element.prototype, {
"hasClass": {
get: function (className)
{
if (className && typeof className === "string")
{
if (this.classList)
{
return this.classList.contains(className);
}
else
{
return new RegExp(className).test("" + this.className + "");
}
}
}
}
});
The first function works fine as it should.
However, when defining same function with Object.defineProperties my Firefox console starts spamming: TypeError: $test.hasClass is not a function (Where $test is element choosen with selector). This error does not appear when using normal, first example of function, while it appears when using the lattest one.
So the question is. Why it does throw such error while it should not?
Oh yeah, funny thing is that when I use the second function with console.log like this:
Object.defineProperties(Element.prototype, {
"hasClass": {
get: function (className)
{
/*if (className && typeof className === "string")
{
if (this.classList)
{
return this.classList.contains(className);
}
else
{
return new RegExp(className).test("" + this.className + "");
}
}*/
console.log( "HI");
}
}
});
Then the console says this:
http://i.imgur.com/ULf7Ev6.png
I am confused.
You're mis-using the property get you should be using value otherwise hasClass will be the value returned from the get function.
Replace get with value
Object.defineProperties(Element.prototype, {
"hasClass": {
value: function (className)
{
/*if (className && typeof className === "string")
{
if (this.classList)
{
return this.classList.contains(className);
}
else
{
return new RegExp(className).test("" + this.className + "");
}
}*/
console.log( "HI");
}
}
});
Refer to documentation on defineProperty even though you're using defineProperties
The problem: in your version the value of hasClass will be undefined because you haven't returned anything from the get function the last statement is console.log( "HI"); if you put return {}; after that, hasClass === {}
Object.defineProperty and Object.defineProperties are not meant to define getter functions with params. In your case, just do it as in your first example (or look below, see Edit).
You can understand Object.defineProperty as a way to "intercept" the plain set/get action, but in the code where you get or set the property you can see no difference to "public" properties.
obj.abc = 'value'; // <-- set function called
console.log(obj.abc); // <-- get function called
There are no braces () - the functions are called transparently.
In case you really have properties, be careful not to run into recursive calls when the property you want to hide is actually as well public. See JS defineProperty and prototype
Edit
As I understand your question, you're looking for a less verbose way to add functions to a prototype.
Many JavaScript libraries have a way to copy some properties from one object to another:
jQuery: jQuery.extend
Underscore/Lodash: _.extend
Leaflet: L.extend
sure there are many more (and maybe a modern version of ECMAScript already has such a utility method)
Here you have a very basic extend function, if you don't have one of those libraries at hand:
function extend1(dest, src) {
var i;
for (i in src) {
if (src.hasOwnProperty(i)) {
dest[i] = src[i];
}
}
return dest;
}
Now you could extend your prototypes like this:
extend1(Element.prototype, {
hasClass: function (className) {
/* the body */
},
methodFoo: function (arg1, arg2) {},
methodBar: function () {},
});
I think, for methods this is a better way than using Object.defineProperty. For real property definitions, take Object.defineProperty.
I'm trying to create an object in JavaScript and I'm following Mozilla's tutorial . the tutorial works just fine, but when I apply that technique to my code it doesn't work. (I'm doing something wrong but I don't see it). I coded all my methods and I don't get any errors, I initialize my object and I don't get any errors either, I even call my methods and I don't get errors, but the return value is a string with my code instead of the value that I'm expecting
function JavaScriptObj(id, datatype) {
function initialize(id, datatype) {
if (typeof id === 'number' && id > 1) {
this.theID = id;
} else {
console.error("ERROR: JavaScriptObj.initialize" + id + "is NOT a valid argument");
}
if (typeof datatype === 'string') {
this.data_type = datatype;
} else {
console.error("ERROR: JavaScriptObj.initialize" + datatype + "is NOT a valid argument");
}
}
}
JavaScriptObj.prototype.getSectionName = function(){
var SectionName = "section-" + this.theID;
return SectionName;
};
var person2 = new JavaScriptObj(2, "texteditor");
alert(person2.getSectionName);
this is my jsfiddle
thanks in advance! :-)
Remove the initialize nested function:
function JavaScriptObj(id, datatype) {
if (typeof id === 'number' && id > 1) {
this.theID = id;
} else {
console.error("ERROR: JavaScriptObj: " + id + "is NOT a valid argument");
}
if (typeof datatype === 'string') {
this.data_type = datatype;
} else {
console.error("ERROR: JavaScriptObj: " + datatype + "is NOT a valid argument");
}
}
JavaScriptObj.prototype.getSectionName = function(){
var SectionName = "section-" + this.theID;
return SectionName;
};
var person2 = new JavaScriptObj(2, "texteditor");
alert(person2.getSectionName()); // need to call it too
It looks like you're not actually executing/calling your method. In order to call your method, you need to append parenthesis to the call:
alert(person2.getSectionName());
Small aside -- using console.log() instead of alert() tends to save you a few keystrokes and makes development a bit faster. Also, alert() is a blocking call that stops all other code execution on the page. While that won't make a difference when you're first starting out, it could potentially be a pain point down the road as your javascript ninja skills increase. :)
Ok, difficult to understand from the title only. Here is an example. I want a function to refer to a variable that is "injected" automagically, ie:
function abc() {
console.log(myVariable);
}
I have tried with:
with({myVariable: "value"}) { abc() }
but this doesn't work unless abc is declared within the with block, ie:
with({myVariable: "value"}) {
function abc() {
console.log(myVariable);
}
abc(); // This will work
}
So the last piece will work, but is it possible to fake the with statement, or do I have to force the developers to declare their function calls in a with statement?
Basically the call I want to do is:
doSomething({myVariable: "value"}, function() {
console.log(myVariable);
});
Ofcourse, I am aware I could pass this is a one parameter object, but that is not what I am trying to do:
doSomething({myVariable: "value"}, function(M) {
console.log(M.myVariable);
});
Further more, I am trying to avoid using eval:
with({myVariable: "value"}) {
eval(abc.toString())(); // Will also work
}
Is this not supported at at all beyond eval in Javascript?
JavaScript does not provide any straightforward way to achieve the syntax you're looking for. The only way to inject a variable into a Lexical Environment is by using eval (or the very similar Function constructor). Some of the answers to this question suggest this. Some other answers suggest using global variables as a workaround. Each of those solutions have their own caveats, though.
Other than that, your only option is to use a different syntax. The closest you can get to your original syntax is passing a parameter from doSomething to the callback, as Aadit M Shah suggested. Yes, I am aware you said you don't want to do that, but it's either that or an ugly hack...
Original answer (written when I didn't fully understand the question)
Maybe what you're looking for is a closure? Something like this:
var myVariable = "value";
function doSomething() {
console.log(myVariable);
};
doSomething(); // logs "value"
Or maybe this?
function createClosure(myVariable) {
return function() {
console.log(myVariable);
};
}
var closure = createClosure("value");
closure(); // logs "value"
Or even:
var closure = function(myVariable) {
return function() {
console.log(myVariable);
};
}("value");
closure(); // logs "value"
I asked a similar question a long time ago: Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
The short answer is no, you can't achieve dynamic scoping without resorting to eval. The long answer is, you don't need to.
JavaScript doesn't support dynamic scoping, but that's not an issue because you can make your free variables parameters of the function that they belong to.
In my humble opinion this is the best solution:
function doSomething(context, callback) {
callback(context);
}
doSomething({myVariable: "value"}, function(M) {
console.log(M.myVariable);
});
However since you don't want to write a formal parameter, the next best thing is to use this instead:
function doSomething(context, callback) {
callback.call(context);
}
doSomething({myVariable: "value"}, function() {
console.log(this.myVariable);
});
Another option would be to manipulate the formal parameter list of the program as follows:
function inject(func, properties) {
var args = [], params = [];
for (var property in properties) {
if (properties.hasOwnProperty(property)) {
args.push(properties[property]);
params.push(property);
}
}
return Function.apply(null, params.concat("return " + func.toString()))
.apply(null, args);
}
Now we can use this inject method to inject properties into a function as follows:
function doSomething(context, callback) {
var func = inject(callback, context);
func();
}
doSomething({myVariable: "value"}, function() {
console.log(myVariable);
});
See the demo: http://jsfiddle.net/sDKga/1/
Note: The inject function will create an entirely new function which will not have the same lexical scope as the original function. Hence functions with free variables and partially applied functions will not work as expected. Only use inject with normal functions.
The Function constructor is kind of like eval but it's much safer. Of course I would advise you to simply use a formal parameter or this instead. However the design decision is your choice.
Try:
function doSomething(vars, fun) {
for (var key in vars) { // set the variables in vars
window[key] = vars[key];
}
fun.call(); // call function
for (var key in vars) { // remove the variables again. this will allow only the function to use it
delete window[key];
}
}
Set global variables that can then be received inside of fun
The JSFiddle: http://jsfiddle.net/shawn31313/MbAMQ/
Warning: disgusting code ahead
function callWithContext(func, context, args) {
var oldProperties = {};
for(var n in context) {
if(context.hasOwnProperty(n)) {
var oldProperty = Object.getOwnPropertyDescriptor(self, n);
oldProperties[n] = oldProperty;
(function(n) {
Object.defineProperty(self, n, {
get: function() {
if(arguments.callee.caller === func) {
return context[n];
}
if(!oldProperty) {
return;
}
if(oldProperty.get) {
return oldProperty.get.apply(this, arguments);
}
return oldProperty.value;
},
set: function(value) {
if(arguments.callee.caller === func) {
context[n] = value;
}
if(!oldProperty) {
return;
}
if(oldProperty.set) {
return oldProperty.get.apply(this, arguments);
} else if(!oldProperty.writable) {
var fakeObject = {};
Object.defineProperty(fakeObject, n, {value: null, writable: false});
fakeObject[n] = value; // Kind of stupid, but…
return;
}
oldProperty.value = value;
}
});
})(n);
}
}
func.apply(this, args);
for(var n in context) {
if(context.hasOwnProperty(n)) {
if(oldProperties[n]) {
Object.defineProperty(self, n, oldProperties[n]);
} else {
delete self[n];
}
}
}
}
This is vomitously horrendous, by the way; don’t use it. But ew, it actually works.
i don't see why you can't just pass the info in or define a single global, but i think that would be best.
that said, i am working on a Module maker/runner that allows sloppy/dangerous code to execute without interference to the host environment. that provides the opportunity to re-define variables, which can be passed as an object.
this does use eval (Function() technically) but it can run in "use strict", so it's not too crazy/clever.
it doesn't leave behind artifacts.
it also won't let globals get hurt.
it's still a work in progress, and i need to iron out a couple minor details before i vouch for security, so don't use it for fort knox or anything, but it's working and stable enough to perform the operation asked for.
tested in ch28, FF22, IE10:
function Module(strCode, blnPreventExtensions, objWhitelist, objExtend) {
var __proto__=self.__proto__, pbu=self.__proto__, str=strCode, om=[].map, wasFN=false,
params = {Object:1}, fnScrubber, natives= [ Object, Array, RegExp, String, Boolean, Date] ,
nativeSlots = [],
preamble = "'use strict';" ,
inherited="__defineGetter__,__defineSetter__,__proto__,valueOf,constructor,__lookupGetter__,__lookupSetter__",
late = inherited +
Object.getOwnPropertyNames(__proto__||{}) + Object.getOwnPropertyNames(window);
late.split(",").sort().map(function(a) {
this[a] = 1;
}, params);
preamble+=";var "+inherited+";";
//turn functions into strings, but note that a function was passed
if(str.call){wasFN=true; str=String(str); delete params.Object; }
objExtend=objExtend||{};
var vals=Object.keys(objExtend).map(function(k){ return objExtend[k]; })
// build a usable clone of Object for all the new OOP methods it provides:
var fakeOb=Object.bind();
(Object.getOwnPropertyNames(Object)||Object.keys(Object)).map(function(a){
if(Object[a] && Object[a].bind){this[a]=Object[a].bind(Object); } return this;
},fakeOb)[0];
//allow "eval" and "arguments" since strict throws if you formalize them and eval is now presumed safe.
delete params.eval;
delete params.arguments;
params.hasOwnProperty=undefined;
params.toString=undefined;
params['__proto__']={};
__proto__=null;
Object.keys(objWhitelist||{}).map(function ripper(a,b){
b=this[a];
if(typeof b!=='object'){
delete this[a];
}
}, params);
// var ok=Object.keys.bind(Object);
// prevent new prototype methods from being added to native constructors:
if (blnPreventExtensions) {
natives.forEach(function(con, i) {
var proto=con.prototype;
Object.getOwnPropertyNames(proto).map(function(prop){
if(proto[prop] && proto[prop].bind ){ this[prop]=proto[prop];}
}, nativeSlots[i] = {});
delete con.constructor;
delete con.prototype.constructor;
}); //end con map()
} /* end if(blnPreventExtensions) */
//white-list harmless math utils and prevent hijacking:
delete params.Math;
if(blnPreventExtensions){Object.freeze(Math);}
//prevent literal constructors from getting Function ref (eg: [].constructor.constructor, /./.constructor.constructor, etc...):
Function.prototype.constructor = null;
try {
//generate a private wrapper function to evaluate code:
var response = Function(
Object.keys(objExtend) + (vals.length?",":"") +
Object.keys(params).filter(/./.test, /^[\w\$]+$/), // localize most globals
preamble + " return " + str.trim() // cram code into a function body with global-blocking formal parameters
);
// call it with a blank this object and only user-supplied arguments:
if (blnPreventExtensions) { //( user-land code must run inside here to be secure)
response = response.apply({}, vals.concat(fakeOb)).apply({}, [].slice.call(arguments,4) );
}else{
response = response.apply({}, vals.concat(fakeOb));
}
} catch (y) {
response = y + "!!";
} /* end try/catch */
if (blnPreventExtensions) {
om.call(natives, function(con, i) {
var pro=con.prototype;
//remove all proto methods for this con to censor any additions made by unsafe code:
Object.getOwnPropertyNames(pro).map(function(a){ try{delete pro[a];}catch(y){}});
//restore all original props from the backup:
var bu = nativeSlots[i];
om.call(Object.keys(bu), function(prop){ con.prototype[prop]=bu[prop]; }, bu);
}); //end con map()
} /* end if(blnPreventExtensions) */
//restore hidden Function constructor property:
Function.prototype.constructor = Function;
return response;
} /* end Module() */
/////////////////////////////////////////////////////////////
function doSomething(context, fn){
console.log(myVariable);
return myVariable;
}
//use 1:
alert( Module(doSomething, true, {console:1}, {myVariable: "value123"} ) );// immed
//use2:
var fn=Module(doSomething, false, {console:1}, {myVariable: "value123"} );// as function
alert(fn);
alert(fn());
again, i think OP would be best off not doing things later than need be, but for the sake of comprehensiveness and inspiration i'm putting this out there in good faith.
You need to use call() to construct a context, as in:
var f=function(){
console.log(this.foo);
};
f.call({foo:'bar'})
will print "bar"
You can avoid using eval() in calling the function, if you are willing to use it in doSomething():
function abc() {
console.log(myVariable);
}
// Prints "value"
callWith({ myVariable: "value" }, abc);
function callWith(context, func) {
for(var i in context) eval('var ' + i + ' = context[i];');
eval('(' + func.toString() + ')')();
}
Have a look at this post.
Have a look at goog.partial, scroll a little bit up to see the description of what it does:
Here is an implementation of it:
var b = goog.partial(alert, 'Hello world!');
b();//alerts "Hello world!"
In the example it passes the function alert with parameter "Hello world!" but you can pass it your own function with multiple parameters.
This allows you to create a variable that points to a function that is always called with a certain paramater. To use parameters in a function that are not named you can use arguments:
function test(){
console.log(arguments);//["hello","world"]
}
test("hello","world");
I have an array of objects called targets and I want to execute a function on each of those objects. The first method:
targets.each(function() {
if (needScrollbars($(this))) {
wrap($(this), id);
id = id + 1;
}
});
This method gives execution speed of ~125ms. The second method is:
var i=0;
while (targets[i] != undefined) {
if (needScrollbars($(this))) {
wrap($(this), id);
id = id + 1;
}
i = i+1;
}
This second method takes whopping 1385ms to execute and I get my head around that. Does anyone have any idea why a bare bones cycle runs slower than a function which I'm only guessing that's doing (just guessing) a whole lot more than a simple cycle?
Thank you.
They are totally different. The this in the first example is the current target, in the second example this is the "external" this. You should change the second example as:
var i=0;
while (targets[i] != undefined) {
var cur = $(targets[i]);
if (needScrollbars(cur)) {
wrap(cur, id);
id = id + 1;
}
i = i+1;
}
The relevant quote
More importantly, the callback is fired in the context of the current DOM element, so the keyword this refers to the element.
But I don't know why you haven't written as:
for (var i = 0; i < targets.length; i++)
{
var cur = $(targets[i]);
if (needScrollbars(cur)) {
wrap(cur, id);
id = id + 1;
}
}
And in the end the each "method" is easier to comprehend (for me).
Your second method is not functionally equivalent to the first one.
Why? Because it uses this, making it a closure on the global scope. Of course the second method is slower: it continuously shells out jQuery objects made out of global scope. Try that benchmark again with:
var i=0;
while (targets[i] !== undefined) {
var o = $(targets[i]);
if (needScrollbars(o)) {
wrap(o, id);
id++;
}
i++;
}
How do I constantly check a variables value. For example:
if(variable == 'value'){
dosomething();
}
This would work if I constantly looped it or something, but is there an efficient way of triggering that as soon as the variable is set to that value?
This solution use deprecated APIs. Computed properties and proxies are a better alternative except on the oldest browsers. See K2Span's answer for an example of how to use those.
Object.watch:
Watches for a property to be assigned a value and runs a function when that occurs.
Object.watch() for all browsers? talks about cross-browser ways to do Object.watch on browsers that don't support it natively.
Object.defineProperty(Object.prototype, 'watch', {
value: function(prop, handler){
var setter = function(val){
return val = handler.call(this, val);
};
Object.defineProperty(this, prop, {
set: setter
});
}
});
How to use:
var obj = {};
obj.watch('prop', function(value){
console.log('wow!',value);
});
obj.prop = 3;
Use setInterval:
var key = ''
setInterval(function(){
if(key == 'value'){
dosomething();
}
}, 1000);
As #Pekka commented, you can have a timer constantly poll the variable. A better solution, if it's all your code that's changing the variable, is to not just set the variable directly, but rather have all setters call a function. The function could then set the variable and do any additional processing you need.
function setValue(value) {
myVariable = value;
notifyWatchers();
}
If you encapsulate your variable so that the value can only be set by calling a function, it gives you the opportunity to check the value.
function ValueWatcher(value) {
this.onBeforeSet = function(){}
this.onAfterSet = function(){}
this.setValue = function(newVal) {
this.onBeforeSet(value, newVal)
value = newVal;
this.onAfterSet(newVal)
}
this.getValue = function() {
return value;
}
}
var name = new ValueWatcher("chris");
wacthedName.onBeforeChange = function(currentVal, newVal) {
alert("about to change from" + currentVal + " to " + newVal);
}
name.setValue("Connor");
I had a similar problem and was able to eliminate it using a timed function that I had used earlier. Even if you don't have a timed function there are easy to create;
var rand = 0
setInterval(function senseConst () {if (rand = 0) {rand = x}, 10);
//x could be equal to the variables value that you want to check
I used this to constantly have a variable that is the length of the page by making using the following code
var widthSensorOutput = 0
setInterval(function senseConst () {if (widthSensorOutput = 0) {widthSensorOutput = document.getElementById('widthSensor').clientWidth}, 10);
//'widthSensor' is a div with the width equal to 100%
I am not sure if this is the best way to solve your problem but it worked for me. To be clear, this is the the same basic code that Chandu gave, just I added what you should do once you are inside the function, which already is pretty obvious. I did not understand Chandu's post and did not realize that they used the same root code.