Replace customElements.define with custom logic - javascript

Is it possible to modify customElements.define and use a custom function instead?
This is what I have tried so far, but obviously this does not work because window.customElements is read only.
const future = ["hello-world"];
let customElementsRegistry = window.customElements;
const registry = {};
registry.define = function(name, constructor, options) {
if (future.includes(name)) {
customElementsRegistry.define(name, constructor, options);
}
}
window.customElements = registry;
window.customElements.define("hello-world", HelloWorld);
Is this possible or is my only option to create my own registry and just use that one?

The property is still configurable so you can redefine it. Though this doesn't look like a great idea for production app.
const future = ["hello-world"];
let customElementsRegistry = window.customElements;
const registry = {};
registry.define = function(name, constructor, options) {
console.log('hijacked')
if (future.includes(name)) {
customElementsRegistry.define(name, constructor, options);
}
}
Object.defineProperty(window, "customElements", {
get() {
return registry
}
})
window.customElements.define("hello-world", class HelloWorld extends HTMLElement {
constructor() {
super()
this.innerHTML = "Hello World"
}
});
<hello-world>Test</hello-world>

Related

call a methods of a class without using NEW keyword inside other class node js

I want to access Main class methods to another Person class without creating a new instance Is it possible??
Can we access it without creating an instance of a class
let myInstance = new Person();
class Main {
constructor(args) {
this.hooks = [];
}
add_hooks(name, func) {
if (!this.hooks[name]) this.hooks[name] = [];
this.hooks[name].push(func);
}
call_hooks(name, ...params) {
if (this.hooks[name]) this.hooks[name].forEach((func) => func(...params));
}
}
other class Person how to access without using new keyword
const Main = require("./main.js");
class Person {
exec() {
const action = Main();
action.add_hook("jump", console.log.bind(console, "this will log "));
}
}
There is no big magic to it. Since the OP just wants to reuse prototypal Main methods, one is going to explicitly delegate the method/s of interest which was/were provided/accessed before via Main.prototype ...
class Main {
constructor(args) {
this.hooks = {};
}
add_hooks(name, func) {
if (!this.hooks[name]) {
this.hooks[name] = [];
}
this.hooks[name].push(func);
}
call_hooks(name, ...params) {
if (this.hooks[name]) {
this.hooks[name].forEach(func => func(...params));
}
}
}
// const Main = require("./main.js");
class Person {
// // ... either add `hooks` as public property at instantiation time ...
// hooks = {};
exec() {
const ref = Main.prototype;
ref.add_hooks.call(this, "jump", console.log.bind(console, "this will log"));
}
}
// ... or add `hooks` via additional glue code ...
function createPersonWithHooksAndExecute() {
const type = new Person();
type.hooks = {};
type.exec();
return type;
}
const someone = createPersonWithHooksAndExecute();
console.log({ someone });
// this will log
Main.prototype.call_hooks.call(someone, "jump");
.as-console-wrapper { min-height: 100%!important; top: 0; }
If you're not planning on instantiating the object, and you don't care about having multiple instances with each having their own state, you don't need a class.
Just create individual functions, or export an object.
const hooks = [];
export function add_hooks(name, func) {
if (!hooks[name]) hooks[name] = [];
hooks[name].push(func);
}
export function call_hooks(name, ...params) {
if (!hooks[name]) return;
for (const func of this.hooks[name]) {
func(...params);
}
}
It's possible too to do this with static methods, and that would be the likely answer if you write Java where everything has to be a class, but I wouldn't recommended it in Javascript.

Force the use of setters instead of straight assignments in a JS ES6 Class

