Javascript ES6 class definition not accessible in

2020-01-26 09:51发布

I have come across an interesting issue, at least I think it's interesting, and a little annoying. I have a class, for this question I will keep it extremely simple...

class Foo {
    static pageChange() {
        console.log('The page changed');
    }
}

Now, I can access that with Foo.pageChange() no problem, everything works as expected. The hard part, and interesting bit comes in when I try to access this dynamically. I have a separate object that monitors events and handles dispatching them as needed. This pertains to the Google visualization library, where I have a table, and the table has events tied to it. I have an object that is responsible for creating all of this from PHP output. It is one heck of a system, in an easy explanation you can do something like this in PHP...

GoogleVisLibrary::renderChart(
    array(
        'chartType' => 'table',
        'chartData' => $this->chartData,
        'chartOptions' => $this-chartOptions,
        'events' => array(
             'sort' => 'Foo.pageChange'
        )
);

now that will create teh table and all that good stuff. The problem is accessing that static method in the Foo class in javascript. What I had prior to creating the Foo as a class was this.

var Foo = {
    pageChange: function() {
         console.log('page changed');
    }
}

then in my event library handler thingy it would look something like this..

for(var i = 0, l = events.length; i < l; i++) {
    window[events.className][events.fnName].apply();
}

and that would work fine because Foo can be accessed through window['Foo'] but when you use the class definition shown in the first code snippet, you can no longer access it from the window super global, it just outputs 'undefined'.

So, is there any way to access a static method in a class, through a dynamic reference like you can with the Foo object through the window global?

I am hoping that makes sense and I am explaining it correctly. If anyhting doesn't make sense please feel free to ask, I will try to explain better. Thank you in advance for any help you can give.

2条回答
等我变得足够好
2楼-- · 2020-01-26 10:27

To get a reference on the window object, you'll need to do that explicitly:

window.Foo = class Foo { ... }

Read more about classes not being properties of the window object in this answer, which also quotes the ECMA2015 Specification, Section 8.1.1.4: Global Environment Records:

A global Environment Record is logically a single record but it is specified as a composite encapsulating an object Environment Record and a declarative Environment Record. The object Environment Record has as its base object the global object of the associated Realm. This global object is the value returned by the global Environment Record’s GetThisBinding concrete method. (E.g., the global object referenced by window on browsers — T.J.) The object Environment Record component of a global Environment Record contains the bindings for all built-in globals (clause 18) and all bindings introduced by a FunctionDeclaration, GeneratorDeclaration, or VariableStatement contained in global code. The bindings for all other ECMAScript declarations in global code are contained in the declarative Environment Record component of the global Environment Record.

Using a dedicated object

It would be better not to use the global object for this, and dedicate a specific object to contain your classes and base your event managing library on that object, as illustrated in this simplified snippet:

(function () {
    var classes = {
        Foo: class Foo {
            static pageChange() {
                console.log('The page changed');
            }
        }
    }

    /////////////
    var events = [{
        className: 'Foo',
        fnName: 'pageChange'
    }];
    for(var event of events) {
        classes[event.className][event.fnName].apply();
    }
}());

查看更多
ら.Afraid
3楼-- · 2020-01-26 10:42

I got the same problem myself for quite a while, and I finally discovered a very simple solution : using eval.

Create your class :

class Foo {
  static pageChange() {...}
}

Get it dynamically :

eval('Foo');   // return the Foo constructor
eval('Foo').pageChange;   // return the pageChange static method
eval('Foo')['pageChange'];  // same

I dislike eval, but in this case it is as simple as fast. And it's the only way I found without adding the class to window.

(I also tried (new Function("return Foo;"))() and it appears eval is 2-3 times faster)

查看更多
登录 后发表回答