Create a Storage subclass with proto - javascript

I'm having a JS black out, or I'm not as good as I thought...
I want to create a class Sites that extends Storage (localStorage and sessionStorage). I want that because Storage has named items AND .length. Perfect for what I want.
So this I did (it's namespaced in rweb):
rweb.Sites = function Sites() {
// There might be something useful in Storage's constructor?
Storage.call(this);
this.STORAGE = 'sync';
};
rweb.Sites.prototype = Object.create(Storage.prototype);
rweb.Sites.prototype.constructor = rweb.Sites;
I think that's how I usually do JS classes...: create constructor, assign other prototype to it, override constructor.
I override the constructor, so that:
var sites = new rweb.Sites;
sites.constructor == rweb.Sites;
> true
It fails in the new constructor:
TypeError: Object function Storage() { [native code] } has no method 'call'
How can function Storage not be callable? (Fair enough, it's not fully exposed.) How can I use its constructor?
If I remove the Storage.call from my constructor, I can create object sites, but then when I try to use it's interface:
var sites = new rweb.Sites;
> Sites {}
sites.setItem('foo', 'bar');
it tells me:
TypeError: Illegal invocation
What?? If I console.log(sites.setItem) I can see it's really the function I mean:
function setItem() { [native code] }
Am I doing it completely wrong or did they really not expose enough of Storage for it to be reusable?
BTW: I can extend Storage and use it:
Storage.prototype.foo = function() {
return 'bar';
};
sites.foo(); // returns 'bar' and no errors
so the prototyping works I think...
I'm using Chrome and it will all be a Chrome extension, so it can be hightech. (I hear there's a setPrototypeOf coming.)

Storage isn't fully exposed and is not extendable.
Probably too much dangerous magic inside.

Related

ES6: Instantiate class without calling constructor

Is there any way how to instantiate new class instance without calling its constructor?
Something like this:
class Test {
constructor(foo) {
this.foo = 'test';
}
}
const a = new Test('bar'); // call constructor
const b = Test.create(); // do not call constructor
console.log(a.foo, a instanceof Test); // bar, true
console.log(b.foo, b instanceof Test); // undefined, true
I am trying to develop TS mongo ORM, and would like to use constructors of entities for creating new objects, but do not want to call them when instancing entities of already persisted objects (those that are already stored in DB).
I know that doctrine (PHP ORM) uses this approach, but afaik they are using proxy classes to achieve it. Is there any easy way to achieve this in typescript (or generally in ES6/ES7)?
I already found this question ES6: call class constructor without new keyword, that asks for the opposite, and saw one answer mentioning Proxy object. That sounds like a possible way to go, but from the docs I am not really sure if it is achievable.
You can add a static method create, that create an Object from the class prototype. Something like that should work:
class Test {
constructor(foo) {
this.foo = foo
}
static create() {
return Object.create(this.prototype)
}
}
const a = new Test('bar') // call constructor
const b = Test.create() // do not call constructor
console.log(a.foo, a instanceof Test) // bar, true
console.log(b.foo, b instanceof Test) // undefined, true

Qml property hooks

Suppose there is a Qml type called Test with foo property, it is implemented as follows:
Test.qml
// Not necessarily QtObject, but derived from QObject
QtObject {
// Not necessarily var/property, but must behave like property (i.e. be accessible using "<instance>.foo")
property var foo
}
And used in the following way:
main.qml
import "."
Test {
id: _test
}
function baz() {
var x = _test.foo;
_test.foo = x;
}
What I want is to be notified every time foo property is accessed by getter or setter. For setter perhaps I can use fooChanged signal, but there is no such solution for getter. Also I could implement Test type as a C++ class with Q_PROPERTY macro and emit corresponding signals form property getter/setter, but I would really like to leave most of the code on Qml side.
Is there any other way to hook Qml properties access?
Ok, after some hours of googling, reading Qml and JS documentation I think I have finally found a solution. It involves some hacks since JS is not fully supported in Qml, but this solution still works. The main idea is to use JS defineProperty method in order to add properties to existing objects dynamically. Now my Test.qml file looks like this:
QtObject {
// NOTE: .defineProperty does not correctly work for Qml objects, so JS object is used instead
property var object: { return {}; }
Component.onCompleted: {
Object.defineProperty(
object,
"foo",
{
get: function () {
// getter handling here
},
set: function (value) {
// setter handling here
}
}
);
}
}
And in main.qml _test instance is declared as follows:
property alias _test: _testDeclaration.object
Test {
id: _testDeclaration
}
Hope this answer will help other people.

JSHint with ECMAScript6: method is not defined

I'm implementing a client-side application using ECMAScript6 and use JSHint for static code analysis. I often use the following pattern in my code:
class MyClass {
constructor() {
//This is how I would like to call myMethod
myMethod();
//This is how I should call myMethod to make JSHint analysis pass
this.myMethod();
}
myMethod(){
//Implementation
}
}
My primary language is Java so I expect that simply calling myMethod() should be ok. However without adding this to method call I'm getting "'myMethod' is not defined" warning from JSHint. My questions are:
Is it correct to make calls without this in such situation? (e.g. in PHP you always need to add $this-> to non-static method call)
If that's correct to make calls without this is there any way (any .jshintrc flag) to turn off this warning in JSHint?
No, this is and never was correct in JavaScript. Methods always need to be called on a receiver explicitly to make this work, and they need to be referred to using property access notation because methods are just functions on properties in javascript. They're not available as functions in the scope of your other methods. It's the same for properties, btw.
JsHint is right here, and there's no reason to turn that warning off. Even if that may possible, executing your program in spite of that would just make it not work.
Is it correct to make calls without this in such situation? (e.g. in
PHP you always need to add $this-> to non-static method call)
No, it is not. You always have to specify the receiver of the method.
If that's correct to make calls without this is there any way (any
.jshintrc flag) to turn off this warning in JSHint?
JSHint returns "'myMethod' is not defined" warning correctly as there is not function called myMethod in the scope of the constructor.
In the code you provided the identifier myMethod isn't defined, but the inherited property myMethod of instances of MyClass is defined.
If you define myMethod as a Function under a closure which isn't available elsewhere then you can access as it in the form you desire
var MyClass = (function () {
function myMethod() {
//Implementation
}
class MyClass {
constructor() {
myMethod();
}
}
return MyClass;
}());
I don't get to write much ES6 so I'm not sure if putting the function myMethod inside MyClass's definition is a SyntaxError
Please note however that this is required to reference your specific instance of MyClass, so you'll probably need to use it somewhere if you want MyMethod to act on the instance.
function myMethod(obj) {...}
// ...
myMethod(this);
If you read the MDN's description of class
JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JS classes provide a much simpler and clearer syntax to create objects and dealing with inheritance.
This is saying using class is just shorthand for the old way of doing it, not a new model, so it may be easier to think of what your current code would look like if written in ES5,
var MyClass = (function () {
function MyClass() {
this.constructor.apply(this, arguments);
}
MyClass.prototype = Object.create(null);
MyClass.prototype.constructor = function () {
myMethod(); // referenceError
this.myMethod(); // works
};
MyClass.prototype.myMethod = function () {
//Implementation
};
return MyClass;
}());

