How can I override the way Proxies are displayed in console? - javascript

I've looked through the docs on Proxies, and there is a long list of function properties which can be overridden. Unfortunately, none of them explicitly mention a link to console.log(), and I can't infer which ones console.log() might interact with.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
I've also seen this Stack Overflow question (Javascript Proxy and spread syntax, combined with console.log) which seems to show something similar to what I'm looking for, but more targeted at getting information about a property than the way it's displayed in console.
The issue is, whenever I console.log my proxies, I get output like this:
Proxy { <target>: {…}, <handler>: {…} }
Further, my target is (in my case) totally empty.
const foo = { foo: "foo string" };
const bar = { bar: "bar string" };
let currentObj = foo;
const proxy = new Proxy({}, {
get: (_, prop) => currentObj[prop],
});
console.log(proxy.foo);
console.log(proxy); // would like this to be { foo: "foo" }
currentObj = bar;
console.log(proxy.bar);
console.log(proxy); // would like this to be { bar: "bar" }
So, in order to make this code possible to debug, I need some way to tell console.log what object to output.
Is it possible to override some aspect of a Proxy handler such that console.log() will output arbitrary, use-case specific data?

You could override the toString method and log your object with its toString() method.
Or you could provide a different view-specific log function such as oob.log => () => obj.name

Related

automating / overriding / extending prefix in console.log for debugging

Anyone have a good solution to extending console.log so that it auto prints class name and method as a prefix? I'm using web components and use strict is turned on.
someFunction() {
let varA = "hello"
console.log(this.constructor.name, "someFunction", {varA})
}
Would like to automate this part: this.constructor.name, "someFunction", ...
arguments.callee.name will print the function name, but no longer works with strict mode turned on.
Extending console.log in a centralized location via:
export var log = function(){
var args = Array.prototype.slice.call(arguments);
args.unshift(this.constructor.name + ": ");
console.log.apply(console, args);
}
does not work as this.constructor.name does not print the correct context and if it's not in a web component, it doesn't work at all.
Extending console.log in each web component defeats the purpose (or half of it).
Could fold a function that extends console.log in the build for each web component but would still have the problem of not being able to call arguments.calleee.
Using lit-element, but this is a native javascript issue.
console.trace() may be of use here, instead of a custom console method.
Or, use the new Error()'s .stack property.
class A {
b() {
return function c() {
return function d() {
return (function e() {
return new Error().stack;
})();
}
}
}
}
console.log("here's the stack:\n", new A().b()()());
so based on sean-7777's answer, I suppose we could slice the stack output like this:
let funcname = new Error().stack.split('\n')[1];
funcname=funcname.slice(funcname.indexOf("at ")+3, funcname.indexOf(" ("));
console.log(debugline, 'debug text');
If you put it into a helper function, you could just grab line index 2 (since index 1 would give you the name of the helper function).
I was hoping for something a little more straightforward but hacking the output does work.
UPDATE:
export function prtfun() {
let debugline = new Error().stack.split('\n')[2];
return debugline.slice(debugline.indexOf("at ")+3, debugline.indexOf(" ("));
}
call it from anywhere like this:
console.log(prtfun(), 'log text...');
and it will print ClassName.FunctionName log text...
Works great. I'd disable this in production though.

Why does Map.get() return undefined when I know for a fact when logged the item is there?

