What is the Chrome console displaying with log()? - javascript

I think I may have found a bug with Google Chrome (16.0.912.75 m, the latest stable at the time of this writing).
var FakeFancy = function () {};
console.log(new FakeFancy());
var holder = {
assignTo : function (func) {
holder.constructor = func;
}
};
holder.assignTo(function () { this.type = 'anonymous' });
var globalConstructor = function () { this.type = 'global' }
console.log(new holder.constructor());
If you run that block in Firefox, it shows both listed as "Object" with the second having type = local, pretty good. But if you run it in Chrome, it shows
> FakeFancy
> globalConstructor.type
If you expand the trees, the contents are correct. But I can't figure out what Chrome is listing as the first line for each object logged. Since I'm not manipulating the prototypes, these should be plain old objects that aren't inheriting from anywhere.
At first, I thought it was WebKit related, but I tried in the latest Safari for Windows (5.1.2 7534.52.7) and both show up as "Object".
I suspect that it's attempting to do some guesswork about where the constructor was called from. Is the anonymous constructor's indirection messing it up?

The first line is a result of
console.log(new FakeFancy());
The WebKit console generally tries to do "constructor name inference" to let you know what type of object it's outputting. My guess is that the more recent version included with Chrome (as opposed to Safari 5.1) can do inference for constructor declarations like
var FakeFancy = function () {};
and not just ones like
function FakeFancy() {}
so that's why you're seeing the disparity.

Related

Subclassing ES6 Set in javascript

I'm running into problems when trying to inherit from the new Set available in ecmascript 6. The class is defined as such:
function SelectionManager () {
Set.call(this);
}
SelectionManager.prototype = Object.create(Set.prototype);
SelectionManager.prototype.add = function (unit) {
unit.setIsSelected(true);
Set.prototype.add.call(this, unit);
};
/* Some functions left out */
When trying to call add I get the following error: TypeError: Set operation called on non-Set object
The code is available at http://jsfiddle.net/6nq1gqx7/
The draft for ES6 clearly states that it should be possible to subclass Set, what is the correct way of doing so?
Looks like this is being tracked:
https://github.com/google/traceur-compiler/issues/1413
No correct way for now. Chrome/V8 (as most other browsers) does not yet properly support subclassing of builtins -- in part because it potentially is quite complicated to implement, and in part because the precise semantics are still wildly in flux, and have just been overthrown at the latest ES6 meeting, with no final resolution yet (as of October 2014).
In Chrome, Firefox, and Edge (But not IE11) you can Subclass ES6 Set and Map with this workaround, done with ES6 classes:
class MySet extends Set {
constructor() {
// instead of super(), you must write:
const self = new Set();
self.__proto__ = MySet.prototype;
return self;
}
// example method:
add(item) {
console.log('added!')
super.add(item); // Or: Set.prototype.add.call(this, item);
}
}
var mm = new MySet();
console.log(mm.size); // logs 0
mm.add(5); // logs 'added!'
console.log(mm.size); // logs 1

How can I log information about global variables whenever these are created?

