Why don't instance methods work as expected? - javascript

(function() {
window.gArr = new ExtArray();
})();
function ExtArray() {
this.bounce = function() {
document.write("Bounced successfully!");
};
}
ExtArray.prototype = new Array;
ExtArray.prototype.first = function() {
document.write(this[0]);
}
var eArr = new ExtArray();
//all three work
eArr.bounce();
eArr.push("I am first! ");
eArr.first();
// invoking global
gArr.bounce(); // it works
gArr.push("No, it is me who is really first! "); // doesn't work
gArr.first(); // doesn't work
Why doesn't it work?

> (function() {
> window.gArr = new ExtArray(); })();
Why is that preferred to just:
var gArr = new ExtArray();
they are functionally identical (unless there is no window object in which case the first will fail);
> function ExtArray() {
> this.bounce = function() {
> document.write("Bounced successfully!");
> }; }
Using document.write after the page has finished loading will first clear the entire document (i.e. everything between the HTML tags) and write whatever is passed to the function (in this case, a two word string).
> ExtArray.prototype = new Array;
> ExtArray.prototype.first = function() {
> document.write(this[0]);
> }
As above, document.write is destructive.
> var eArr = new ExtArray();
> //all three work
> eArr.bounce();
> eArr.push("I am first! ");
> eArr.first();
Presumably this is running before the load event then.
> // invoking global
> gArr.bounce(); // it works
> gArr.push("No, it is me who is really first! "); // doesn't work
> gArr.first(); // doesn't work
The bits that "don't work" are because you initialised gArr before you modified ExtArray.prototype, so it has the instance method bounce but still has the default prototype when the constructor was declared.
Remember that once declarations are done, the code runs in sequence so gArr = new ExtArray() runs before ExtArray.prototype = new Array; and so on.
Also, an instance has an internal prototype property that references the constructor's prototype at the instant it was created and can't be changed afterward (except for Mozilla's deprecated proto property). So changing the contsructor's prototype doesn't change the internal prototype of any instances that have already been constructed.

You should define window.gArr after you define ExtArray.prototype:
function ExtArray() {
this.bounce = function() {
document.write("Bounced successfully!");
};
}
ExtArray.prototype = new Array;
ExtArray.prototype.first = function() {
document.write(this[0]);
}; // <-- semicolon will be REQUIRED here.
(function() {
window.gArr = new ExtArray();
})();
DEMO: http://jsfiddle.net/Vg3Ze/

Related

Javascript Scope - including without passing or making global

I'm working on some script for a set of functions that all operate from one call and take a large number of parameters to return one value. The main function requires the use of 11 other functions which need to work with the same parameters. I have it structured somewhat like this:
function mainfunction(param1, param2, ..., param16)
{
//do a bunch of stuff with the parameters
return output;
}
function secondaryfunction1()
{
//gets called by mainfunction
//does a bunch of stuff with the parameters from mainfunction
}
Is there anything I can do to make the parameters passed to mainfunction available to all the secondary functions without passing them or making them global variables? If not, that's fine, I'll pass them as parameters - I'm curious as to whether or not I can do it more elegantly.
You can place the definition of secondaryfunction1 inside mainfunction:
function mainfunction(param1, param2, ..., param16){
function secondaryfunction1() {
// use param1, param2, ..., param16
}
secondaryfunction1();
}
Update:
As #dystroy pointed out, this is viable if you don't need to call secondaryfunction1 somewhere else. Where the list of parameters would be coming from in this case - I don't know.
You could use arguments to pass to secondaryFunction1 all the arguments of mainfunction. But that would be silly.
What you should probably do, and what is usually done, is embed all the parameters in an "options" object :
function mainfunction(options){
secondaryfunction1(options);
}
function secondaryfunction1(options) {
// use options.param1, etc.
}
// let's call it
mainfunction({param1: 0, param2: "yes?"});
This leds to other advantages, like
naming the parameters you pass, it's not a good thing for maintenance to have to count the parameters to know which one to change. No sane library would let you pass 16 parameters as direct unnamed arguments to a function
enabling you to pass only the needed parameters (the other ones being default)
#Igor 's answer (or some variation) is the way to go. If you have to use the functions elsewhere, though (as #dystroy pointed out), then there is another possibility. Combine your parameters together into an object, and pass that object to the secondary functions.
function combineEm() {
// Get all parameters into an array.
var args = [].slice.call(arguments, 0),
output = {},
i;
// Now put them in an object
for (i = 0; i < args.length; i++) {
output["param" + i] = args[i];
}
return output;
}
From your main function, you can do:
function mainfunction(param1, param2, ..., param16) {
var params = combineEm(param1, param2, ..., param16);
var output = secondaryfunction(params);
// etc.
return output;
}
Edit: I just wanted to clarify that all of the proposed suggestions so far do work. They just each have their own trade-offs/benefits.
I tried just suggesting some changes to other answers, but ultimately I felt like I needed to just post my solution to this.
var externalFn = function(options) {
var str = options.str || 'hello world';
alert(str);
};
var main = function(options) {
var privateMethod = function() {
var str = options.str || "foobar";
alert("str: " + str);
};
// Bind a private version of an external function
var privateMethodFromExternal = externalFn.bind(this, options);
privateMethod();
privateMethodFromExternal();
};
main({ str: "abc123"});
// alerts 'str: abc123'
// alerts 'abc123'
main({});
// alerts 'str: foobar'
// alerts 'hello world'
It seems like the main point of the question is that the functions used by the 'main function' shouldn't have to keep having the options/context passed to them.
This example shows how you can use privateMethods inside the function
It also shows how you can take external functions (that you presumably use outside of main) and bind a private method version of them for use inside main.
I prefer using some sort of 'options' object, but that aspect isn't really that important to the question of scoping that the OP was really asking about. You could use 'regular' parameters as well.
This example can be found on codepen.
Here's an incredibly naughty solution, if you're interested in that sort of thing.
var f1 = function() {
var a = 1;
var _f2 = f2.toString().replace(/^function[^{}]+{/, '');
_f2 = _f2.substr(0, _f2.length - 2);
eval(_f2);
}
var f2 = function(a) {
var a = a || 0;
console.log(a);
}
f2(); // logs 0
f1(); // logs 1
It executes the contents of some external function entirely in the current scope.
However, this sort of trickery is almost definitely an indicator that your project is mis-organized. Calling external functions should usually be no more difficult than passing an object around, as dystroy's answer suggests, defining the function in-scope, as Igor's answer suggests, or by attaching some external function to this and writing your functions primarily against the properties of this. Like so:
var FunLib = {
a : 0,
do : function() {
console.log(this.a);
}
}
var Class = function() {
this.a = 1;
this.do = FunLib.do;
this.somethingThatDependsOnDo = function() {
this.a++;
this.do();
}
}
var o = new Class();
FunLib.do() // 0
o.do() // 1
o.somethingThatDependsOnDo(); // 2
o.do() // 2 now
Similarly, and possibly better-solved with a class hierarchy.
function BasicShoe {
this.steps_taken = 0;
this.max_steps = 100000;
this.doStep = function() {
this.steps_taken++;
if (this.steps_taken > this.max_steps) {
throw new Exception("Broken Shoe!");
}
}
}
function Boot {
this.max_steps = 150000;
this.kick_step_equivalent = 10;
this.doKick = function() {
for (var i = 0; i < this.kick_step_equivalent; i++) {
this.doStep();
}
}
}
Boot.prototype = new BasicShoe();
function SteelTippedBoot {
this.max_steps = 175000;
this.kick_step_equivalent = 0;
}
SteelTippedBoot.prototype = new Boot();

Insert scripts into an existing function?

This function is built into the page and I cannot modify the original .js file:
cool.lol = function () {
// contents here
}
Is there a way for me to append this function with some of my own scripts?
Like this:
cool.lol = function () {
// contents here
// i would like to add my own stuff here!!!
}
Or is there a way for me to detect that the function has been executed so I can run something after it?
Here's a demo of the following. Updated to use a closure and remove the need for a temporary variable.
//your cool with lol
var cool = {
lol: function() {
alert('lol');
}
}
//let's have a closure that carries the original cool.lol
//and returns our new function with additional stuff
cool.lol = (function(temp) { //cool.lol is now the local temp
return function(){ //return our new function carrying the old cool.lol
temp.call(cool); //execute the old cool.lol
alert('bar'); //additional stuff
}
}(cool.lol)); //pass in our original cool.lol
cool.lol();
cool.lol();​
http://jsfiddle.net/Pcxn5/1/
// original definition which you can't touch
var cool = {
lol: function() {
alert("test");
}
}
//
// your script
var ori = cool.lol;
cool.lol = function() {
ori();
alert('my test');
}
cool.lol();​
You can override the function:
// The file you can't touch has something like this
var cool = {};
cool.lol = function () {
console.log("original");
}
// Keep a copy of the original function.
// Override the original and execute the copy inside
var original = cool.lol;
cool.lol = function () {
original();
console.log("modified");
}
// Call
cool.lol();​ // logs "original", then "modified".
http://jsfiddle.net/K8SLH/
Without creating variables polluting the public scope:
//let's have your cool namespace
var cool = {
lol: function() {
alert('lol');
}
}
//temporarily store
cool.lol_original = cool.lol;
//overwrite
cool.lol = function() {
this.lol_original(); //call the original function
alert('bar'); //do some additional stuff
}
cool.lol();​
JavaScript allows you to
get the code of the functions as a string
create new functions by supplying a string with code
Every object has a toString() method. For functions, it returns their code (unless overriden).
cool.lol.toString();
returns function() { // contents here }.
Let us extract the body of the function from this string. It starts immediately after { and includes everything except the last }.
var code = cool.lol.toString();
var body = code.substring(code.indexOf('{') + 1, code.length - 1);
Then we add more stuff
var newBody = body + '// i would like to add my own stuff here!!!';
and create a new function using the Function constructor.
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function
cool.lol = new Function(newBody);
Of course, there is more work to do if the new function also has to retain arguments (you have to parse them out from the function code, then give them as parameters to the Function constructor). For simplicity, in this case I assumed there are no arguments to the function.
An example implementation:
http://jsfiddle.net/QA9Zx/

Javascript - dumping all global variables

Is there a way in Javascript to get a list or dump the contents of all global variables declared by Javascript/jQuery script on a page? I am particularly interested in arrays. If I can get the array names, it will be enough to me. Seeing its values is a bonus.
Object.keys( window );
This will give you an Array of all enumerable properties of the window object, (which are global variables).
For older browsers, include the compatibility patch from MDN.
To see its values, then clearly you'll just want a typical enumerator, like for-in.
You should note that I mentioned that these methods will only give you enumerable properties. Typically those will be ones that are not built-in by the environment.
It is possible to add non-enumerable properties in ES5 supported browsers. These will not be included in Object.keys, or when using a for-in statement.
As noted by #Raynos, you can Object.getOwnPropertyNames( window ) for non-enumerables. I didn't know that. Thanks #Raynos!
So to see the values that include enumerables, you'd want to do this:
var keys = Object.getOwnPropertyNames( window ),
value;
for( var i = 0; i < keys.length; ++i ) {
value = window[ keys[ i ] ];
console.log( value );
}
The following function only dumps global variables that have been added to the window object:
(function(){
//noprotect <- this comment prevents jsbin interference
var windowProps = function() {
// debugger;
var result = {};
for (var key in window) {
if (Object.prototype.hasOwnProperty.call(window, key)) {
if ((key|0) !== parseInt(key,10)) {
result[key] = 1;
}
}
}
window.usedVars = result;
};
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'about:blank';
document.body.appendChild(iframe);
var fwin = iframe.contentWindow;
var fdoc = fwin.document;
fdoc.open('text/html','replace');
fdoc.write('<!DOCTYPE html><body><script>window.onload = ' + windowProps.toString() + '<\u002Fscript>');
fdoc.close();
var button = document.createElement('input');
button.type = 'button';
button.value = 'go';
document.body.appendChild(button);
button.onclick = function() {
var clean = fwin.usedVars;
windowProps();
var dirty = window.usedVars;
for (var key in clean) {
delete dirty[key];
}
for (var variable in dirty) {
var div = document.createElement('div');
div.textContent = variable;
document.body.appendChild(div);
}
document.body.removeChild(button);
document.body.removeChild(iframe);
};
})();
It works by using an iframe to get a clean list of global window variables, then comparing that with the list of global variables in the current window. It uses a button because the iframe runs asynchronously. The code uses a global variable because that makes the code easier to understand.
You can see it working here or here, although note that these examples show many global variables "leaked" by jsbin itself (different depending on which link you use).
Since all global variables are properties of the window object, you can get them using:
for(var key in window) { // all properties
if(Array.isArray(window[key])) { // only arrays
console.log(key, window[key]); // log key + value
}
}
Since all default/inherited properties are not plain arrays (mostly host objects or functions), the Array.isArray check is sufficient.
To get "globals" object you can use this function:
function globals() { return this; }
Here is the test:
http://jsfiddle.net/EERuf/
window is the global object in a browser, and you can use a for..in loop to loop through its properties:
if(!Array.isArray) {
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
for(var x in window) {
if(Array.isArray(window[x])) {
console.log('Found array ' + x + ' in ' + window + ', it has the value ' + window[x] + '!');
}
}
You can use npm package called get-globals. It compares properties of window with fresh-created iframe to print only variables declared by dev(s), not browser vendor.
Greasymonkey script to get leaked globals
// ==UserScript==
// #name SCI
// #namespace ns
// #version 1
// #grant none
// #run-at document-start
// ==/UserScript==
console.log('SCI loaded');
var SCI = window.SCI = {
defprops: [],
collect: function(){
var wprops = [];
for(var prop in window){
wprops.push(prop);
}
return wprops;
},
collectDef: function(){
this.defprops = this.collect();
},
diff: function(){
var def = this.defprops,
cur = this.collect();
var dif = [];
for(var i = 0; i < cur.length; i++){
var p = cur[i];
if(def.indexOf(p) === -1){
dif.push(p);
}
}
return dif;
},
diffObj: function(){
var diff = this.diff();
var dobj = {};
for (var i = 0; i < diff.length; i++){
var p = diff[i];
dobj[p]=window[p];
}
return dobj;
}
};
SCI.collectDef();
To use run in console SCI.diff() to get list of names or SCI.diffObj() to get object with variables
Here’s a simple, more modern snippet that logs an object with the globals and their values (rather than just the global variable names), which is usually what I’m looking for when debugging:
(function () {
const iframe = document.createElement('iframe')
iframe.setAttribute('hidden', '')
iframe.src = 'about:blank'
iframe.onload = function () {
// Iterate through the properties of the current `window` and reduce the output
// to only properties that are not in the iframe’s `window`.
console.debug(Object.entries(window).reduce((reducedObj, [property, value]) => {
// Check if the property does not exist in the iframe and if so, add it to
// the output object.
if (! (property in iframe.contentWindow))
reducedObj[property] = value
return reducedObj
}, {})))
// Clean up the iframe by removing it from the DOM.
iframe.remove()
}
// Append the iframe to the DOM to kick off loading.
document.body.append(iframe)
})()
Tip: You can also swap 'about:blank' with window.location to get only the globals set since the page was first loaded.
This uses an iframe to determine which properties to ignore like robocat’s answer, but it’s based on David Walsh’s solution.

How to detect creation of new global variables?

I want watch for the creation of new global variables in Javascript so that, anytime a global variable is created, an event is fired.
I've heard of the watch() function but that is only for watching for specific variable names. I want a catchall.
If you already know which names pollute your global namespace (see Intercepting global variable definition in javascript), you can use this trick to figure out when does it actually happen:
window.__defineSetter__('someGlobalVar', function() {
debugger;
});
Be sure to have your developer tools open when you run this.
Obviously works only if your browser supports __defineSetter__ but that's true for modern browsers. Also, don't forget to remove your debug code after you've finished.
Found here.
I don't know how to make this work "on demand" as soon as a var is created, but I can suggest a polling approach. In a browser window, all global become a member of the global "window" object. (Because technically, "window" is the "global object"). So you could do something like the following:
1) enumerate all the properties on a window
window.proplist = window.proplist || {};
for (propname in window) {
if (propname !== "proplist") {
window.proplist[propname] = true;
}
}
2) Set a timer to periodically "poll" window for new properties
setInterval(onTimer, 1000);
3) Wake up on the timer callback and look for new props
function onTimer() {
if (!window.proplist) {
return;
}
for (propname in window) {
if (!(window.proplist[propname])) {
window.proplist[propname] = true;
onGlobalVarCreated(propname);
}
}
}
Afaik, .watch() is only SpiderMonkey (Firefox).
I played around with a polling function, I finally came up with this:
var mywatch = (function() {
var last = {
count: 0,
elems: {}
};
return function _REP(cb) {
var curr = {
count: 0,
elems: {}
},
diff = {};
for(var prop in window) {
if( window.hasOwnProperty(prop) ) {
curr.elems[prop] = window[prop]; curr.count++;
}
}
if( curr.count > last.count ) {
for(var comp in curr.elems) {
if( !(comp in last.elems) ) {
diff[comp] = curr.elems[comp];
}
}
last.count = curr.count;
last.elems = curr.elems;
if(typeof cb === 'function')
cb.apply(null, [diff]);
}
setTimeout(function() {
_REP(cb);
}, 400);
};
}());
And then use it like:
mywatch(function(diff) {
console.log('NEW GLOBAL(s): ', diff);
});
Be aware that this only handles new globals. But you can easily extend this for the case last.count > curr.count. That would indicate that global variables were deleted.
You cant have an event fired when some script does var v = 10, but as selbie said, you can poll the window object... I meant to suggest the same, but he beat me to it. Here's my other example... you count how many window objects are there, and execute GlobalVarCreated() function:
var number_of_globals = 0; //last known globals count
var interval = window.setInterval(function(){
var new_globals_count = 0; //we count again
for(var i in window) new_globals_count++; //actual counting
if(number_of_globals == 0) number_of_globals = new_globals_count; //first time we initialize old value
else{
var number_of_new_globals = new_globals_count - number_of_globals; //new - old
if(number_of_new_globals > 0){ //if the number is higher then 0 then we have some vars
number_of_globals = new_globals_count;
for(var i = 0; i<number_of_new_globals; i++) GlobalVarCreated(); //if we have 2 new vars we call handler 2 times...
}
}
},300); //each 300ms check is run
//Other functions
function GlobalVarCreated(){}
function StopInterval(){window.clearInterval(interval);}
You can load up that code in Chrome or FF console only change: function GlobalVarCreated(){console.log("NEW VAR CREATED");} and test it:
var a = 10
b = 10
String NEW VAR CREATED is displayed 2 times.

