Chrome Debugger: What's a "PropertyBag" object? - javascript

Using the Chrome Debugger in the following way:
console.dir(element);
On the console the data-type is called a "PropertyBag". I would have expected "Object".
What special kind of object is a "PropertyBag"?
I have never read that term in JavaScript before ...

As you said in the above comments, you are using some code written by somebody else (or a custom framework) and element is just an instance of a custom class. For example, the PropertyBag class exists in cesiumjs.
If the object is an instance of a class (not a simple object, really a class with a constructor), if you use console.log on that item, you'll get the class name in the console (at least in Chrome) and a little arrow to expand it. You can copy/paste the following code in the console to test the behavior.
class User {
constructor(name) {
this.name = name;
}
sayHi() {
alert(this.name);
}
}
let user = new User("John");
console.log(user);
Cheers!

Related

How to get list of class methods in JavaScript?

I have the following class in JavaScript.
class User {
get(id) {
return {id};
}
}
I'm trying to figure out how to get a list of the methods from an instance.
const user = new User();
Basically this means, I'm trying to find code where the input is user and the output is ["get"].
I have tried the following, but they all print [] (which is not what I expect).
console.log(Object.keys(user)); // []
console.log(Object.keys(Object.getPrototypeOf(user))); // []
console.log(Object.getOwnPropertyNames(user)); // []
I know user.get exists because the following line works and prints [Function: get]:
console.log(user.get); // [Function: get]
Obviously this works for properties, and prototype functions. But I'm trying to figure out how to get this to work with class methods. So that means that any solution can not change the User class.
How can I achieve this?
You need to get the prototype of the instance first:
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(u)));
prints
["constructor", "get"]

Javascript square bracket notation couldn't be evaluated properly while using an android webview. Any advice to fix it?

I'm using android webview and want to make dynamic function calls over the Javascript Interface. My interface's name is AndroidBridge. And here is my code:
AndroidBridge[key](values[key]);
It works properly on the browser. But when it comes to my webview, is throwing the error below:
Error connecting controller TypeError: AndroidBridge[key] is not a function [object Object]"
Any advice to jump over that issue?
I can give you the two possible reasons for that issue now:
Missing #JavascriptInterface annotation.
The method is missing or params not matching.
I don't know why browser's js interpreter looks to "a square named version of that object", but method names that match should work without that errors.
I can leave here a fully working example:
class JsBridge {
#JavascriptInterface
fun showToast(message: String) {
Log.d("DEBUG", "JsBridge showToast message:$message")
val currentActivity = current ?: return
Toast.makeText(currentActivity, message, Toast.LENGTH_SHORT).show()
}
companion object {
#JvmStatic
val instance: JsBridge = JsBridge()
}
}
// Somewhere in your code
webView.addJavascriptInterface(JsBridge.instance, "AndroidBridge")
Now you can call that method in the js side:
AndroidBridge.showToast('A message');
// or
AndroidBridge['showToast']('A message');
They both should work.

Jasmine Unit Tests Unexpected this Reference

I am trying to test a fairly simple JavaScript function in Jasmine, however the first statement is throwing an error for being undefined.
myClass.prototype.functiontoBeTested = function() {
var x = this.method()
...
}
The above throws an error in Jasmine as method is not a function and is undefined. The prototype is altered earlier to have this method, and out of curiosity I assigned this method to my test object in the spec itself as such:
myObject.method = function(){return mockResults;};
So I decided to log this to the console and instead of it being my object, I see Window {top: Window, location: Location, document: document, window: Window, external: Object…} which doesn't seem right. I've had this issue before with testing a function in Jasmine that used this but just changed the source code to refer to the object by name since the function was being assigned to something within the closure. I can't do that in this case, and I'm curious why this is referring to something unexpected (at least to me).
Edit: Some details on what the test case looks like as requested:
it("updates the control count", function(){
var mockResults = { ... };
myObject.method = function() {return mockResults;};
expect(myObject.method).not.toThrow();
});
Right now I'm just trying to get the method to execute to completion during the test. The function to be tested updates the text on some HTML components, I'll work on verifying those once I can get it to actually run. The method that is causing an error is the first line of the function, and is simply an accessor method for the object being called. In actual execution, var x = this.method() runs without issue. When testing in jasmine var x = this.method() throws an error because method() is undefined for this. Instead of this referring to the calling object, it is referring to the window. This doesn't happen live, but only during testing with Jasmine. This method is undefined even when I forcibly define it for the test object just prior to execution in the test as above. That's when I decided to log this to console in the source code and realized it isn't referring to what I would have expected it to refer to.
In JavaScript this for a method depends on the context it was called from. When you do a call myObject.method(), then method was called from the context of myObject, therefore this is myObject.
When you pass your function to Jasmine toThrow matcher, it calls it as it was passed (see source code):
try {
actual();
} catch (e) {
// ....
}
Here, actual() is a call of your method. It has no any specific context, so by default it will be called from window context.
Solution is to explicitly bind you method to myObject like the following:
expect(myObject.method.bind(myObject)).not.toThrow();
Credits to questions, you can find more details there:
Does Jasmine's toThrow matcher require the argument to be wrapped in an anonymous function?
How to write a test which expects an Error to be thrown in Jasmine?

