Hello, my name is Dmitry Karlovsky and I ... do not forgive mistakes. As soon as I see it, I immediately throw something extremely heavy. And how hard the work of a JS programmer is ...
class Foo extends Object {}
const foo = new Foo
`, ${ foo }!`
// " [object Object]!"
` ${ foo / 1000 } .`
// " NaN ."
` "${ 'foo'[4] }" - .`
// " "undefined" - ."
` ${ foo.length - 1 } .`
// " NaN ."
There are several ways to ease his suffering.
- Cover yourself with a typescript. But in runtime, the feet still remain bare, and someone always steps on them.
- To impose checks. But you hesitate a little and the runtime rake immediately hits the head.
- Fix JS. Don't even hope.
- Fix JS runtime. Well, let's think ..
JS dynamic typing problems arise for 2 main reasons:
- Automatic (and sometimes inappropriate) typecasting when a value of one type is used in a context intended for another.
- Return undefined as the value of un-declared fields.
. JS , . . , :
Object.prototype[ Symbol.toPrimitive ] = function() {
throw new TypeError( `Field Symbol(Symbol.toPrimitive) is not defined` )
}
, , Symbol.toPrimitive
.
, . - … - ! … , .
- , . JS — . , :
export let $mol_strict_object = new Proxy( {}, {
get( obj: object, field: PropertyKey, proxy: object ) {
const name = JSON.stringify( String( field ) )
throw new TypeError( `Field ${ name } is not defined` )
},
})
, prototype
Object
, , . Object.prototype
— null
. Object
:
:
for( const name of Reflect.ownKeys( $ ) ) {
// ...
}
, :
const func = Reflect.getOwnPropertyDescriptor( globalThis, name )!.value
if( typeof func !== 'function' ) continue
if(!( 'prototype' in func )) continue
, globalThis[name]
, .
, Object
:
const proto = func.prototype
if( Reflect.getPrototypeOf( proto ) !== Object.prototype ) continue
, , Object.prototype
:
Reflect.setPrototypeOf( proto, $mol_strict_object )
, , . , .
, , Object, , EventTarget, .
CSSStyleDeclaration
: ( , ), , , 89 style
dom-:
( <div style={{ color: 'red' }} /> ).outerHTML // <div></div>
.
… :
class Foo {}
. :
class Foo extends Object {}
… . Object
:
globalThis.Object = function $mol_strict_object( this: object, arg: any ) {
let res = Object_orig.call( this, arg )
return this instanceof $mol_strict_object ? this : res
}
Reflect.setPrototypeOf( Object, Reflect.getPrototypeOf({}) )
Reflect.setPrototypeOf( Object.prototype, $mol_strict_object )
, Object
, .
, ...
class Foo extends Object {}
const foo = new Foo
`, ${ foo }!`
// TypeError: Field "Symbol(Symbol.toPrimitive)" is not defined
` ${ foo / 1000 } .`
// TypeError: Field "Symbol(Symbol.toPrimitive)" is not defined
` "${ 'foo'[4] }" - .`
// TypeError: Field "4" is not defined
` ${ foo.length - 1 } .`
// TypeError: Field "length" is not defined
, , :
, - . — .
, : JavaScript ?.
: $mol_strict.
NPM - :
import "mol_strict"
:
require("mol_strict")
$mol : $mol: Usage from NPM ecosystem.
JS , :
- ( . , , , .)
- UfoStation Chat ( — .)
, _jinnin , JS, UX .