Background
I just learned that calling keys(window) (or Object.keys(window)) in the DevTools console reveals variables in the global scope (source). I called that code on the StackOverflow page and got this result:
Variable i got my attention as it seems to be in the global scope by a mistake. I tried locating code that is responsible for declaring i, but it turned out to be cumbersome (there is a lot of code and a lot of is).
Question
Getting console warnings that say
Global variable "i" created (main.js:342)
could be useful. How can I implement that feature?
Research
I figured that I need some kind of an event whenever new variable is created.
We do have setters in JavaScript. However, creating a setter requires that you provide a property name. Since I want to monitor all properties I can't really use that.
__noSuchMethod__ (MDN) would be perfect but it only covers methods (and there is no __noSuchProperty__ method).
Object.observe (HTML5 Rocks) doesn't reveal anything about the code that created the property (console.trace() gives me only the name of the observer function).
Object.prototype.watch (MDN) - same as the setter, you have to specify a property name.
Calling Object.preventExtensions(window) (MDN) causes errors with a nice stack trace whenever new global variable is created. The problem with this solution is that it interferes with the script execution and may change its behaviour. It also doesn't allow me to catch the error and format it properly.
Notes
I know about jshint/jslint and I still think that catching these declarations in the runtime could be useful.
I don't care about i variable on the SO page that much, you can probably find the declaration using setters. My question concerns the general solution for this problem.
IMO you have two decent options:
JSHint.
Strict mode.
Both will yell at you when you leak a global. Strict mode will probably be better for your usecases.
You've definitely done your homework, and you thought of all the things I would have thought of and, as you discovered, none of them fit.
The only way I can think of doing is to just monitor the global object (this example is using window as the global object: modify accordingly for Node or another JavaScript container). Here's an example that monitors new globals and deleted globals (if you don't need to monitor deletions, you can remove that functionality):
var globals = Object.keys(window);
var monitorGlobalInterval = 50;
setInterval(function(){
var globalsNow = Object.keys(window);
var newGlobals = globalsNow.filter(function(key){
return globals.indexOf(key)===-1;
});
var deletedGlobals = globals.filter(function(key){
return globalsNow.indexOf(key)===-1;
});
newGlobals.forEach(function(key){
console.log('new global: ' + key);
});
deletedGlobals.forEach(function(key){
console.log('global deleted: ' + key);
});
globals = globalsNow;
}, monitorGlobalInterval);
See it in action here: http://jsfiddle.net/dRjP9/2/
You can try this method to get a list of global variables that you've created:
(function(){
var differences = {},
ignoreList = (prompt('Ignore filter (comma sep)?', 'jQuery, Backbone, _, $').split(/,\s?/) || []),
iframe = document.createElement('iframe'),
count = 0; ignoreList.push('prop');
for (prop in window) {
differences[prop] = {
type: typeof window[prop],
val: window[prop]
}; count++;
}
iframe.src = 'about:blank';
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe = iframe.contentWindow || iframe.contentDocument;
for (prop in differences) {
if (prop in iframe || ignoreList.indexOf(prop) >= 0) {
delete differences[prop];
count--;
}
}
console.info('Total globals: %d', count);
return differences;
})();

Disable editing of javascript from chrome console?

