Tag: es6

As i talked about ecma2015/es6 with a few people, i’ve realized that many features are still a mystery in the community. One of them is Proxy object. Besides classic language changes there is a handful of goodies, which i particularly really like.

General Proxy

What is the proxy object? If you haven’t heard of it yet, let’s look at this example schema of classical Proxy server:

Uses of proxy servers are many, just picture this one: proxy lies between target and source of the request and is masking the source identity, so the target does not know about the real computer beyond the proxy. This concept is similar to NAT(although it is working diferently). As you can see from the schema above – there is a way for the proxy server to see and optionally alter the response or request. And for the goal of this article it’s enough to know, that javascript Proxy object is masking the call to some method/atribute of another object – basically is wrapping the target object. Since it is a wrapper and is “trapping” all calls, it can easily alter them.

In the contrast to some other languages, the javascript lacked this functionality a few years ago. It is said, that the proxy is similar to python’s get-attribute-access methods or php’s catch-all method, or that it implements meta-programming or is basis for defensive programming. You can definitely use it for many benefits, that’s for sure.
(Note – this article is considering support in Node.js rather than browsers) The existence of Proxy objects is around for several years now(as a planned feature for ecma2016/es6) but wasn’t fully implemented even in Node.js until recently(6.0.0 it looks like). Even so, many enthusiasts has been using shims/transpilers/workarounds to be able to use Proxy objects before. Now they don’t have to complicate things. Back in the good ol’ days of Node.js version 0.12.x developers had another choice – to use old Proxies API, which was somehow not as flexible as new API. Even so, to get this done they had to enable harmony feature(s) to be able to run their code.

Let’s talk about small difference between the old API and new API – the basic usage:

JavaScript

1

2

3

varproxy=Proxy.create(handler,proto);// old API

varproxy=Proxy.createFunction(handler,callTrap,constructTrap);// old API

varp=newProxy(target,handler);// new API

The new one is unifying the previous two methods. The target argument in new API accepts array, generic object, even proxy or function. The old API has been more prone to mistakes as could be seen below and this was probably the reason why they updated it. The handler argument alone is basically the same and could be used without changes in both APIs, while it is basically an object which holds functions – or better traps(full list ie. here). I will skip the basics and follow slightly more advanced usage, since i don’t believe that anybody would be using the proxies with only the handler(ie. without meaningful target / proto arguments).

This will be basically about implementing the __noSuchMethod__ in objects, which could be aliased as catch-all method. First – the old API:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

Dummy=function(){}

Dummy.prototype.test=function(){

console.log('Test called')

}

Dummy.prototype.__noSuchMethod__=function(){

console.log('No such method');

}

varbundle=function(proto,obj){

varnewObj=newobj()

varhandler={

get:function(rcvr,p){

if(typeofnewObj[p]==='function'){

returnnewObj[p];

}else{

returnfunction(){

varargs=[].slice.call(arguments,0);

returnnewObj.__noSuchMethod__.call(newObj,p,args);

};

}

}

};

varp=Proxy.create(handler,proto);

returnp

};

varinstance=bundle(Dummy.prototype,Dummy);

instance.test();// will call the test method

instance.test2();// will call the __noSuchMethod__

However this code is working, it’s not exactly what i want – i don’t want to call the bundle function and create the instance of the Dummy inside it…let’s put it somewhere else.

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

Dummy=function(){

varthat=this;

returnProxy.create({

get:function(rcvr,p){

if(typeofthat[p]==='function'){

returnthat[p];

}else{

returnfunction(){

varargs=[].slice.call(arguments,0);

returnthat.__noSuchMethod__.call(that,p,args);

};

}

}

});

}

Dummy.prototype.test=function(){

console.log('Test called')

}

Dummy.prototype.__noSuchMethod__=function(){

console.log('No such method');

}

varinstance=newDummy();

instance.test();

instance.test2();

Ok, that’s better. But now i am contradicting my initial statement, that i want to use both parameters. Moreover, this code is not the best, since the function in get trap would be created everytime when new Dummy would be created. One method of fixing this is to move this function out of constructor.

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

varthat=null;

functionget(rcvr,p){

if(typeofthat[p]==='function'){

returnthat[p];

}else{

returnfunction(){

varargs=[].slice.call(arguments,0);

returnthat.__noSuchMethod__.call(that,p,args);

};

}

}

Dummy=function(){

that=this;

varp;

p=Proxy.create({

get:get

});

returnp;

}

Dummy.prototype.test=function(){

console.log('Test called')

}

Dummy.prototype.__noSuchMethod__=function(){

console.log('No such method');

}

varinstance=newDummy();

instance.test();

instance.test2();

And there is another problem – it uses global variable, which would be overwritten everytime when new Dummy would be created in this scope. Somehow, the reference to created object has to be passes into the get function. But that won’t be the issue with the new API as this below would simply work, since we are not passing non-instantiated Dummy, but complete object.

new Proxy API

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

functionget(rcvr,p){

if(typeofrcvr[p]==='function'){

returnrcvr[p];

}else{

returnfunction(){

varargs=[].slice.call(arguments,0);

returnrcvr.__noSuchMethod__.call(rcvr,p,args);

};

}

}

Dummy=function(){

returnnewProxy(this,{

get:get

});

}

Dummy.prototype.test=function(){

console.log('Test called')

}

Dummy.prototype.__noSuchMethod__=function(){

console.log('No such method');

}

varinstance=newDummy();

instance.test();

instance.test2();

Conclusion

The last code example is crude, yet effective way to implementing catch-all method in your objects which in turn could help you tremendously when creating large project with many models / controllers while fully using inheritance. Personally, i see a big difference while creating code purely in es6(August 2016 – however still using babel with es6). Then again – i would prefer even better usage – if not built-in method for each object, then at least directly extending Proxy prototype(or “class” if we are talking about es6) which is an idea for part 2 of this article.