I'm curious if I'm missing something here -- When I call a method that should get something from the collection because I have proof it exists, it doesn't get it still
My Object of Collections
export const databaseCache = {
/* Other collections ommitted */
roles: new Collection<string, Role>(),
};
My method
export function getRoleByName(name: string) {
return databaseCache.roles.get(name);
}
and my call
const feedbackMod = getRoleByName("pack feedback helper");
feedbackMod is undefined even though I have proof it exists because I logged the collection, (collection is considered a Map object as Collection is a class that extends Map)
https://imgur.com/hD2RP2S image of proof of collection item existing
Reproduceable Code:
interface Role {
name: string;
roleId: number;
}
const myCollections = {
roles: new Map<string, Role>()
};
myCollections.roles.set("pack feedback helper", { name: "pack feedback helper", roleId: 543576460363956244 });
const item = myCollections.roles.get("pack feedback helper");
console.log(item);
Although when tested online, this works, locally it does not for me.
Almost certainly there is something more going on that your example shows. The likely problem is that you have a String vs. a string kicking around on one side or the other (e. g. an objectified string vs. a primitive string):
let key = "pack feedback helper";
let memo = new Map();
memo.set(key, "some value here");
memo.get(key); // Works
memo.get("pack feedback helper"); // Works
let objectifiedKey = new String(key);
memo.get(objectifiedKey); // DOES NOT WORK
The simplest test to validate that this is the case is to wrap both the set and the get calls' key argument in String to de-objectify the key:
// Continuing from the example above
let deObjectifiedKey = String(objectifiedKey); // Note the lack of `new` here
memo.get(deObjectifiedKey); // WORKS AGAIN!
If it is the case that de-objectifying works, then you can trace which side is causing the problem by checking typeof key. It will be object for "objectified" values and string for primitive ones.
Alternatively, test in another browser. If it works in one and not another, then you may have a browser bug.
I found my answer; it is too much code to paste but in short and simple terms it was because some of my methods were lexically scoped, thus being read by the compiler first, where the collection still had no items in it. When I converted the lexical scoped methods to block (es6 arrow functions), everything was able to load in correctly and be used accordingly.

What does a Javascript Object.create do?

