edge-cases.md

May 13, 2020 ยท View on GitHub

Edge cases

The three values (true, false, null) are mutually exclusive, but theorically classical functions can play multiple roles (constructors, methods, plain functions) so there will be false positives.

If we use own data property API form (f.thisArgumentExpected), programmers could reconfig the property for these edge cases. If we use other API form, we would need some other mechnism to deal with them or just ignore them.

The following code use thisArgumentExpected data property API form for demostration.

Direct eval

function func() {}
func.thisArgumentExpected // false

function directEval() {
  eval('this')
}
directEval.thisArgumentExpected // false

Object.defineProperty(directEval, 'thisArgumentExpected', {value: true})

Old style constructor functions

function OldStyleConstructor(foo) {
  this.foo = foo
}
new OldStyleConstructor(42)

OldStyleConstructor.thisArgumentExpected // true
Object.defineProperty(OldStyleConstructor, 'thisArgumentExpected', {value: null})
function OldStyleConstructor(foo) {
  if (new.target === undefined) return new OldStyleConstructor(foo)
  this.foo = foo
}
Object.defineProperty(OldStyleConstructor, 'thisArgumentExpected', {value: false})

Optional this argument

class X {
  static of(...args) {
    return new (this ?? X)(args)
  }
}
X.of.thisArgumentExpected // true
Object.defineProperty(X.of, 'thisArgumentExpected', {value: false})

Retrieve global this

let getGlobalThis = new Function('return this')
getGlobalThis.thisArgumentExpected // true
Object.defineProperty(getGlobalThis, 'thisArgumentExpected', {value: false})

Avoid edge cases

If you have the control of the source code of these edge case functions, there are possible ways to eliminate some cases without reconfiguration. For example, use classes to replace old style constructor functions. Generally, if we have function decorators available, we could use some declarative form like @thisArgumentExpected(true) function f() {}.