I have a CameraBuilder class that looks like this:
class CameraBuilder {
constructor() {
if (arguments.length) {
throw new Error('[CameraBuilder constructor ERROR] class constructor does not accept parameters.');
}
this.camera = {};
}
withFarmLabel(farmLabel) {
this.camera.farm_label = farmLabel;
return this;
}
// more methods here
build() {
const missingProps = [];
if (!this.camera.farm_label) {
missingProps.push('\nMissing farm_label property. Use the withFarmLabel method in order to assign it.');
}
// more validations like the one above here
if (missingProps.length) {
const errorMsg = missingProps.join('');
throw new Error(`[CameraBuilder build ERROR] ${errorMsg}`);
}
return this.camera;
}
}
Since most of my validations are on the build() method and there are some business logic on some of these methods associated with how the user is building an instance of CameraBuilder, I wouldn't want anyone assigning cameraBuilderObj.camera directly. Is there any way I can enforce the use of the Class methods in order to assign properties to the Camera object?
You could make the camera property private by putting # in front of it, ensuring that only CameraBuilder's internals can reference it:
class CameraBuilder {
#camera = {};
constructor() {
if (arguments.length) {
throw new Error('[CameraBuilder constructor ERROR] class constructor does not accept parameters.');
}
}
withFarmLabel(farmLabel) {
this.#camera.farm_label = farmLabel;
return this;
}
// more methods here
build() {
const missingProps = [];
if (!this.#camera.farm_label) {
missingProps.push('\nMissing farm_label property. Use the withFarmLabel method in order to assign it.');
}
// more validations like the one above here
if (missingProps.length) {
const errorMsg = missingProps.join('');
throw new Error(`[CameraBuilder build ERROR] ${errorMsg}`);
}
return this.#camera;
}
}
const c = new CameraBuilder();
c.withFarmLabel('label');
console.log(c.camera);
console.log(c.build().farm_label);
CertainPerformance's answer probably makes more sense--don't expose it in the first place--but if for some reason you didn't want to go that route (or if you're in an environment where private fields aren't supported) you could define setters on it, so that direct assignments go through your function.
class Foo {
constructor () {
this._bar = 'baz';
}
set bar (value) {
this._bar = value;
console.log('do whatever you want to do here.');
}
}
const f = new Foo();
f.bar = 'hey'; // direct assignment invokes the setter

How to define a public property in the javascript class?

