Meta Programming in JavaScript
(upbeat music) - Hi.
It was early this year that I had a presentation in a local meet up in Sydney and after the meet up, the organiser of CCS says, "Send me a message on Twitter," and she mentioned that, "Are you available at conference like Web Directions and would you be interested to have a talk there?" I never thought of that I can just go and have a talk in a conference.
So I was, "Okay, sure." I'm going to just send a proposal.
So fast forward a while, it's end of the year and I'm here and this is my first basically talk in a conference. So I hope you enjoy the talk.
I am Mehdi, I am a software engineer at Autopilot and the purpose of this talk is going to be an introduction about metaprogramming and what we can do with metaprogramming in JavaScript. But let's start with a definition of metaprogramming in general and what do we mean by metaprogramming.
So meta programme is a programming technique in which computer programme can treat the code as a data. It means that programme can be designed in such a way that it can actually regenerate and analyse or transform itself or other programmes while running.
So if you look at this definition that I got from Wikipedia, it's quite broad.
It can target other programming languages and it can basically be within a programming language. And obviously metaprogramming has a lot of flavours. It's like the programming in general.
You have functional, you have procedural and each of them has different type of style.
So let's focus in a part of this definition that's around regenerate, analyse and transform itself while running. So that is the purpose of this talk.
Can we see how basically we can do this kind of stuff in JavaScript but there is actually a term for this type of behaviour in a programming language and that's called reflection. And in computer science when we talk about reflection, we talk about ability of a computer programme that can examine, introspect and modify itself and its own structure while running.
So we are having focus on run time.
We don't focus on complex time and that's not something that we are going to talk about.
So reflection is a feature of programming language and it's very valuable and it facilitates metaprogramming. But what sort of features a programming language should offer, should have to provide us with reflection? So basically there are a set of features but I highlighted, I'm going to highlight a couple of them that are relative to JavaScript programme.
The first one is actually evaluating a string in run time and you can basically manage the result of execution of a piece of a string as a code inside your running code. This is called inferencing and also we should be able to have a string, a representation of a class and function or a variable and then have access to that actual object on run time.
And also we should be able to get information about source code construction like classes, objects and functions at run time.
These are a couple of features that allows us to do reflection.
And so the purpose of this talk will be a reflective JavaScript metaprogramming, a very specific type of metaprogramming.
And I would like to divide the reflective JavaScript metaprogramming in two parts.
The first part will be what it can do using ES5 and then obviously we go to beloved ES6 and talk what it can do and what sort of features are introduced in ES6. But in ES5, obviously we had eval.
We can use it and it's a part of metaprogramming. Next time that somebody told you that it's a bad idea, you can say, "No that's a metaprogram feature." And now it's a buzz word.
So that's not basically necessarily a bad thing. I admit that maybe it's not solution for any problem. Also, we have the features for examination. For example, we can use instance of to get information about a class and if an object is instance of a specific class or we can get information about type of an object.
Or, for example, we have an object, we want to get what are the keys that we have in that object. So these are examinations.
So these features were available on ES5.
Also, as we saw in the previous presentation, we have getters and setters in ES5.
We could have a property and have control over getting the value of that property or setting a value for that property.
And we could do this kind of things in ES5 and you probably use them but that's a term we use for them from now on is metaprogramming and it's reflective metaprogramming. And starting with ES6, there were a couple of features that were introduced to the language and allows us to do metaprogramming in a different way.
So there are three features, symbol, proxy and reflect. They were all introduced in ES6 and today I'm going to go through all of these three features of JavaScript and see how they can help us to do metaprogramming. But let's start with symbols.
So they are the new type in JavaScript.
So we had six different types and this is the seventh one. And so you can create a symbol value by calling it a single function and the cool thing about basically symbols are that they are unique.
So if you call symbol function twice and it's going to return two different values and it's guaranteed that it's going to be always unique unless you have a reference where symbol value.
And what is interesting about symbols are that you can actually use them as an identifier of your object properties and actually that was the main purpose of creating symbols so they can be unique.
It generates unique values and you can use them as an identifier for your objects. So in this example, I have basically a symbol called name field and actually I have a description for it as well but description is completely optional.
And also I created the beer object and I used that symbol as an identifier for that field and the value is VB. Of course and then at console.log basically that value it shows that how much I love this beer.
So, this is nothing very specific to metaprogramming but that's of any conferencing that we talk about well-known symbols.
So when the symbol talk was introduced in JavaScript in ES6, it came with a set of built-in symbols and what they do they basically represent internals of JavaScript and they were not available to us beforehand but now they are exposed to the developers and we can use them.
So what we can do actually with symbols are reflection within implementation.
What that means that if you have a class or an object, you can include these well-known symbols and you can have control on solve the operations that we didn't have the features to have control over them.
So for example, now with well-known symbols, we can have control over instance of operator.
So we can whenever our class is on the right hand side of an instance of operator, we can handle that if the value on the left hand side is instance of our class or not, by ourself. What we can do also is that for example we can have control over for of operators so when there is an iteration we can decide as a basically developer of a class that how the iteration is going to work.
For example if our basically instance of a class is used with Array.concat, we can think about how the concatenation should work. So it's control over those kind of operations. So now we can for example implement custom matches for a string that match if you want to have a custom matcher for searching something in a string we can use it today but we didn't have these options before ES6. And quite interesting, now we can actually convert a very complex object to a primitive data type and we as a developer decide that how the conversion should actually work.
So I'm going to basically give you a couple of examples. In this one, this is basically a class called MyArray and the next line I have a array called friends and I console.log friends instance of MyArray and obviously that's going to return false because there are not basically friends is not instance of MyArray.
It's instance of Array but I just extended MyArray class and I included a very well-known symbol called hasInstance. That decides that if a given object is instanceof basically this class or not.
In this example, I decide that if the provided object is array, it's going to be an instanceof our class as well. So I execute at the same code and now it returns true. So it's a control over instanceof operator, which is a low level feature of the programming language. In this example, I'm trying to basically have control over converting a complex object to a primitive data doc.
So I have a shopping basket.
It's a class you can have some items with and then you can calculate the total amount of the money that you need to pay for that when I also added some items.
Then I went forward and I included two primitive basically a symbol and there I decided if I'm going to be converted to a number, I should just calculate the total amount of money that should be paid for that shopping basket.
And at the end I see, I can say plus basket means I converted to number and it's going to console.log 6.4 and then I can say convert this basket object to the number data type and that's going to just work.
The next basically feature of JavaScript ES6 that allows us to do reflective metaprogramming is proxy.
And so the proxy is basically a reflection via interception. So the proxy, as the name indicates, sits in front of your object and you can have some interceptions and decide that how the operations should work.
So for example, we can have a proxy or basically say with a trap for delete operator and so whenever somebody wants to delete a property in our object, we can intercept it.
We can also have control of our in operator or we can have control over getting a value or setting a value for a property.
And also we can have control over function calls. A developer can actually invoke a function that doesn't exist in that object, we can have a trap for it to see, okay, this function actually match to another function. We can do a lot of stuff with it and also we can have control over new operator and this are a list of features that you can. It's not all of them.
So in this example, I have a student object and I want to introduce a visual field to it using proxy. So I create in the proxy object has a field called get which means that I want to intercept basically reading value of a property and in that piece I'd advise you I would say if the requested field is fieldName, simply concatenate first name and last name and return it. As a result, I can create a proxy using an object and then console.log proxiedStudent.fullName and it returns the basically first name and last name. Next example is I want to have a valuation on some of my fields.
For example, there is a field called age and I want that field to be always number and to be greater than zero because it doesn't make sense age to be less than zero. So, I simply define my proxy object and I define set basically property.
A little bit of logic there.
And then I can just create an object using that proxy. And if I try to set age to A or minus Y, it's going to throw a type error because that's how I trained my code but with 12 it's going to just proceed.
The next one is for example, I want to delete ... I want to protect some of my fields for example, ID. A user shouldn't be able to delete them.
So in this case, I had a proxy that has a trap for delete property and there's a little bit of logic and I ignore the action if it's basically targeting a field called ID and as you can see, I try to delete that field and the value is still there.
And so there's no way for the user to actually delete that field.
The next feature that we have is reflect.
So this is quite interesting.
It's about reflective metaprogramming and this API was infrequent use and just for reflection. So it basically provides reflection through introspection. So it gives you a set of API that allows you to get information about your code also does some operations that you can do. But what is interesting about reflective API that there's some of the APIs that are available via this naming space, used to be available in other basically naming spaces as well but now they are combined together.
And for example, we can call reflect.apply and call a function or reflect.define property, delete property, get, set and has is basically get some information.
And as I mentioned, some of these features are new, some of them are existing, it's just a new space for them. There is really nothing special about them. In this example, I'm trying to delete a field in an object using basically reflect.delete property. I start with console.log that value and then I try to delete it and it's deleted, nothing fancy.
You could use delete operator.
It's just up to you.
It's different flavours in this case.
And basically, it comes to the native support in browsers and in service style renderings.
So modern browser including Edge, Firefox, Safari and Chrome they have 100% native so you don't need to use polyfills and they will do all these sort of ugly codes and so it's possible to use them today in browser.
If you're a fan of service like JavaScript, Node 8 has a 100% support of all of these features.
So that's available for you.
I have a little coding for you and IE 11 doesn't support it. So if you're going to support that one, that's probably you. (laughing) I put together a list of resources that I think they are useful if you are interested to the topic of metaprogramming.
So Wikipedia has a good introduction about the metaprogramming and reflection.
Also Mozilla has a great article about them. Also, there is a historology from Keith Cirkel about metaprogramming in ES6 with a lot of practical examples I'd highly recommend to take a look at that one. And also I want to give credits to great people to put a lot of effort to documenting these features so we can actually use them today.
So, MDN has great docs, Wikipedia for their great articles and also Keith Cirkel for the metaprogramming series. And that's it, thank you.
And if you are interested to the slides, that's bit.ly/meta-javascript.
You can just take a look at the slides.
All of the links are available and my contact information is there.
If you want to be in touch, I'll be more than happy to have a chat about that.
Thank you.
(audience applauding) (upbeat music)