My application is using Protractor and the "Page Model" for testing. There's an external module that looks like this:
'use strict';
var AdminTestPage = function () {
browser.get('/');
};
AdminTestPage.prototype = Object.create({}, {
detailTabClear: {
get: function () {
element(by.id('name')).clear();
}
}
});
module.exports = AdminTestPage;
Can someone explain why the developer has used Object.create? Is this different from just combining the first and second parts of the code?
The properties in the property descriptor become non-enumerable and non-configurable by default, as explained in the documentation Object.defineProperty.
So the call is the same as
AdminTestPage.prototype = Object.create({}, {
detailTabClear: {
get: function () {
element(by.id('name')).clear();
},
enumerable: false,
configurable: false
}
});
What does that mean? It means that this property won't show up when you iterate over the object with for...in and you won't be able to delete it from the object via delete.
The same could have been written as
Object.defineProperty(AdminTestPage.prototype, 'detailTabClear', {
get: function () {
element(by.id('name')).clear();
}
});
Note: This is very different from assigning the property directly. Properties created via the assignment statement (i.e. obj.prop = foo)` are enumerable and configurable by default.
Whether this was the intention of the author or not I cannot say though.
Same question, I've got no idea why the developer decided to use Object.create.
Object.create does exactly what it says. It creates an object, in this case there is no difference from using the short hand notation...
AdminTestPage.prototype = {...};
it is exactly the same. Object.create is most useful when you already have a prototype of an object and want to create another "instance" of the same object because it "inherits" the prototype so you will not have to define all of its members. For example, consider this prototype...
var car = { make: "", model: ""};
Then you can create an instance of a car...
var subaru = Object.create(car);
the subaru variable is already a prototype of car...in OOP terms it'd be something like an instance of a car...
subaru.make = "Subaru";
subaru.model = "WRX";
Be aware that Object.create is not supported by all browsers

Protecting a Global Javascript "API" Object

I currently have a Web Application that runs off a global Javascript-based API, and it is initialized like this:
var Api = {
someVar: "test",
someFunction: function() {
return "foo";
}
}
This API is shared across many "Widgets" that live in the Web Application, and they should all run off this single Api instance so they can pass data to each other.
AJAX is currently used to load these Widgets, for example in widgets/mywidget.html, and it's placed in, say, <div id='widget_<random number>'>...</div>
Certain other parts of the code may choose to add more functionality to Api, and it's currently done like this:
Api.myExtension = {
myNewFunction: function() {
return "bar";
}
}
However, some issues arise from this kind of usage:
Problem One: What if one Widget (these may be provided by third-parties) decides to hide some code within, and does something similar to Api = {}, destroying the global Api var everything lives on, and breaking the whole Application? Is it possible to protect this Api variable from being overwritten from outside? Only "extending" is allowed (adding new things), but "removing/changing" is not allowed. i.e.:
Api.foo = { test: "bar" } // allowed
Api.someVar = "changing the existing someVar"; // not allowed
The following code is located "inside" Api, for example:
var Api = {
Debug: {
Messages = new Array,
Write: function() {
Api.Debug.Messages.push("test"); // allowed
}
}
}
Api.Debug.Messages.push("test 2"); // not allowed
Probable Solutions I've Thought Of:
Suppose we simply use frames to resolve this issue. The Apis provided are now separate from each other. However, there's additional overhead when loading Api again and again if I have many Widgets running, and they can no longer communicate with the "Host" of the widgets (the page where frames reside in), for example, I may want to tell the host to show a notification: Api.Notify.Show("Test"), but it cannot do so because this Api is completely independent from other instances, and it cannot communicate with the "Host"
Using something like a "getter" and "setter" function for the Api to be read and written. I'm unsure on how to implement this, so any help on directions on how to implement this is welcome!
A mixture of 1/2?
There's no good way to prevent having a "third party" widget overwrite the a global variable. Generally it is the responsibility of whoever is putting together the final application to ensure that whatever JavaScripts they are using aren't littering the global namespace and conflicting. The best thing you can do in that direction is give your "Api" a nice, unique name.
What I think can help you a lot is something like the "revealing pattern", which would be a way of doing the "getters and setters" you mentioned, plus more if you needed it.
A simple, useless example would be like the following:
var Api = (function () {
// private variable
var myArray = [];
return {
addItem: function (newItem) {
myArray.push(newItem);
},
printItems: function () {
console.log("lots if items");
}
};
})();
Api.addItem("Hello, world");
Api.extensionValue = 5;
I think you should make a clear delineation of what is shared, or "singleton" data, and keep those items private, as with myArray in my example.
Make it a constant:
const Api = "hi";
Api = 0;
alert(Api); //"hi"
Take a look at
Object.freeze
More info here
Here is a code example from Mozilla's page:
var obj = {
prop: function (){},
foo: "bar"
};
// New properties may be added, existing properties may be changed or removed
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
var o = Object.freeze(obj);
assert(Object.isFrozen(obj) === true);
// Now any changes will fail
obj.foo = "quux"; // silently does nothing
obj.quaxxor = "the friendly duck"; // silently doesn't add the property
// ...and in strict mode such attempts will throw TypeErrors
function fail(){
"use strict";
obj.foo = "sparky"; // throws a TypeError
delete obj.quaxxor; // throws a TypeError
obj.sparky = "arf"; // throws a TypeError
}
fail();
// Attempted changes through Object.defineProperty will also throw
Object.defineProperty(obj, "ohai", { value: 17 }); // throws a TypeError
Object.defineProperty(obj, "foo", { value: "eit" }); // throws a TypeError
However browser support is still partial
EDIT: see Kernel James's answer, it's more relevant to your question (freeze will protect the object, but not protect reassigning it. however const will) same issue with limited browser support though.
The only way (at least that I can think of) to protect your global variable is to prevent the Widgets from having a direct access to it. This can be achieved by using frames functions, as you suggested. You should create an object that contains all the functions that the Widgets should be able to use, and pass such to each Widget. For example:
var Api = {
widgetApi = {
someFunction: function(){
// ...
}
},
addWidget:function(){
var temp = this.widgetApi.constructor();
for(var key in this.widgetApi)
temp[key] = clone(this.widgetApi[key]);
return temp;
}
// Include other variables that Widgets can't use
}
This way, the Widgets could execute functions and communicate with the host or global variable Api. To set variables, the Widget would be editing its private object, rather than the global one. For every frame (that represents a Widget), you must initialize or create a copy of the widgetApi object, and probably store it inside an array, in such a way that an instance of a Widget is stored in the main Api object.
For example, given <iframe id="widget"></iframe>
You would do the following:
var widget = document.getElementById("widget");
widget.contentWindow.Api = Api.addWidget();
widget.contentWindow.parent = null;
widget.contentWindow.top = null;
Additionally, in every frame you would need to set the parent and top variables to null so that the Widgets wouldn't be able to access the data of the main frame. I haven't tested this method in a while, so there might be ways to get around setting those variables to null.

Does Javascript have something like Ruby's method_missing feature?

In Ruby I think you can call a method that hasn't been defined and yet capture the name of the method called and do processing of this method at runtime.
Can Javascript do the same kind of thing ?
method_missing does not fit well with JavaScript for the same reason it does not exist in Python: in both languages, methods are just attributes that happen to be functions; and objects often have public attributes that are not callable. Contrast with Ruby, where the public interface of an object is 100% methods.
What is needed in JavaScript is a hook to catch access to missing attributes, whether they are methods or not. Python has it: see the __getattr__ special method.
The __noSuchMethod__ proposal by Mozilla introduced yet another inconsistency in a language riddled with them.
The way forward for JavaScript is the Proxy mechanism (also in ECMAscript Harmony), which is closer to the Python protocol for customizing attribute access than to Ruby's method_missing.
The ruby feature that you are explaining is called "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.
It's a brand new feature that is present only in some browsers like Firefox (in the spider monkey Javascript engine). In SpiderMonkey it's called "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
Please read this article from Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ for more details about the upcoming implementation.
Not at the moment, no. There is a proposal for ECMAScript Harmony, called proxies, which implements a similar (actually, much more powerful) feature, but ECMAScript Harmony isn't out yet and probably won't be for a couple of years.
You can use the Proxy class.
var myObj = {
someAttr: 'foo'
};
var p = new Proxy(myObj, {
get: function (target, methodOrAttributeName) {
// target is the first argument passed into new Proxy, aka. target is myObj
// First give the target a chance to handle it
if (Object.keys(target).indexOf(methodOrAttributeName) !== -1) {
return target[methodOrAttributeName];
}
// If the target did not have the method/attribute return whatever we want
// Explicitly handle certain cases
if (methodOrAttributeName === 'specialPants') {
return 'trousers';
}
// return our generic method_missing function
return function () {
// Use the special "arguments" object to access a variable number arguments
return 'For show, myObj.someAttr="' + target.someAttr + '" and "'
+ methodOrAttributeName + '" called with: ['
+ Array.prototype.slice.call(arguments).join(',') + ']';
}
}
});
console.log(p.specialPants);
// outputs: trousers
console.log(p.unknownMethod('hi', 'bye', 'ok'));
// outputs:
// For show, myObj.someAttr="foo" and "unknownMethod" called with: [hi,bye,ok]
About
You would use p in place of myObj.
You should be careful with get because it intercepts all attribute requests of p. So, p.specialPants() would result in an error because specialPants returns a string and not a function.
What's really going on with unknownMethod is equivalent to the following:
var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
This works because functions are objects in javascript.
Bonus
If you know the number of arguments you expect, you can declare them as normal in the returned function.
eg:
...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
I've created a library for javascript that let you use method_missing in javascript: https://github.com/ramadis/unmiss
It uses ES6 Proxies to work. Here is an example using ES6 Class inheritance. However you can also use decorators to achieve the same results.
import { MethodMissingClass } from 'unmiss'
class Example extends MethodMissingClass {
methodMissing(name, ...args) {
console.log(`Method ${name} was called with arguments: ${args.join(' ')}`);
}
}
const instance = new Example;
instance.what('is', 'this');
> Method what was called with arguments: is this
No, there is no metaprogramming capability in javascript directly analogous to ruby's method_missing hook. The interpreter simply raises an Error which the calling code can catch but cannot be detected by the object being accessed. There are some answers here about defining functions at run time, but that's not the same thing. You can do lots of metaprogramming, changing specific instances of objects, defining functions, doing functional things like memoizing and decorators. But there's no dynamic metaprogramming of missing functions as there is in ruby or python.
I came to this question because I was looking for a way to fall through to another object if the method wasn't present on the first object. It's not quite as flexible as what your asking - for instance if a method is missing from both then it will fail.
I was thinking of doing this for a little library I've got that helps configure extjs objects in a way that also makes them more testable. I had seperate calls to actually get hold of the objects for interaction and thought this might be a nice way of sticking those calls together by effectively returning an augmented type
I can think of two ways of doing this:
Prototypes
You can do this using prototypes - as stuff falls through to the prototype if it isn't on the actual object. It seems like this wouldn't work if the set of functions you want drop through to use the this keyword - obviously your object wont know or care about stuff that the other one knows about.
If its all your own code and you aren't using this and constructors ... which is a good idea for lots of reasons then you can do it like this:
var makeHorse = function () {
var neigh = "neigh";
return {
doTheNoise: function () {
return neigh + " is all im saying"
},
setNeigh: function (newNoise) {
neigh = newNoise;
}
}
};
var createSomething = function (fallThrough) {
var constructor = function () {};
constructor.prototype = fallThrough;
var instance = new constructor();
instance.someMethod = function () {
console.log("aaaaa");
};
instance.callTheOther = function () {
var theNoise = instance.doTheNoise();
console.log(theNoise);
};
return instance;
};
var firstHorse = makeHorse();
var secondHorse = makeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
This doesn't work for my use case as the extjs guys have not only mistakenly used 'this' they've also built a whole crazy classical inheritance type system on the principal of using prototypes and 'this'.
This is actually the first time I've used prototypes/constructors and I was slightly baffled that you can't just set the prototype - you also have to use a constructor. There is a magic field in objects (at least in firefox) call __proto which is basically the real prototype. it seems the actual prototype field is only used at construction time... how confusing!
Copying methods
This method is probably more expensive but seems more elegant to me and will also work on code that is using this (eg so you can use it to wrap library objects). It will also work on stuff written using the functional/closure style aswell - I've just illustrated it with this/constructors to show it works with stuff like that.
Here's the mods:
//this is now a constructor
var MakeHorse = function () {
this.neigh = "neigh";
};
MakeHorse.prototype.doTheNoise = function () {
return this.neigh + " is all im saying"
};
MakeHorse.prototype.setNeigh = function (newNoise) {
this.neigh = newNoise;
};
var createSomething = function (fallThrough) {
var instance = {
someMethod : function () {
console.log("aaaaa");
},
callTheOther : function () {
//note this has had to change to directly call the fallThrough object
var theNoise = fallThrough.doTheNoise();
console.log(theNoise);
}
};
//copy stuff over but not if it already exists
for (var propertyName in fallThrough)
if (!instance.hasOwnProperty(propertyName))
instance[propertyName] = fallThrough[propertyName];
return instance;
};
var firstHorse = new MakeHorse();
var secondHorse = new MakeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
I was actually anticipating having to use bind in there somewhere but it appears not to be necessary.
Not to my knowledge, but you can simulate it by initializing the function to null at first and then replacing the implementation later.
var foo = null;
var bar = function() { alert(foo()); } // Appear to use foo before definition
// ...
foo = function() { return "ABC"; } /* Define the function */
bar(); /* Alert box pops up with "ABC" */
This trick is similar to a C# trick for implementing recursive lambdas, as described here.
The only downside is that if you do use foo before it's defined, you'll get an error for trying to call null as though it were a function, rather than a more descriptive error message. But you would expect to get some error message for using a function before it's defined.

Categories