With ES5 constructor and prototype approach I can add public (prototype) properties as below:
function Utils(){}
Utils.prototype.data = {};
var utils = new Utils();
console.log(utils.data); //{}
The ES6 class allows me to define only public methods in the class. I build an app with a class-approach and I don't want to mix constructors and classes features. The working code that I figured out is:
class Utils(){
get _data(){
const proto = Object.getPrototypeOf(this);
if(!proto._status) proto._data = {};
return proto._data;
}
}
const utils = new Utils();
console.log(utils._data); //{}
When I call _data getter method, it checkes whether the _data property exists in the prototype object. If so, it returns it, otherwise it initiates the _data property.
Is it a good practice? Is there any other way to do it better?
To make data a public instance property:
class Utils {
constructor () {
this.data = {}
}
}
To make data a public static property, get/set is probably the best way:
let data = {}
class Utils {
get _data () {
return data
}
set _data (d) {
data = d
}
}
I don't know if the code you provided is your full code or not, but when I run it, it throws an error:
class Utils {
get _data(){
const proto = Object.getPrototypeOf(this);
if(!proto._status) proto._data = {};
return proto._data;
}
}
/* TEST */
const a = new Utils();
a._data.ok = 'ok';
const b = new Utils();
console.log(b._data.ok);
If I understand you correctly, you want all instances of Utils to share the same data property.
There is a few ways that I can think of to do what you need, but it might "mix constructors and classes features" (I don't really get what you mean by that).
1: Good ol' ES5 way
class Utils {}
Utils.prototype.data = {};
/* TEST */
const a = new Utils();
a.data.ok = 'ok';
const b = new Utils();
console.log(b.data.ok);
2: Same as your way, but in it's constructor
class Utils {
constructor(){
if (!this.data) {
Utils.prototype.data = {};
}
}
}
/* TEST */
const a = new Utils();
a.data.ok = 'ok';
const b = new Utils();
console.log(b.data.ok);
Though, as the data property needs to be shared across instances, I'd suggest you to add the property to its prototype using Object.defineProperty method to make it unwritable and unconfigurable:
Object.defineProperty(Utils.prototype, 'data', {
value: {},
writable: false,
enumerable: true,
configurable: false
});
This is to ensure the data property cannot be reassigned or deleted, thus minimising the chance of mistakenly reset the data or etc.
I'd recommend the first way (with Object.defineProperty) because it is :
More foolproof
Clearer
Easier to maintain

ES6 Classes - Updating Static Properties

I am trying to figure out alternative ways to set a static (or class) property an ES6 Class and then change it after new instances of the class are created.
For example, lets say I have a class called Geo, and I need a static property called all that will give me the array of all instances of the Geo class.
This version works:
class Geo {
constructor(name){
this.name = name;
Geo.all.push(this);
}
}
Geo.all = [];
ruby = new Geo("Ruby");
rocks = new Geo("Rocks");
console.log(Geo.all.length); // => 2
I would prefer to not set the property OUTSIDE of the class definition though. I've tried a few things but can't seem to create a static property within the class that I can update from the constructor.
I should also mention I need to be able to do this in the browser (Chrome) without use of Babel or similar.
Here are examples of some things I've tried:
class Geo {
constructor(name){
this.name = name;
Geo.all.push(this);
}
static get all() {
return [];
}
}
ruby = new Geo("Ruby");
rocks = new Geo("Rocks");
console.log(Geo.all.length); // => 0
And another
class Geo {
constructor(name){
this.name = name;
Geo.all.push(this);
}
static all = [];
}
ruby = new Geo("Ruby");
rocks = new Geo("Rocks");
console.log(Geo.all.length); // => error unexpected "="
There's no such thing as static all = [] in ES6. Class instance and static fields are currently stage 3 proposals which can be used via a transpiler, e.g. Babel. There's already existing implementation in TypeScript that may be incompatible with these proposals in some way, yet static all = [] is valid in TS and ES.Next.
Geo.all = [];
is valid and preferable way to do this in ES6. The alternative is getter/setter pair - or only a getter for read-only property:
class Geo {
static get all() {
if (!this._all)
this._all = [];
return this._all;
}
constructor() { ... }
}
Tracking instances in static property can't generally be considered a good pattern and will lead to uncontrollable memory consumption and leaks (as it was mentioned in comments).
This works for me for static properties.
class NeoGeo {
constructor() {
}
static get topScore () {
if (NeoGeo._topScore===undefined) {
NeoGeo._topScore = 0; // set default here
}
return NeoGeo._topScore;
}
static set topScore (value) {
NeoGeo._topScore = value;
}
}
And your example:
class NeoGeo {
constructor() {
NeoGeo.addInstance(this);
console.log("instance count:" + NeoGeo.all.length);
}
static get all () {
if (NeoGeo._all===undefined) {
NeoGeo._all = [];
}
return NeoGeo._all;
}
static set all (value) {
NeoGeo._all = value;
}
static addInstance(instance) {
// add only if not already added
if (NeoGeo.all.indexOf(instance)==-1) {
NeoGeo.all.push(instance);
}
}
}
Note: In the getter you could also check for the existence of the property using the in keyword or the hasOwnProperty keyword.
static get topScore () {
if (!("_topScore" in NeoGeo)) {
NeoGeo._topScore = 0; // set default here
}
return NeoGeo._topScore;
}
And using hasOwnProperty:
static get topScore () {
if (NeoGeo.hasOwnProperty("_topScore")==false) {
NeoGeo._topScore = 0; // set default here
}
return NeoGeo._topScore;
}
I recently had a similar issue of creating static classes.
I first tried it with constant class variables, but Chrome debugger threw an error.
So I defined the class variables 'static', also the getter methods.
Worked in Chrome.
class TestClass {
//static properties.
static _prop1 = [ 'A', 'B', 'C'];
static _prop2 = true;
static _prop3 = 'some String';
//constructor. Commented out because the class only has static elements.
//constructor () {}
//Getters.
static get prop1 () {
return this._prop1;
}
static get prop2 () {
return this._prop2;
}
static get prop3 () {
return this._prop3;
}
}
The only way to properly add a getter is to extend the class and use that extended class.
class Basic {
get firstGetter() {
return 'firstGetter'
}
}
class ExtendedClass extends Basic {
get firstGetter() {
return 'updatedFirstGetter'
}
}
}
Update your node to the version 12 or up and that's it ;)

React components as plain JS objects?

