$ mol_strict: Bagaimana dengan saya [Objek Objek] NaN Anda yang belum ditentukan‼

Halo, nama saya Dmitry Karlovsky dan saya ... tidak memaafkan kesalahan. Begitu saya melihatnya, saya langsung melempar sesuatu yang sangat berat. Dan seberapa keras kerja seorang programmer JS ...







class Foo extends Object {}
const foo = new Foo

`, ${ foo }!`
// " [object Object]!"

`     ${ foo / 1000 }  .`
// "     NaN  ."

`  "${ 'foo'[4] }" - .`
// "  "undefined" - ."

`  ${ foo.length - 1 }     .`
// "  NaN     ."
      
      





Ada beberapa cara untuk meringankan penderitaannya.







  1. Tutupi diri Anda dengan naskah ketikan. Namun saat runtime, kaki masih tetap telanjang, dan seseorang selalu menginjaknya.
  2. Untuk memaksakan cek. Tapi Anda sedikit ragu dan runtime langsung mengenai kepala.
  3. Perbaiki JS. Jangan berharap.
  4. Perbaiki runtime JS. Baiklah, mari kita pikirkan ..


Masalah pengetikan dinamis JS muncul karena 2 alasan utama:







  • Typecasting otomatis (dan terkadang tidak tepat) ketika nilai dari satu jenis digunakan dalam konteks yang dimaksudkan untuk jenis lainnya.
  • Kembalikan tidak terdefinisi sebagai nilai bidang yang tidak dideklarasikan.


Mari kita tangani masalah pertama dulu. JS dirancang sedemikian rupa sehingga kami tidak dapat memperbaiki casting tipe primitif dengan cara apa pun. Tapi kami memiliki kendali penuh atas casting objek. Oleh karena itu, mari kita tambal prototipe global semua objek sehingga tidak ada objek yang dapat dilemparkan secara default:







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 , :









, _jinnin , JS, UX .








All Articles