JavaScript Hacking 101

Have you ever wondered about how do the things work in JavaScript under the hood? I think it’s a very interesting topic for everyone who’s ever touched this language. But before you spend hours digging through TC39’s ecma262 specification and the core.js repository maybe, it’s a good idea to take a look at what we can achieve with simple knowledge of ES6 APIs. Back in the day we used to use the eval() function that let us evaluate JavaScript code in a string.

So expression like this:

Would work pretty much everywhere where you can execute JS (there are some exceptions though). This is very handy when it comes to hacking, but you shouldn’t really rely on it when developing real-life applications.

But what do we do if we want to change some behaviour of our JavaScript or reveal information about our code that is not really visible to normal users? ES6 comes with a few useful APIs that can help us achieve exact goals. Today I’ll try to help you understand them even if you’re not a Senior JS Programmer with 10 years of experience.

Symbol

First thing that TC39 brought us is a new data type called Symbol. Symbols are unique. Symbols are immutable. Symbols are amazing. It’s useful to know how Symbols work and what exactly they can be used for, since they can be a really powerful weapon in your hands.

So let’s start from the very beginning. Do you remember what I’ve just said? Symbols are unique. I mean really unique.

Ok, but what about that? A symbol is a unique and immutable data type. Its new primitive which can be used as object keys just like strings. Example below will illustrate this uniqueness better:

Huh — that can be useful, right? If you didn’t notice, we can define keys which look the same but are completely unique. That’s a big advantage when it comes to hacking. But this is not the last thing that Symbols bring us.

If you started your journey with programming with a different language, then you would probably wonder how to implement private fields in JS class. Well — Symbols can help you achieve a similar thing. Why similar? Because the information is still accessible, but not really visible at first glance. Let’s take a look at the example below:

In this case, your ‘crazy’ Symbol won’t be visible until you call Object.getOwnProperties() on your instance. That’s pretty handy when you don’t want to expose internal details.

Ok — that’s already something. But at the beginning we talked about overriding some language defaults. There are some pre-defined Symbol constants in JavaScript and that’s where they can show off their power. The most common constant is Symbol.iterator — it can help us change JavaScript’s for of iteration. So if we want to print every second element of an array instead of each one, we can follow this example:

Magic.

Another useful constant is Symbol.hasInstance. It can be used to change the mechanics of the instanceof operator. We can easily specify if a given variable is an instance of our class. We don’t even need to extend the class in any way, just specify a static [Symbol.hasInstance] function and enjoy the result.

Symbols are very useful if you want to do something differently within String methods. If you need String.match(), String.replace() or String.search() to provide you a different result, then you can pick between Symbol.match, Symbol.replace and Symbol.search. I’ll just show you how usage of the last one can be implemented.

There is much more to learn about Symbols. But since it’s not the only feature i want to show you today, I’ll send you back to mozilla docs.

Reflect

6th edition of EcmaScript brings us a new built-in object called Reflect from Reflection API. You cannot create a new instance of Reflect, but it exposes a few handy methods which can be used to reveal information about objects and provide some nice syntax sugar for old methods.

If you had read this article carefully, you would have probably noticed that thanks to symbols we can hide some of the information about an object at first glance. Reflect objects can help us discover some lower-level information about an object and all its properties and symbols with just one function.

As you can see, we can discover all of the information about an object (including its symbols). You can think of this method as a mixture of Object.getOwnPropertyNames() and Object.getOwnPropertySymbols().

Have you ever used Object.create() ? It’s a method used to create instances of an object. But it has its pitfalls — what if we want to create an instance and call the class constructor with our new argument? Then we should probably consider using Object.call().

Yeah — almost everything is achievable, but why not make it easy. Just watch how easily we can construct a new date with Reflect compared to old Object’s way.

As we’ve just seen how to create an object with Reflect, now it’s time to do some damage to objects. We can use our new friend to delete the object property. Simply call Reflect.deleteProperty() and free your object from unwanted props.

If you know JS’s delete operator then it’s almost identical, but Reflect.deleteProperty() ignores type checking and works only for objects, not for any kind of variable.

Everything we just learned about Reflect is related to Objects — but it also provides some improvements over Function’s .call() and .apply(). Now, instead of calling both those functions over your function, you can simply use Reflect.apply(). It’s a really nice piece of syntax sugar over chaining those two. Just watch.

Proxies

I know that there might be quite a lot of new things already, so I’ll try to keep this one short. So Reflect brings us quite a few amazing methods (check the full list). Proxies are used to intercept all those methods for a chosen object. I’ll show you a quick example of overriding JavaScript’s default delete operator with a proxy. Remember, that’s not the only method you can override with proxy! You can basically do the same for any method exposed by Reflect.

As you can see, the width persisted through deletion. That’s quite a powerful property when it comes to modifying default behavior.

Conclusion

Today we went through some fairly basic, but really useful tools that can help us customize JavaScript to suit our needs better — at least within our application 💪🏻. If you’re interested in this topic, and want to dig deeper into possibilities of modifying defaults, please refer to TC39’s docs. There is a large amount of information that could help you write secure and amazing JS apps.

Insights

Learn more about JavaScript

cover

Jun 26Article

React Native Legal: CLI & Programmatic API - Unified Tooling for License Attributions

Discover React Native Legal, the first unified solution for managing open-source license attributions. Automatically handle both JS & native dependencies in any project via a powerful CLI and programmatic API.

cover

Dec 14Article

ReasonML for JS developers

If you are a JavaScript developer and want to learn a new functional language, this article is a must-read for you. Check it out!

cover

Jun 13Article

Manage Async Code Like a PRO With RxJS

Discover the Saviour for async code management in React with our blog post.

cover

Aug 1Article

ECMAScript ES8 and Beyond

Quick introduction to ECMAScript proposals ES8 with the list of currently available presets and presentation of some features of each preset.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.