Does anybody has experience in working with React components as plain JS objects instead of annoying ES6 classes and deprecated .createClass method.
Maybe you have some examples of factory functions or similar to share?
Thanks!
React.Component is a plain javascript function, since es6 classes are syntactic sugar around them. So we could use whatever es5 class-like concept we like e.g. I just borrowed Backbone's extend method here:
// From backbone
var extend = function(protoProps) {
var parent = this;
var child;
var extendObj = function(obj1, obj2) {
for (var i in obj1) {
if (obj1.hasOwnProperty(i)) {
obj2[i] = obj1[i];
}
}
};
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent constructor.
if (protoProps && hasOwnProperty.call(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function() { return parent.apply(this, arguments); };
}
// Set the prototype chain to inherit from `parent`, without calling
// `parent` constructor function.
var Surrogate = function(){ this.constructor = child; };
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
// Add prototype properties (instance properties) to the subclass,
// if supplied.
if (protoProps) extendObj(child.prototype, protoProps);
// Set a convenience property in case the parent's prototype is needed
// later.
child.__super__ = parent.prototype;
return child;
};
React.Component.extend = extend;
Then we could create components like this:
var MyComponent = React.Component.extend({
constructor: function() {
console.log('hello from react component');
this.state = {
open: false
};
React.Component.apply(this, arguments);
}
});
new MyComponent();
That's just an example (and untested), you could do any kind of prototypal implementation you like since it's just a normal function. If you search for "es5 inheritance" you should be able to apply any of those solutions.
I think my answer is late. But I do make a lot of React Components using traditional prototype based javascript objects. If you love prototype based object, you can try the following :)
A simple example:
step 1: install inherits module
npm install inherits -S
then,
const React = require('react'); // switch to import, if you like
const is = require('prop-types');
const inherits = require('inherits');
inherits(MyComponent, React.Component);
module.exports = MyComponent;
var prototype = MyComponent.prototype;
MyComponent.defaultProps = {
onClick: function(){ }
};
MyComponent.propTypes = {
onClick: is.func,
href: is.string,
label: is.string
}
function MyComponent(props) {
React.Component.call(this, props);
this.state = {clicked: false};
}
prototype.render = function() {
return (
<a href={this.props.href} onClick={this.props.onClick}>
{this.props.label}
</a>)
}
// for debugging purpose, set NODE_ENV production, will remove the following
if (process.env.NODE_ENV !== 'production') {
MyComponent.displayName = 'MyComponent';
}
A more advanced way to separate your concerns is to put your methods in different files. ( Usually, the protected, or private methods, something you do not need to know after couple months or years.) Then, merge them onto the prototype object. You can do it in the following way.
...
const _proto = require('./prototype'); //make a prototype folder, and merge all files' methods into one.
...
var prototype = Object.assign(MyComponent.prototype, _proto);
Or, you want to make your component as an EventEmitter, you can do it like following:
....
const _proto = require('./prototype');
const Emitter = require('component-emitter');
....
var prototype = Object.assign(MyComponent.prototype, _proto, Emitter.prototype);
function MyComponent(props) {
React.Component.call(this, props);
this.onClick = _=> this.emit("click");
}
prototype.render = function() {
return <a href={this.props.href} onClick={this.onClick}>{this.props.label}</a>
}
In the prototype folder, you can write like following:
index.js
Object.assign(exports, require('./styles.js').prototype)
styles.js
const prototype = exports.prototype = {};
prototype.prepareStyles = function() {
var styles = Object.defineProperties({}, {
wrapper: { get: _=> ({
backgroundColor: '#333'
})},
inner: {get: _=> {
return this.state.clicked ? {...} : {...}
}}
});
Object.defineProperties(this, {
styles: {get: _=> styles}
})
}
//All the methods are prefixed by prototype, so it is easy to cut and paste the methods around different files, when you want to hide some methods or move some methods to be with the constructor to make your component more easy to read.
then, in the main file. simply call the method to prepare all the styles:
function MyComponent(props) {
React.Component.call(this, props);
this.prepareStyles();
}
and use the styles,
prototype.render = function() {
return (
<div style={this.styles.wrapper}>
<div styles={this.styles.inner}>hello world</div>
</div>
)
}

Categories