So, I just noticed today that you can apparently run javascript in the chrome console. I had no idea you could do this. It's actually really cool.
In my rails app, I have an external javascript page. Some of the variables on that page I would like to be global so that all the functions in the JS file can access them. for example I have a map, and I would like the map object to be global in the javascript file because that way all my functions access the one map variable instead of creating their own, and I can break complex operations down into smaller functions.
This is all well and good I know how to do that and it's working perfectly. My problem now, can I protect the variables from outside? For example you can change the values of all the javascript class variables from the chrome console.. as well methods from for example the map are accessible and excecutable.. I have locked the map settings on one of the pages so it is not zoomable or movable, however from the console I can simply say map.setZoom(11) and the map will zoom to 11.. I can type map.dragable = true and bam u can drag the map.. I don't like this really..
It's not too terribly bad yet like the user enabling map drag and zoom isnt the worst thing in the world.. but still I'd like to disable this. Any ideas?
EDIT
Thanks all for the answers and comments. I guess I will just resort to not putting anything that can be turned malicious into my javascript, and do thing like pass my map variable to functions where necessary to slow people down.
You can use an immediately-invoked function (IIFE) expression to prevent your variables and functions from being exposed in the global scope:
var a = 10;
(function() {
var b = 20;
})();
window.a lets you view and modify a, but you cannot do that with b:
Try it out here
I'm more than sure that there's a way to edit b with Inspector, but I haven't taken the time to figure it out. Don't waste your time trying to prevent your users from modifying code that they can view.
You can't. Even if you wrap them into anonymous functions, user can get to them through debugger. As last resort he can simply intercept your traffic to his own machine and replace your JavaScript with something else.
Bottom line: JavaScript in browser is client-side. Client can do whatever he pleases with it.
Try doing something like this:
(function(){
//All of your current code
})();
One thing to still be aware of - Chrome developer tools also lets you edit the javascript (not the javascript file on the server, just currently running copy.) Go to Chrome Dev Tools->Sources and you can edit the javascript files.
You can't. Your saying you need to define your map globally, this means it's accessible for everyone.
You could define your map in a different scope and then only define the "public" things:
(function() {
var map = new Map();
window.myMap = {
goTo: function(lat, lng) {
map.goTo(lat, lng);
}
};
})();
Depending on your architecture, there are a few ways to accomplish this. Use this method to create a reusable component that has public and private properties:
var protectedScope = function () {
var protected_var = 'protected';
this.showProtected = function () {
return protected_var;
}
this.public = 'public';
};
var myObject = new protectedScope();
console.log('Public var: '+myObject.public); // outputs "public"
console.log('Protected via accessor: '+myObject.showProtected ()); // outputs "private"
console.log('Protected var: '+myObject.protected); // outputs undefined
Any variable or function declared with a var keyword will be, in effect, private. Any variable or function that uses the this.name mechanism will be "public".
Understand that this structure is not truly public or private, such concepts are not a part of the language. There are still ways to get at those variables, and one can always view source. Just be clear; this is a code organization concept rather than a security concept. Chrome has had this developer console for a while, and other major user agents are moving to include similar tools (or already have done so). There are also tools like Firebug which allow a user full access to your javascript runtime environment. This isn't a realm that the developer can control at all.
Try it here: http://jsfiddle.net/cf2kS/
More Reading
"Private Members in JavaScript" by Douglas Crockford* - http://www.crockford.com/javascript/private.html
"OOP in JS, Part 1 : Public/Private Variables and Methods" on http://phrogz.net - http://phrogz.net/JS/classes/OOPinJS.html
Javascript Object Management on MDN - https://developer.mozilla.org/en-US/docs/XUL_School/JavaScript_Object_Management
Closures on MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures
Object.defineProperty(map, 'zoom', {value:1});
or
Object.defineProperty(map, 'zoom',{
set: function(){console.warn('Access denied!');},
get: function(){return 1;}
});
demo
or
Object.defineProperty(Object.prototype, 'protect', {
value: function(ignore){
var childObjects = [], ignore = ignore || [];
ignore.push(this);
if(this instanceof MimeType)return; //Chrome Fix //window.clientInformation.mimeTypes[0].enabledPlugin[0] !== window.clientInformation.mimeTypes[0]
for(var prop in this){
if(typeof this[prop] === "unknown")continue; //IE fix
if(this[prop] instanceof Object){
var skip = false;
for(var i in ignore)
if(ignore[i]===this[prop]){
skip = true;
break;
}
if(!skip)childObjects.push(prop);
}
var d = Object.getOwnPropertyDescriptor(this, prop);
if(!d || !d.configurable || !d.writable)continue;
var that = this;
(function(){
var temp = that[prop];
delete that[prop];
Object.defineProperty(that, prop,{
set: function(){console.warn('Access denied!');},
get: function(){return temp;}
});
})();
}
for(var i = 0;i<childObjects.length;i++)
this[childObjects[i]].protect(ignore);
}
});
this.onload=function(){this.protect();} //example
demo

Is toJSON supported in Google Chrome?

when using JSON.stringify in Google Chrome it seems that toJSON isn't being called? I am using json2.js as a back-up for browsers that don't support it. I guess since Chrome supports JSON but not toJSON json2.js isn't being used at all?
Update
Here is an example: http://jsfiddle.net/GZzvZ/
Firefox: {"foo":"foo","bar":"bar"}
Chrome: {"bar":"bar"}
var t = {};
t.toJSON = function () { alert('meuh'); return (''); }
JSON.stringify(t)
Works perfectly fine for me.
It does alert, so it does call the toJSON method appropriately (in Chrome 8).
EDIT:
That's normal. Your Foo is a function, and function objects are not allowed in JSON. Firefox is just being forgiving, I guess.
Well, actually I wonder, I don't find a clear answer in the standard. Considering you supply a toJSON() to provide your own serialization, should it be allowed or not. But anyway, that's the reason for your failure.
If you edit your jsFiddle example like this, the toJSON is called accordingly on foo.
var obj = function(){
this.foo = 'test'; // OK
//this.foo = function(){ }; KO
this.foo.toJSON = function(){
return 'foo';
};
this.bar = 'bar';
}
var ins = new obj();
var json = JSON.stringify( ins );
document.write( json );

Object.watch() for all browsers?

