Overwrite Javascript prototype with stub functions - javascript

I'm trying to run an A/B test for a new feature I'm adding to a website. In the past, our team has done something like this before showing various features on the page:
someUserActionThatEnablesFeature:function(){
experiments.isUserInControlGroup('new-feature1') && newFeature1Obj.show();
}
someUserActionThatDisablesFeature:function(){
experiments.isUserInControlGroup('new-feature1') && newFeature1Obj.hide();
}
I have found this to be pretty kludgey, since we have to check if the experiment is enabled in every place we use the new feature. What I was thinking about doing instead something like this:
function NewFeature1(){
//constructor
}
NewFeature1.prototype = {
show:function(){
//do something
},
hide:function(){
//do something
},
//etc
};
//before any objects are declared
if (experiments.isUserInControlGroup('new-feature1')) {
for(var prop in NewFeature1.prototype) {
//replace with empty function
NewFeature1.prototype[prop] = function(){};
}
}
The idea here is that I'm replacing the NewFeature1 class's methods with empty stubs before I ever instantiate it, thereby ensuring that none of my calls on the object will ever do anything if the user isn't in the control group.
Is this dangerous in some browsers? I couldn't tell in my initial tests if I was overwriting Object's properties. In webkit, it didn't seem like it was hurting anything. Am I missing something? I only have to worry about webkit, FF, and IE8+.
Thanks!

I think it's acceptable, maybe better to stub only direct properties determined by hasOwnProperty if the class has no inherits.

Related

Deleting a property of an object inside its definition; Why?