Inheritance and "TypeError: Cannot read property 'someProperty' of undefined"

PROBLEM: When inheriting an object's properties and methods, a child object seems to loose connection with parent's 'this'. To illustrate it better, look at my example:
function RigidBody() {
this.local = new Matrix4x3();
this.position = new vec3();
...
}
RigidBody.prototype = {
...
setPosition: function(vec) {
this.position.copy(vec);
this.local.setTranslation(this.position);
}
...
};
CameraObject.prototype = new RigidBody();
CameraObject.prototype.constructor = CameraObject;
function CameraObject() {
this.fov = ...
}
CameraObject.prototype.add = ...;
var camera = new CameraObject();
camera.add(...); // Works fine;
camera.setTranslation(...); // Throws "TypeError: Cannot read property 'setTranslation' of undefined
// And on another PC it throws: "http://localhost:8080/js/rigidBody.js(61): Uncaught exception: TypeError: Cannot convert 'this.local' to object"
How to bypass it? I found a workaround for this problem by assigning this.this = this; to parental object and replacing every this by this.this. Unfortunately as a result I need to add .this to every camera function call like this: camera.this.setPosition(...);
as a general advice, please add console.log(camera) to your code and inspect the object in a good browser console, I highly recommend Firebug
that way you can explore what properties and nested properties are available to camera object
from your code example in original question, setTranslation appears to be a property of camera.local, not camera itself.
or perhaps you wanted to call camera.setPosition(...); given that you added it to RigidBody's prototype, but never used it afterwards - jsfiddle?
and given the code provided in comment below:
function Matrix4x3(){
this.element = new Float32Array(16);
}
which does not define setTranslation, so this.local.setTranslation might be undefined as well..
You need to call "super", in other words, inherit the properties from the parent constructor:
function CameraObject() {
RigidBody.call(this); // inherit RigidBody properties
this.fov = ...
}

Video.js: Adding a menu to the control bar via plugin

I'm trying to a menu to the control bar of a video.js player via a plugin. Here's a demo of my end goal. The problem is that the demo is adding a menu to the control bar in video.js itself whereas I want a plugin that adds a menu.
So far I have been able to create a plugin that adds a MenuButton to the control bar, but I'm unable to populate the menu with any menu items.
Here's a demo of where I'm at now.
The problem is when I try to create a new PlaybackRateMenuItem object with new videojs.PlaybackRateMenuItem(this, {rate: rates[i] + 'x'}). The JS console throws the following error on line 805 of video.dev.js:
Uncaught TypeError: Cannot read property 'guid' of undefined
This corresponds to the following function in the unminified version:
vjs.bind = function(context, fn, uid) {
// Make sure the function has a unique ID
if (!fn.guid) {
fn.guid = vjs.guid++;
}
... omitted ...
};
Or more specifically, this line: if (!fn.guid) {.
From stepping through in a debugger, I see that fn is being passed in as undefined and the caller of the function is:
vjs.bind(this.player(), this.player().reportUserActivity);
When this function is being called, this.player() returns a defined value, but once vjs.bind is called, both arguments become undefined and I have no idea why. Any ideas or a point in the right direction? Thanks!
Turns out I had two major problems here.
Overriding the createMenu method instead of the createItems method for my class that extended the videojs.MenuButton class.
The this argument to the constructor of the videojs.MenuItem class must be the videojs player object, not just this. Problem is that the name of this object is mangled in the minified version of video.js. For this situation, I found that using this.c was the player object, but overall, I found it easier to just use the unminified version, video.dev.js, and not have to deal with the name mangling. In that case, using this.player_ was correct.

Categories

Resources