Hallo, mein Name ist Dmitry Karlovsky und ich ... vergib keine Fehler. Sobald ich es sehe, werfe ich sofort etwas extrem Schweres. Und wie schwer die Arbeit eines JS-Programmierers ist ...
class Foo extends Object {}
const foo = new Foo
`, ${ foo }!`
// " [object Object]!"
` ${ foo / 1000 } .`
// " NaN ."
` "${ 'foo'[4] }" - .`
// " "undefined" - ."
` ${ foo.length - 1 } .`
// " NaN ."
Es gibt verschiedene Möglichkeiten, sein Leiden zu lindern.
- Bedecken Sie sich mit einem Typoskript. Aber zur Laufzeit bleiben die Füße immer noch nackt und jemand tritt immer auf sie.
- Schecks auferlegen. Aber Sie zögern ein wenig und der Laufzeitschwader trifft sofort den Kopf.
- Fix JS. Hoffe nicht einmal.
- Korrigieren Sie die JS-Laufzeit. Nun, lass uns nachdenken ..
Probleme mit der dynamischen Typisierung von JS treten aus zwei Hauptgründen auf:
- Automatische (und manchmal unangemessene) Typumwandlung, wenn ein Wert eines Typs in einem für einen anderen bestimmten Kontext verwendet wird.
- Rückgabe undefiniert als Wert nicht deklarierter Felder.
Lassen Sie uns zuerst das erste Problem behandeln. JS ist so konzipiert, dass wir das Casting primitiver Typen in keiner Weise korrigieren können. Wir haben jedoch die volle Kontrolle über das Gießen von Objekten. Lassen Sie uns daher den globalen Prototyp aller Objekte patchen, damit keines der Objekte standardmäßig umgewandelt werden kann:
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 .