Looking at the recent Google Maps API loader source, I'm wondering what is the purpose of the following:
google.maps.Load = function(apiLoad) {
delete google.maps.Load;
...
Why would you delete a property of an object, inside its definition? I suspect it could have some performance increase, but can't figure out how a property can delete itself inside its definition.
Obviously we can only make assumptions since it's only that code author can say for sure.
If the reason was to ensure that the Load procedure is performed just once then the decision chosen is really poor.
The problem is that deletion of properties makes impossible V8 (and may be other engines) to use so called "hidden classes" (which is an optimisation method for faster object's properties lookup).
The better alternative would be
google.maps.Load = function() {};
or
google.maps.Load = function() { throw new Error("Already loaded") };
as suggested by #Sam in the comments.
References:
Understanding hidden classes in v8
Fast Property Access
I'd say to only allow to be loaded once.
This is to ensure that the API is only loaded once. However, this will not throw a useful error when the function is called a second time, but it may cause an exception.
Here is an alternative solution which throws a more useful error.
google.maps.Load = function() { throw new Error("Already loaded") };

Is it possible to assign function hooks to game objects, e.g. run function on init, death, etc?

I haven't found any way to assign hooks to anything, and if I could it would be very useful. Also, how can I check whether a game object still exists? (i.e. hasn't died of age, and wasn't destroyed by an enemy.)
If you mean via API - not possible yet. But you can change your current state and compare it to memorized previous state of the objects. For example, if some creep name in Memory still presents, but it is gone in Game.creeps, then something happened.
for(var i in Game.creeps) {
var creep = Game.creeps[i];
creep.memory.lastSeenAt = {x: creep.pos.x, y: creep.pos.y};
}
for(var i in Memory.creeps) {
if(!Game.creeps[i]) {
console.log("Oops! Something happened with a creep "+creep.name+" at "+
Memory.creeps[i].lastSeenAt.x+","+Memory.creeps[i].lastSeenAt.y);
}
}
Here is my use case :
Count the number of hostiles (N) in the current room
Count the number of alive guards (C)
If C < N then build another guard
After a while, when using the Room.find(Game.MY_CREEPS), I'll get dead guards aswell. Having to filter them all the time is really painfull, and the global Memory continues to list them. Is there a way to remove dead creeps from the global Memory object ?
[EDIT]
Found this, hope it will help.
for(var i in Memory.creeps) {
if(!Game.creeps[i]) {
delete Memory.creeps[i];
}
}
I run it at the beginning of each tick
I forked the script-samples repository and made my own events handling code - https://github.com/pineapplemachine/script-samples/tree/master/hooks
Using that script you can assign initialization, update, and destruction methods to events rather than having to handle things more obtusely.
I don't think so. The API seems to be focused on developing code for the main game loop, like writing code inside a while(true). But you can make your creeps do something before they die, for example.
I've created a guard module (just like the harvester module you create on the tutorial). The code below should work:
module.exports = function (creep) {
if(creep.hits<=100) { Game.spawns.Spawn1.createCreep([Game.ATTACK, Game.ATTACK, Game.TOUGH, Game.TOUGH, Game.MOVE], "guard2", {role:"guard"}); }
var targets = creep.room.find(Game.HOSTILE_CREEPS);
if(targets.length) {
creep.moveTo(targets[0]);
creep.attack(targets[0]);
} else{
creep.moveTo(Game.spawns.Spawn1);
}
}

How to create a well formed global javascript object containing special functions

I am creating a small project that heavily relies on JavaScript. I come from php/mysql and now stepping into node.js/javascript/mongodb, and I hve to say it's quite a mindswitch.
I want to create a simple object that has some special function that I can use in the page. I have been looking at some tutorial, and looking at the libraries such as jquery and backbone, but I need some final advice on my decision.
I only need some small functions, and no cross-browser support, that's why I don't choose something like backbone. Maybe ill change to that later when I have a better crasp on JavaScript programming.
What is confusing me is whether to use the new, or maybe wrapping the code into a self-invoking function.
I see jquery creates an object inside the window and than exposes that, but I have no idea how that works.
Enough intro, now to the point. I have created something like this:
var $s = Object.create({
page: Object.create({
title: 'pagetitle',
html: '',
data: {},
render: function(){
// Basic render function
}
}),
socket: Object.create({
// My websocket connection
}),
store: function(key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
retrieve: function(key) {
var value = localStorage.getItem(key);
return value && JSON.parse(value);
},
slugify: function(slug){
return slug.replace(/[^a-zA-Z 0-9-]+/g,'').toLowerCase().replace(/ /g,'-');
}
});
This are just a few random functions I put in.
I haven't tested this yet, it is a draft, I want to know if this is any good.
Now I was thinking i can do some stuff like this:
$s.page.html = 'somehtml';
$s.page.render();
// Maybe
$s.store( $s.page.title, $s.page.html );
I do use jQuery and jQuery templating, so something like this could be possible:
$.tmpl( $s.page.html, $s.page.data ).appendTo( "#content" );
Nothing fancy is needed here. You can create a global javascript object with a method like this:
var myGlobalObject = {};
myGlobalObject.testFunction = function() {
// put your code here
};
You can then call that like this:
myGlobalObject.testFunction();
One slightly more flexible design pattern you will often seen used is this:
var myGlobalObject = myGlobalObject || {};
myGlobalObject.testFunction = function() {
// put your code here
};
This is used when there might be lots of different pieces of code contributing to myGlobalObject and they all want to make sure that it's properly declared before adding properties to it. This way of doing it, creates it if it doesn't already exist and if it does already exist, leaves the methods and properties on it that might already be there. This allows multiple modules to each contribute initialization to myGlobalObject without regards for the order they load.

"Error calling method on NPObject!" in Uploadify

I'm using Uploadify to upload file in my CMS. Everything works fine until recently. I got an error
Error calling method on NPObject
on this line
document.getElementById(jQuery(this).attr('id') + 'Uploader').startFileUpload(ID, checkComplete);
on this part
uploadifyUpload:function(ID,checkComplete) {
jQuery(this).each(function() {
if (!checkComplete) checkComplete = false;
document.getElementById(jQuery(this).attr('id') + 'Uploader').startFileUpload(ID, checkComplete);
});
},
I don't know why and after a day debugging and testing I found that if I remove replace(/\&/g, '\\&') from
String.prototype.escAll = function(){
var s = this;
return s.replace(/\./g, '\\.').replace(/\?/g, '\\?').replace(/\&/g, '\\&');
};
It then works again. I really don't know why.
Any helps would be appreciated!
I think the reason is in additional Javascript libraries you use.
Some libraries (for example Prototype.js or jQuery.js) change behaviour of your code. For example, you can't overload prototype in some cases. The result may be undefined in clear (obvious) places (like you use an array variable with wrong index). You should view the source code of additional libraries, probably they do with prototype something that breaks your code in the function you mentioned.
In my practice I had the situation when overloading of prototype worked incorrectly (it was String prototype like in your case).
So just don't use prototype.

Failure to override Element's addEventListener in Firefox

I'm trying to override the Element object's addEventListener method, in a cross-browser manner. The purpose is so that I can load some 3rd party scripts asynchronously, and those scripts call this method prematurely.
I created an HTML file that works perfectly in Chrome, but on Firefox I get this exception:
"Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)"
If you comment out the lines in the file that change the INSTANCE methods, it works. But I need to do it on the "class type" (i.e. prototype).
Any suggestions would be appreciated.
Thanks,
Guypo
Here's the file I created
<html><body>
<img id="testImg" src="http://www.blaze.io/wp-content/themes/Blaze/images/header_logoB.png">
<script>
function myLog(msg) { "undefined" != typeof(console) && console.log("Log: " + msg); }
function customListener(type, event, useCapture) {
// Register the event
myLog('Registering event');
this._origListener.apply(this, arguments);
}
// Also tried HTMLImageElement
Element.prototype._origListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = customListener;
var img = document.getElementById("testImg");
// Uncommenting these lines works - but in the real case I can't access these objects
//img._origListener = img.addEventListener;
//img.addEventListener = customListener;
img.addEventListener('load',function() { myLog('load callback'); }, false);
</script>
</body>
</html>
Like Marcel says, this has to do with the way host objects are exposed. The problem is that if you override a predefined property of an interface, it doesn't get changed in the interfaces that inherit from it (at least in Firefox).
Although I agree with Marcel's remarks, there is in fact a way to do this. You should override the property of the lowest possible interface of the object you want to do this for. In your case this would be the HTMLImageElement.
This will do the trick:
(function() {
var _interfaces = [ HTMLDivElement, HTMLImageElement /* ... (add as many as needed) */ ];
for (var i = 0; i < _interfaces.length; i++) {
(function(original) {
_interfaces[i].prototype.addEventListener = function(type, listener, useCapture) {
// DO SOMETHING HERE
return original.apply(this, arguments);
}
})(_interfaces[i].prototype.addEventListener);
}
})();
Please use a valid Doctype, preferably one that cast browsers into (Almost) Standards mode (<!DOCTYPE html> is fine).
typeof is an operator, you don't need the parentheses (but it doesn't hurt)
Most important: don't try to manipulate DOM Element's prototype: those are host objects and you can't rely on host objects exposing any of the core JavaScript functionality of an object. Worse, there may be a performance penalty and you might break other things. Stay away from that technique. For more information, read What’s wrong with extending the DOM.
What is it that you want to achieve, that you want your event listeners to be called automatically when using 3rd party scripts?

Categories

Resources