Some ES6 features are really easy to polyfill:
if(!Array.prototype.find){
Array.prototype.find=...
}
How would you polyfill new.target? It triggers a syntax error when it's used in an unsupported browser. try/catch doesn't work because it's a syntax error. I don't have to use new.target, I'm mostly just curious.
As Jaromanda commented, you cannot polyfill new syntax, but you can easily work around some new.target use cases for now
Taking a look at the new.target docs you'll see some examples that can easily be written with es5
with new.target
function Foo() {
if (!new.target) throw "Foo() must be called with new";
console.log("Foo instantiated with new");
}
Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"
without
function Foo() {
if (!(this instanceof Foo)) throw "Foo() must be called with new";
console.log("Foo instantiated with new");
}
Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"
with new.target
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A { constructor() { super(); } }
var a = new A(); // logs "A"
var b = new B(); // logs "B"
without
class A {
constructor() {
// class forces constructor to be called with `new`, so
// `this` will always be set
console.log(this.constructor.name);
}
}
class B extends A { constructor() { super(); } }
var a = new A(); // logs "A"
var b = new B(); // logs "B"
Hope this helps a little
Here's a way using Function::bind:
const requireNew = (() => {
const kFake = {};
const CtorMap = new WeakMap();
const FuncToString = function toString () {
const info = CtorMap.get(this);
return Function.prototype.toString.apply(
info ? info.ctor : this, arguments);
};
const GetProto = function prototype() {
const info = CtorMap.get(this);
return info ? info.ctor.prototype : undefined;
};
const SetProto = function prototype(prototype) {
const info = CtorMap.get(this);
return !info ? prototype
: info.wrapper.prototype = info.ctor.prototype = prototype;
}
return (Ctor) => {
const wrapper = function () {
if (this === kFake) {
throw new TypeError("Please use 'new' to call this");
}
return Ctor.apply(this, arguments);
}
wrapper.prototype = Ctor.prototype;
const bound = wrapper.bind(kFake);
CtorMap.set(bound, { ctor: Ctor, wrapper });
Object.defineProperties(bound, {
prototype: { get: GetProto, set: SetProto,
enumerable: false, configurable: true },
name: { value: Ctor.name, writable: false,
enumerable: false, configurable: true },
length: { value: Ctor.length, writable: false,
enumerable: false, configurable: true },
toString: { value: FuncToString, writable: true,
enumerable: false, configurable: true }
});
return bound;
}
})();
And here's a simple demo:
function Abc (a) {
this.a = a;
console.log("this =", this, "; .a =", this.a, "; new.target:", new.target);
}
const A = requireNew(Abc);
const a = new A(1);
Related
I am reviewing some Javascript code and stumbled upon a syntax that I didn't knew. The application is a React and Redux one, though I think this is plain Javascript.
The syntax I'm concerned with is the { f1(), f2(), ... } argument of combineReducers().
This is the syntax:
combineReducers({
Reducer1,
Reducer2,
...
});
ReducerN is a function, i.e.:
const Reducer1 = (state = INITIAL_STATE, action) => {
// ...
};
I get { f1(), ... } creates an object where the function name is the key and the function itself is the value, so in a browser console I tried the following:
a = () => { console.log(1) }
b = () => { console.log(2) }
o = {a, b}
and if I print o:
{a: ƒ, b: ƒ}
a: () => { console.log(1) }
b: () => { console.log(2) }
__proto__: Object
But if I try to initialize o in a single operation:
o = { () => return 1 }
or
o = { function y() { return 1 }}
they both give a syntax error.
It's the first time I see an object created with that syntax: What kind is that? Where can I find its reference?
As said previously,
combineReducers({
Reducer1,
Reducer2,
...
});
is equivalent to this in plain ES5:
combineReducers({
Reducer1: Reducer1,
Reducer2: Reducer2,
...
});
and combineReducers is concerned only with the values of the object passed in. The first form is just a shorthand for defining properties with the same name as the value. This is the reason you cannot use anonymous functions in this form. To define function members on classes and objects, you can use the following form:
class Foo {
foo() { console.log('foo'); }
bar = () => console.log('bar')
}
const a = new Foo();
a.foo();
a.bar();
const b = {
foo() { console.log('foo'); }
bar: () => console.log('bar')
};
b.foo();
b.bar();
When transpiling to plain ES5, this will generate the following:
"use strict";
var Foo = /** #class */ (function () {
function Foo() {
this.bar = function () { return console.log('bar'); };
}
Foo.prototype.foo = function () { console.log('foo'); };
return Foo;
}());
var a = new Foo();
a.foo();
a.bar();
var b = {
foo: function () { console.log('foo'); },
bar: function () { return console.log('bar'); }
};
b.foo();
b.bar();
{ f1() } is very different than { f1 }.
The latter is a shorthand of { f1: f1 } which is an object having the key 'f1' (a string) associated to the value f1 (a function). The function is not executed.
In the first example f1() is a function call. The function f1 is executed and the value it returns is used instead. But because you didn't provide a key to associate the value with and because f1() is a value that does not have a name (it is an expression that needs to be evaluated in order to get its value), JS cannot produce an object out of it.
{ f1 } can be evaluated at the compile time and turned into { f1: f1 }.
{ f1() } cannot be evaluated at the compile time. The value of f1() is available only at the run time.
This is why { f1() } is invalid code.
If you need to call f1 and use the value it returns to create an object you can do it this way:
const x = { f1: f1() }
This is the same thing as:
const v = f1();
const x = { f1: v }
Getting a function name is pretty straightforward:
const func1 = function() {}
const object = {
func2: function() {}
}
console.log(func1.name);
// expected output: "func1"
console.log(object.func2.name);
// expected output: "func2"
How can I get the string name of a getter/setter, though?
class Example {
get hello() {
return 'world';
}
}
const obj = new Example();
Important note:
I don't want to use a hard-coded string:
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), 'hello')
But get the name, e.g.:
console.log(getGetterName(obj.hello))
// expected output: "hello"
That syntax sets the get function of the hello property descriptor so the name of the function will always be get you can check if the hello property has a get function on it's property descriptor with Object.getOwnPropertyDescriptor().
class Example {
get hello() {
return 'world';
}
}
/*
compiles to / runs as
var Example = (function () {
function Example() {
}
Object.defineProperty(Example.prototype, "hello", {
get: function () {
return 'world';
},
enumerable: true,
configurable: true
});
return Example;
}());
*/
const des = Object.getOwnPropertyDescriptor(Example.prototype, 'hello');
console.log(des.get.name); // get (will always be 'get')
// to check if 'hello' is a getter
function isGetter(name) {
const des = Object.getOwnPropertyDescriptor(Example.prototype, name);
return !!des && !!des.get && typeof des.get === 'function';
}
console.log(isGetter('hello')); // true
Sounds like this won't solve your ultimate issue but:
Object.getOwnPropertyDescriptor(Example.prototype, 'hello').get.name
100% answers the question "How to get getter/setter name in JavaScript/TypeScript?" and it will always be "get"
Edit:
Once you call obj.hello the getter is already called an all you have is the primitive result, but you may be able to use metadata on the property value its self.
function stringPropertyName() {
let _internal;
return (target, key) => {
Object.defineProperty(target, key, {
get: () => {
const newString = new String(_internal);
Reflect.defineMetadata('name', key, newString);
return newString;
},
set: value => {
_internal = value;
}
});
};
}
class Example1 {
#stringPropertyName()
hello = 'world';
}
const obj1 = new Example1();
console.log(Reflect.getMetadata('name', obj1.hello)); // hello
class Example2 {
_hello = 'world';
get hello() {
const newString = new String(this._hello);
Reflect.defineMetadata('name', 'hello', newString);
return newString;
}
set hello(value) {
this._hello = value;
}
}
const obj2 = new Example2();
console.log(Reflect.getMetadata('name', obj2.hello)); // hello
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.6.11/core.min.js"></script>
I have the following code for inheritance:
SubClass= function () {
ParentClass.call(this);
}
SubClass.prototype = Object.create(ParentClass.prototype);
SubClass.prototype.constructor = SubClass;
However, I want to define some properties in the subclass as well:
SubClass.prototype = {
get x() {
return this.newX;
},
set x(val) {
this.newX = val;
alert("X has a value of " + this.newX);
}
}
The problem I'm having is combining the two. In other words, in the first code sample I'm saying:
SubClass.prototype = Object.create(ParentClass.prototype);
But then in the second code sample I'm saying:
SubClass.prototype = {...
How can I achieve both? What is the syntax that would allow me to inherit from a parent class and define properties using the same prototype definition?
Thank you :)
Define your properties by passing a property descriptor to Object.defineProperty:
Object.defineProperty(SubClass.prototype, 'x', {
configurable: true,
get: function () {
return this.newX;
},
set: function (val) {
this.newX = val;
alert("X has a value of " + this.newX);
},
});
It’s also possible to pass an object containing property descriptors to Object.create:
function SubClass() {
ParentClass.call(this);
}
SubClass.prototype = Object.create(ParentClass.prototype, {
constructor: {
configurable: true,
writable: true,
value: SubClass,
},
x: {
configurable: true,
get: function () {
return this.newX;
},
set: function (val) {
this.newX = val;
alert("X has a value of " + this.newX);
},
}
});
ES6 classes are nicer if you can use them:
class SubClass extends ParentClass {
get x() {
return this.newX;
}
set x(val) {
this.newX = val;
alert("X has a value of " + this.newX);
}
}
You can also make this sort of helpful function:
function extend(target, source) {
Object.getOwnPropertyNames(source).forEach(function (name) {
var descriptor = Object.getOwnPropertyDescriptor(source, name);
Object.defineProperty(target, name, descriptor);
});
}
and use it like so:
extend(SubClass.prototype, {
get x() {
return this.newX;
},
set x(val) {
this.newX = val;
alert("X has a value of " + this.newX);
},
});
I want to keep this in class methods.
I can use arrow functions, but I want to override some methods in extended class.
Now I have this solution and it works:
class Foo {
bar = "Context preserved.";
constructor() {
this.foo = this.foo.bind(this);
}
foo() {
alert(this.bar);
}
}
class Foo2 extends Foo {
foo() {
alert(this.bar + " Class extended");
}
}
class Bar {
bar = "Context lost.";
}
let foo = new Foo2();
let bar = new Bar();
foo.foo.apply(bar); // Context preserved. Class extended
Is it a good practice to do it such way? If it is, is there some keyword in typescript to do it automatically?
like
class Foo() {
public conserved foo() { }
}
which generates:
var Foo = (function () {
function Foo() {
this.foo = this.foo.bind(this);
}
Foo.prototype.foo = function () { };
return Foo;
}());
It's a valid practice and it's being used.
I'm unaware of a way to tell typescript to do this automatically, but you can search the issues for something like it.
You can have a decorator that does that for you, for example:
function construct(constructor: Function, methods: string[], args: any[]) {
var c: any = function () {
return constructor.apply(this, args);
}
c.prototype = constructor.prototype;
let instance = new c();
methods.forEach(name => {
instance[name] = instance[name].bind(instance);
});
return instance;
}
function BindMethods(constructor: Function) {
const methods = [] as string[];
Object.keys(constructor.prototype).forEach(name => {
if (typeof constructor.prototype[name] === "function") {
methods.push(name);
}
});
return (...args: any[]) => {
return construct(constructor, methods, args);
};
}
#BindMethods
class Foo {
bar = "Context preserved.";
foo() {
console.log(this.bar);
}
}
let foo = new Foo();
setTimeout(foo.foo, 10);
(code in playground)
I tested it with this simple use case and it worked just fine.
I have read "How to implement a typescript decorator?" and multiple sources but there is something that i have nor been able to do with decorators.
class FooBar {
public foo(arg): void {
console.log(this);
this.bar(arg);
}
private bar(arg) : void {
console.log(this, "bar", arg);
}
}
If we invoke the function foo:
var foobar = new FooBar();
foobar.foo("test");
The object FooBar is logged in the console by console.log(this); in foo
The string "FooBar {foo: function, bar: function} bar test" is logged in the console by console.log(this, "bar", arg); in bar.
Now let's use a decorator:
function log(target: Function, key: string, value: any) {
return {
value: (...args: any[]) => {
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args); // How to avoid hard coded this?
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
}
};
}
We use the same function but decorated:
class FooBar {
#log
public foo(arg): void {
console.log(this);
this.bar(arg);
}
#log
private bar(arg) : void {
console.log(this, "bar", arg);
}
}
And we invoke foo as we did before:
var foobarFoo = new FooBar();
foobarFooBar.foo("test");
The objectWindow is logged in the console by console.log(this); in foo
And bar is never invoked by foo because this.bar(arg); causes Uncaught TypeError: this.bar is not a function.
The problem is the hardcoded this inside the log decorator:
value.value.apply(this, args);
How can I conserve the original this value?
Don't use an arrow function. Use a function expression:
function log(target: Object, key: string, value: any) {
return {
value: function(...args: any[]) {
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args);
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
}
};
}
That way it will use the function's this context instead of the value of this when log is called.
By the way, I would recommend editing the descriptor/value parameter and return that instead of overwriting it by returning a new descriptor. That way you keep the properties currently in the descriptor and won't overwrite what another decorator might have done to the descriptor:
function log(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
var originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
var a = args.map(a => JSON.stringify(a)).join();
var result = originalMethod.apply(this, args);
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
};
return descriptor;
}
More details in this answer - See the "Bad vs Good" example under "Example - Without Arguments > Notes"
I believe you can use
var self = this;
in order to preserve the 'this' at that specific point. Then, just use self at the later point where you would have wanted that particular this