Please note that Object.Watch and Object.Observe are both deprecated now (as of Jun 2018).
I was looking for an easy way to monitor an object or variable for changes, and I found Object.watch(), that's supported in Mozilla browsers, but not IE. So I started searching around to see if anyone had written some sort of equivalent.
About the only thing I've found has been a jQuery plugin, but I'm not sure if that's the best way to go. I certainly use jQuery in most of my projects, so I'm not worried about the jQuery aspect...
Anyway, the question: Can someone show me a working example of that jQuery plugin? I'm having problems making it work...
Or, does anyone know of any better alternatives that would work cross browser?
Update after answers:
Thanks everyone for the responses! I tried out the code posted here:
http://webreflection.blogspot.com/2009/01/internet-explorer-object-watch.html
But I couldn't seem to make it work with IE. The code below works fine in Firefox, but does nothing in IE. In Firefox, each time watcher.status is changed, the document.write() in watcher.watch() is called and you can see the output on the page. In IE, that doesn't happen, but I can see that watcher.status is updating the value, because the last document.write() call shows the correct value (in both IE and FF). But, if the callback function isn't called, then that's kind of pointless... :)
Am I missing something?
var options = {'status': 'no status'},
watcher = createWatcher(options);
watcher.watch("status", function(prop, oldValue, newValue) {
document.write("old: " + oldValue + ", new: " + newValue + "<br>");
return newValue;
});
watcher.status = 'asdf';
watcher.status = '1234';
document.write(watcher.status + "<br>");
(Sorry for the cross-posting, but this answer I gave to a similar question works fine here)
I have created a small object.watch shim for this a while ago. It works in IE8, Safari, Chrome, Firefox, Opera, etc.
That plugin simply uses a timer/interval to repeatedly check for changes on an object. Maybe good enough but personally I would like more immediacy as an observer.
Here's an attempt at bringing watch/unwatch to IE: http://webreflection.blogspot.com/2009/01/internet-explorer-object-watch.html.
It does change the syntax from the Firefox way of adding observers. Instead of :
var obj = {foo:'bar'};
obj.watch('foo', fooChanged);
You do:
var obj = {foo:'bar'};
var watcher = createWatcher(obj);
watcher.watch('foo', fooChanged);
Not as sweet, but as an observer you are notified immediately.
The answers to this question are a bit outdated. Object.watch and Object.observe are both deprecated and should not be used.
Today, you can now use the Proxy object to monitor (and intercept) changes made to an object. Here's a basic example:
var targetObj = {};
var targetProxy = new Proxy(targetObj, {
set: function (target, key, value) {
console.log(`${key} set to ${value}`);
target[key] = value;
}
});
targetProxy.hello_world = "test"; // console: 'hello_world set to test'
If you need to observe changes made to a nested object, then you need to use a specialized library. I published Observable Slim and it works like this:
var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
p.testing.blah = 42; // console: [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
Current Answer
Use the new Proxy object, which can watch changes to it's target.
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// The default behavior to store the value
obj[prop] = value;
// Indicate success
return true;
}
};
let person = new Proxy({}, validator);
person.age = 100;
console.log(person.age); // 100
person.age = 'young'; // Throws an exception
person.age = 300; // Throws an exception
Old answer from 2015
You could have used Object.observe() from ES7. Here's a polyfill. But Object.observe() is now cancelled. Sorry people!
Note that in Chrome 36 and higher you can use Object.observe as well. This is actually a part of a future ECMAScript standard, and not a browser-specific feature like Mozilla's Object.watch.
Object.observe only works on object properties, but is a lot more performant than Object.watch (which is meant for debugging purposes, not production use).
var options = {};
Object.observe(options, function(changes) {
console.log(changes);
});
options.foo = 'bar';
you can use Object.defineProperty.
watch the property bar in foo
Object.defineProperty(foo, "bar", {
get: function (val){
//some code to watch the getter function
},
set: function (val) {
//some code to watch the setter function
}
})
I have used Watch.js in one of my projects. And it is working fine.One of the main advantage of using this library is :
"With Watch.JS you will not have to change the way you develop."
The example is given below
//defining our object however we like
var ex1 = {
attr1: "initial value of attr1",
attr2: "initial value of attr2"
};
//defining a 'watcher' for an attribute
watch(ex1, "attr1", function(){
alert("attr1 changed!");
});
//when changing the attribute its watcher will be invoked
ex1.attr1 = "other value";
<script src="https://cdn.jsdelivr.net/npm/melanke-watchjs#1.5.0/src/watch.min.js"></script>
This is as simple as this!
This works for me
$('#img').hide().attr('src', "path/newImage.jpg").fadeIn('slow');
I also think that right now the best solution is to use Watch.JS, find a nice tutorial here: Listen/Watch for object or array changes in Javascript (Property changed event on Javascript objects)

Categories

Resources