Find and execute Javascript fragments in a bunch of HTML

I need to detect and eval the Javascript code contained in a string.
The following code works, but it only evaluates the first <script>...</script> it founds.
function executeJs(html) {
var scriptFragment = "<script(.+?)>(.+?)<\/script>";
match = new RegExp(scriptFragment, "im");
var matches = html.match(match);
if (matches.length >= 2) {
eval(matches[2]);
}
}
I wonder if there is a method that allows me to iterate and execute all Javascript fragments.
The reason it only takes the first one is because you're missing the g flag. Try this:
function executeJs(html) {
var scriptFragment = '<script(.*?)>(.+?)<\/script>';
var re = new RegExp(scriptFragment, 'gim'), match;
while ((match = re.exec(html)) != null) {
eval(match[2]);
}
}
executeJs('<script>alert("hello")</script>abc<script>alert("world")</script>');
Here is some code that does the same thing in a slightly different way. You can pass the string to the function and it will eval all the script tags and return the cleaned source(without script). There is also a slight difference in the way IE handles it, that is handled in the code as well, you may adapt it to your requirements. Also, the evaluated code has the global context. Hope it helps.
function parseScript(_source)
{
var source = _source;
var scripts = new Array();
// Strip out tags
while(source.indexOf("<script") > -1 || source.indexOf("</script") > -1)
{
var s = source.indexOf("<script");
var s_e = source.indexOf(">", s);
var e = source.indexOf("</script", s);
var e_e = source.indexOf(">", e);
// Add to scripts array
scripts.push(source.substring(s_e+1, e));
// Strip from source
source = source.substring(0, s) + source.substring(e_e+1);
}
// Loop through every script collected and eval it
for(var i=0; i<scripts.length; i++)
{
try
{
//eval(scripts[i]);
if(window.execScript)
{
window.execScript(scripts[i]); // IE
}
else
{
window.setTimeout(scripts[i],0); // Changed this from eval() to setTimeout() to get it in Global scope
}
}
catch(ex)
{
// do what you want here when a script fails
alert("Javascript Handler failed interpretation. Even I am wondering why(?)");
}
}
// Return the cleaned source
return source;
}
Blixt should be right...
You may also take a look at prototype's String.evalScripts function.
http://api.prototypejs.org/language/string.html#evalscripts-instance_method

Categories