extending an object with new functions in Javascript

Basically Dub and Dub.socialize objects already exist as an included library. I'm trying to extend the library with some additional custom functions that I created.
I attempted the following concept below:
Dub.socialize = {
setUID : function(preUID, postUID)
{
// .. function code here
}
}
However, I receive the following error "Uncaught TypeError: Cannot set property 'setUID' of undefined" from my console.
Obviously my knowledge of objects is a bit misled. What would be the proper method of extending this function into the already existing object library?
A simple solution could be
Dub.socialize.setUID = function(preUID, postUID) {};
Try this:
Dub.socialize.prototype.setUID = function(preUID, postUID) {
...
};
Object Constructor and prototyping
Edit: Realized you're working with a "static" object. This only works for something that is instantiated, and since you're not making new instances, this doesn't apply.
If you are going to create the function for declared object means then you have to use "prototype" keyword for example.
`var Dub = {
socialize: new Object()
};
Dub.socialize.prototype.setUID = function(preUID, postUID) {
// Function Body
};`
http://www.javascriptkit.com/javatutors/proto3.shtml

JSON.stringify missing superclass properties when using a subclass with Spine

I'm seeing strange behaviour using JSON.stringify against a subclassed model in Spine, and I'm hoping someone can help!
Here's a simplified excerpt from some code that we've got on one of our projects:
define([
"jquery",
"underscore"
],
function ($, _) {
var SuperClass = Spine.Model.sub();
SuperClass.configure("SuperClass", "SuperClassProperty");
var SubClass = SuperClass.sub();
SubClass.configure("SubClass", "SubClassProperty");
var instance = new SubClass({ SuperClassProperty: "Super", SubClassProperty: "Sub" });
console.log(instance);
var json = JSON.stringify(instance);
console.log(json);
});
The "console.log(instance)" is printing out exactly what I would expect in this scenario:
result
SubClassProperty: "Sub"
SuperClassProperty: "Super"
cid: "c-0"
__proto__: ctor
However, when I use JSON.stringify against the instance, this is all that I am returned:
{"SubClassProperty":"Sub"}
Why doesn't the SuperClassProperty get included in the stringify?
I've ruled out a problem with the JSON.stringify method by forcing JSON2 to override Chrome's native JSON object; both implementations yield the same result. It looks like stringify will delegate to the "toJSON" function on the object if there is one - and in this case there is (as part of Spine).
So it looks like either (a) this is a bug in Spine, or (b) I'm doing something incorrectly, which is the more likely option.
I know I can work around this problem by re-configuring the superclass properties on the subclass as well:
SubClass.configure("SubClass", "SuperClassProperty", "SubClassProperty");
However this seems counter-intuitive to me (what's the point of subclassing?), so I'm hoping that's not the answer.
Update: I've done some debugging through the Spine source code, and from what I can tell the problem is the way that I'm configuring the subclass:
var SubClass = SuperClass.sub();
SubClass.configure("SubClass", "SubClassProperty");
Calling "configure" here appears to wipe out the attributes from SuperClass. The "toJSON" implementation on the Model prototype is as follows:
Model.prototype.toJSON = function() {
return this.attributes();
};
Since the attributes collection is reset when SubClass is configured, the SuperClass properties don't come through in the JSON string.
I'm not sure if I shouldn't be calling "configure" on subclassed objects, but I can't find anywhere in the documentation that says I should be doing something else - this is the only reference I can find for subclassing Models (from: http://spinejs.com/docs/models):
Models can be also be easily subclassed:
var User = Contact.sub();
User.configure("User");
As I suspected, the problem was in the way that I'm using Spine. This comment from the author of Spine infers that using "configure" on a subclass will wipe out the attributes of the superclass. I have to admit I don't understand why this is; it seems counter-intuitive to me, but at least I now know that it's not a bug.
In case anyone else runs into this issue, the way I've worked around it is by adding my own extension method to the Spine Model as follows:
(function () {
var Model = Spine.Model;
Model.configureSub = function () {
var baseAttributes = this.attributes.slice();
this.configure.apply(this, arguments);
this.attributes = baseAttributes.concat(this.attributes);
return this;
};
})();
Now to configure my subclass:
var SubClass = SuperClass.sub();
SubClass.configureSub("SubClass", "SubClassProperty");
And now my JSON correctly reflects the properties from both the super and subclasses.

Categories

Resources