Recommended Video Course
Python vs JavaScript for Python Developers
Table of Contents
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Python vs JavaScript for Python Developers
If you’re serious aboutweb development, then you’ll need to learn aboutJavaScript at some point. Year after year, numeroussurveys have shown that JavaScript is one of the mostpopular programming languages in the world, with a large and growing community of developers. Just like Python, modern JavaScript can be used almost anywhere, including the front end, back end, desktop, mobile, and theInternet of Things (IoT). Sometimes it might not be an obvious choice between Python vs JavaScript.
If you’ve never used JavaScript before or have felt overwhelmed by the quick pace of its evolution in recent years, then this article will set you on the right path. You should already know thebasics of Python to benefit fully from the comparisons made between the two languages.
In this article, you’ll learn how to:
Free Bonus:5 Thoughts On Python Mastery, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.
If you’re already familiar with the origins of JavaScript or just want to see the code in action, then feel free to jump ahead to thenext section. Otherwise, prepare for a brief history lesson that will take you through the evolution of JavaScript.
Many people, notably some IT recruiters, believe that JavaScript andJava are the same language. It’s hard to blame them, though, because inventing such a familiar-sounding name was a marketing trick.
JavaScript was originally calledMocha before it was renamed toLiveScript and finally rebranded asJavaScript shortly before its release. At the time,Java was a promising web technology, but it was too difficult for nontechnical webmasters. JavaScript was intended as a somewhat similar but beginner-friendly language to supplementJava applets in web browsers.
Fun Fact: Both Java and JavaScript were released in 1995. Python was already five years old.
To add to the confusion, Microsoft developed its own version of the language, which it calledJScript due to a lack of licensing rights, for use with Internet Explorer 3.0. Today, people often refer to JavaScript asJS.
While Java and JavaScript share a few similarities in theirC-like syntax as well as in their standard libraries, they’re used for different purposes. Java diverged from the client side into a more general-purpose language. JavaScript, despite its simplicity, was sufficient for validating HTML forms and adding little animations.
JavaScript was developed in the early days of the Web by a relatively small company known asNetscape. To win the market against Microsoft and mitigate the differences across web browsers, Netscape needed to standardize their language. After being turned down by the internationalWorld Wide Web Consortium (W3C), they asked a European standardization body calledECMA (todayEcma International) for help.
ECMA defined a formal specification for the language calledECMAScript because the nameJavaScript had been trademarked bySun Microsystems. JavaScript became one of the implementations of the specification that it originally inspired.
Note: In other words, JavaScript conforms to theECMAScript specification. Another notable member of the ECMAScript family isActionScript, which is used on the Flash platform.
While individual implementations of the specification complied with ECMAScript to some extent, they also shipped with additional proprietary APIs. This led to web pages not displaying correctly across different browsers and the advent of libraries such asjQuery.
To this day, JavaScript remains the only programming language natively supported by web browsers. It’s thelingua franca of the Web. Some people love it, while others don’t.
There have been—and continue to be—many attempts to replace or supplant JavaScript with other technologies, including:
These attempts were driven not only by personal preference but also by web browsers’ limitations beforeHTML5 came onto the scene. In those days, you couldn’t use JavaScript for computationally intensive tasks such as drawing vector graphics or processing audio.
Rich Internet Applications (RIA), on the other hand, offered an immersive desktop-like experience in the browser through plugins. They were great for games and processing media. Unfortunately, most of them were closed source. Some had security vulnerabilities or performance issues on certain platforms. To top it off, they all severely limited the ability of web search engines to index pages built with these plugins.
Around the same time cametranspilers, which allowed for an automated translation of other languages into JavaScript. This made the entry barrier to front-end development much lower because suddenly back-end engineers could leverage their skills in a new field. However, the downsides were slower development time, limited support for web standards, and cumbersome debugging of the transpiled JavaScript code. To link it back to the original code, you’d need asource map.
Note: While acompiler translates human-readable code written in a high-level programming language straight intomachine code, a transpiler translates one high-level language into another. That’s why transpilers are also known assource-to-source compilers. They’re not the same ascross compilers, though, which produce machine code for foreign hardware platforms.
To write Python code for the browser, you can use one of the available transpilers, such asTranscrypt orpyjs. The latter is a port of Google Web Toolkit (GWT), which was a wildly popular Java-to-JavaScript transpiler. Another option is to use a tool likeBrython, which runs a streamlined version of the Python interpreter in pure JavaScript. However, the benefits might be offset by poor performance and lack of compatibility.
Transpiling allowed a ton of new languages to emerge with the intent of replacing JavaScript and addressing its shortcomings. Some of these languages were closely relateddialects of JavaScript. Perhaps the first wasCoffeeScript, which was created about a decade ago. One of the latest was Google’sDart, which was the fastest-growing language in 2019according to GitHub. Many more languages followed, but most of them are now obsolete due to the recent advances in JavaScript.
One glaring exception is Microsoft’sTypeScript, which has gained much popularity in recent years. It’s a fully compatible superset of JavaScript that adds optionalstatic type checking. If that sounds familiar to you, that’s because Python’stype hinting was inspired by TypeScript.
While modern JavaScript is mature and actively developed, transpiling is still a common approach to ensure backward compatibility with older browsers. Even if you’re not using TypeScript, which seems to be the language of choice for many new projects, you’re still going to need to transpile your shiny new JavaScript into an older version of the language. Otherwise, you run the risk of getting aruntime error.
Some transpilers also synthesize cutting-edge web APIs, which might be unavailable on certain browsers, with a so-calledpolyfill.
Today, JavaScript can be thought of as theassembly language of the Web. Many professional front-end engineers tendnot to write it by hand anymore. In such a case, it’s generated from scratch through transpiling.
However, even handwritten code often gets processed in some way. For example,minification removes whitespace and renamesvariables to reduce the amount of data to transfer and to obfuscate the code so that it’s harder to reverse engineer. This is analogous to compiling the source code of a high-level programming language into native machine code.
In addition to this, it’s worthwhile to mention that contemporary browsers support theWebAssembly standard, which is a fairly new technology. It defines a binary format for code that can run with almost-native performance in the browser. It’s fast, portable, secure, and allows for cross compilation of code written in languages like C++ or Rust. With it, for example, you could take the decades-old code of your favorite video game and run it in the browser.
At the moment, WebAssembly helps you optimize the performance of computationally critical parts of your code, but it comes with a price tag. To begin with, you need to know one of thecurrently supported programming languages. You have to become familiar with low-level concepts such as memory management as there’s no garbage collector yet. The integration with JavaScript code is difficult and costly. Also, there’s no easy way to call web APIs from it.
Note: For a deep dive into WebAssembly, check outThe Real Python Podcast - Episode 154 with Brett Cannon.
It seems that, after all these years, JavaScript isn’t going away anytime soon.
One of the first similarities you’ll notice when comparing Python vs JavaScript is that the entry barriers for both are pretty low, making both languages very attractive to beginners who’d like to learn to code. For JavaScript, the only starting requirement is having a web browser. If you’re reading this, then you’ve already got that covered. This accessibility contributes to the language’s popularity.
To get a taste of what it’s like to write JavaScript code, you can stop reading now and type the following text into the address bar before navigating to it:
The literal text is Copying and pasting such a snippet into the address bar will fail in most browsers, which filter out the If you’re viewing this page on a desktop or a laptop computer, then you can take advantage of theweb developer tools, which provide comparable experience across competing web browsers. Note: The examples that follow useGoogle Chrome version To toggle these tools, refer to your browser’s documentation or try one of these common keyboard shortcuts: F12 Ctrl+Shift+I Cmd+Option+I This feature may be disabled by default if you’re using Apple Safari or Microsoft Edge, for example. Once the web developer tools are activated, you’ll see a myriad of tabs and toolbars with content similar to this: Collectively, it’s a powerful development environment equipped with a JavaScript debugger, a performance and memory profiler, a network traffic manager, and much, much more. There’s even a remote debugger for physical devices connected over a USB cable! For the moment, however, just focus on theconsole, which you can access by clicking a tab located at the top. Alternatively, you can quickly bring it to the front by pressingEsc at any time while using the web developer tools. The console is primarily used for inspectinglog messages emitted by the current web page, but it can also be a great JavaScript learning aid. Just like with theinteractive Python interpreter, you can type JavaScript code directly into the console to have itexecuted on the fly: It has everything you’d expect from a typicalREPL tool and more. In particular, the console comes with syntax highlighting, contextual autocomplete, command history, line editing similar toGNU Readline, and the ability to render interactive elements. Its rendering abilities can be especially useful for introspecting objects and tabular data, jumping to source code from a stack trace, or viewing HTML elements. You can log custom messages to the console using a predefined This will make the message appear in the console tab in the web developer tools. Apart from that, there are a few moreuseful methods available in the#"https://files.realpython.com/media/javascript_alert.ffc4dd53a1db.png">
#"#web-developer-tools" title="Permanent link">
80.0
. Keyboard shortcuts may vary for other browsers, but the interface should be largely the same.console
object. JavaScript’sconsole.log()
is the equivalent ofPython’sprint()
:console.log('hello world');
console
object.
By far the most natural place for the JavaScript code is somewhere near anHTML document, which it typically manipulates. You’ll learn more on that later. You can reference JavaScript from HTML in three different ways:
Method | Code Example |
---|---|
HTML Element’s Attribute | <button>Click</button> |
HTML<script> Tag | <script>alert('hello');</script> |
External File | <script src="/path/to/file.js"></script> |
You can have as many of these as you like. The first and second methods embedinline JavaScript directly within an HTML document. While this is convenient, you should try to keep imperative JavaScript separate from declarative HTML to promote readability.
It’s more common to find one or more<script>
tags referencingexternal files with JavaScript code. These files can be served by either a local or a remote web server.
The<script>
tag can appear anywhere in the document as long as it’s nested in either the<head>
or the<body>
tag:
<!DOCTYPE html><html><head><metacharset="UTF-8"><title>Home Page</title><scriptsrc="https://server.com/library.js"></script><scriptsrc="local/assets/app.js"></script><script>functionadd(a,b){returna+b;}</script></head><body><p>Lorem ipsum dolor sit amet (...)</p><script>console.log(add(2,3));</script></body></html>
What’s important is how web browsers process HTML documents. A document is read top to bottom. Whenever a<script>
tag is found, it gets immediately executed even before the page has been fully loaded. If your script tries to find HTML elements that haven’t been rendered yet, then you’ll get an error.
To be safe, always put the<script>
tags at the bottom of your document body:
<!DOCTYPE html><html><head><metacharset="UTF-8"><title>Home Page</title></head><body><p>Lorem ipsum dolor sit amet (...)</p><scriptsrc="https://server.com/library.js"></script><scriptsrc="local/assets/app.js"></script><script>functionadd(a,b){returna+b;}</script><script>console.log(add(2,3));</script></body></html>
Not only will this protect you against the said error, but it will also improve the overall user experience. By moving those tags down, you’re allowing the user to see the fully rendered page before the JavaScript files start to download. You could alsodefer
the download of external JavaScript files until the page has loaded:
<scriptsrc="https://server.com/library.js"defer></script>
If you want to find out more about mixing JavaScript with HTML, then take a look at aJavaScript Tutorial by W3Schools.
You don’t need a web browser to execute JavaScript code anymore. There’s a tool calledNode.js that provides aruntime environment for server-side JavaScript.
A runtime environment comprises theJavaScript engine, which is the language interpreter or compiler, as well as anAPI for interacting with the world. There are several alternative engines that come with different web browsers:
Web Browser | JavaScript Engine |
---|---|
Apple Safari | JavaScriptCore |
Microsoft Edge | V8 |
Microsoft IE | Chakra |
Mozilla Firefox | SpiderMonkey |
Google Chrome | V8 |
Each of these is implemented and maintained by its vendor. For the end user, however, there’s no noticeable difference except for the performance of individual engines. Node.js uses the same V8 engine developed by Google for its Chrome browser.
When running JavaScript inside a web browser, you typically want to be able to respond to mouse clicks, dynamically add HTML elements, or maybe get an image from the webcam. But that doesn’t make sense in a Node.js application, which runs outside of the browser.
After you’veinstalled Node.js for your platform, you can execute JavaScript code just like with the Python interpreter. To start an interactive session, go to yourterminal and typenode
:
$node> 2 + 24
This is similar to the web developer console that you saw earlier. However, as soon as you try to refer to something browser related, you’ll get an error:
>alert('hello world');Thrown:ReferenceError:alertisnotdefined
That’s because your runtime environment is missing the other component, which is the browser API. At the same time, Node.js provides aset of APIs that are useful in a back-end application, such as the file system API:
>constfs=require('fs');>fs.existsSync('/path/to/file');false
For safety reasons, you won’t find these APIs in the browser. Imagine allowing some random website to have control over the files on your computer!
If the standard library doesn’t satisfy your needs, then you can always install a third-party package with theNode Package Manager (npm
) that comes with the Node.js environment. To browse or search for packages, go to thenpm
public registry, which is like thePython Package Index (PyPI).
Similar to thepython
command, you can runscripts with Node.js:
$echo"console.log('hello world');">hello.js$nodehello.jshello world
By providing a path to a text file with the JavaScript code inside, you’re instructing Node.js to run that file instead of starting a new interactive session.
On Unix-like systems, you can even indicate which program to run the file with using ashebang comment in the very first line of the file:
#!/usr/bin/env nodeconsole.log('hello world');
The comment has to be a path to the Node.js executable. However, to avoid hard-coding an absolute path, which may differ across installations, it’s best to let theenv tool figure out where Node.js is installed on your machine.
Then you have to make the file executable before you can run it as if it were a Python script:
$chmod+xhello.js$./hello.jshello world
The road to building full-blownweb applications with Node.js is long and winding, but so is the path to writingDjango orFlask applications in Python.
Sometimes the runtime environment for JavaScript can be another programming language. This is typical of scripting languages in general. Python, for example, is widely used inplugin development. You’ll find it in theSublime Text editor,GIMP, andBlender.
To give you an example, you can evaluate JavaScript code in a Java program using the scripting API:
packageorg.example;importjavax.script.ScriptEngine;importjavax.script.ScriptEngineManager;importjavax.script.ScriptException;publicclassApp{publicstaticvoidmain(String[]args)throwsScriptException{finalScriptEngineManagermanager=newScriptEngineManager();finalScriptEngineengine=manager.getEngineByName("javascript");System.out.println(engine.eval("2 + 2"));}}
This is a Java extension, though it might not be available in your particularJava virtual machine. Subsequent Java generations bundle alternative scripting engines, such asRhino,Nashorn, andGraalVM.
Why is this useful?
As long as the performance isn’t too bad, you couldreuse the code of an existing JavaScript library instead of rewriting it in another language. Perhaps solving a problem, such as math expression evaluation, would be moreconvenient with JavaScript than your native language. Finally, using a scripting language forbehavior customization at runtime, like data filtering or validation, could be the only way to go in a compiled language.
In this section, you’ll compare Python vs JavaScript from a Pythonista’s perspective. There will be some new concepts ahead, but you’ll also discover a few similarities between the two languages.
Python is ageneral-purpose, multi-paradigm, high-level, cross-platform, interpreted programming language with a rich standard library and an approachable syntax.
As such, it’s used across a wide range of disciplines, including computer science education,scripting andautomation, prototyping,software testing,web development, programmingembedded devices, andscientific computing. Although it’s doable, you probably wouldn’t choose Python as the primary technology for video game or mobile app development.
JavaScript, on the other hand, originated solely as aclient-side scripting language for making HTML documents a little more interactive. It’s intentionally simple and has a singular focus: adding behavior to user interfaces. This is still true today despite its improved capabilities. With Javascript, you can build not only web applications but also desktop programs and mobile apps. Tailor-made runtime environments let you execute JavaScript on the server or even on IoT devices.
Python emphasizes codereadability andmaintainability at the price of its expressiveness. After all, you can’t even format your code too much without breaking it. You also won’t find esoteric operators like you would in C++ or Perl since most of the Pythonoperators are English words. Some people joke that Python is executablepseudocode thanks to its straightforward syntax.
As you’ll find out later, JavaScript offers much moreflexibility but also more ways to cause trouble. For example, there’s no one right way of creating custom data types in JavaScript. Besides, the language needs to remain backward compatible with older browsers even when new syntax fixes a problem.
Up until recently, you would find two largely incompatible versions of Python available for download on itsofficial website. This divide betweenPython 2.7 andPython 3.x was confusing to beginners and was a major factor in slowing down the adoption of the latest development branch.
In January 2020, after years of delaying the deadline, the support for Python 2.7 was finally dropped. However, despite the looming lack of security updates and warnings issued by some government agencies, there are still a lot of projects that haven’t migrated yet:
Brendan Eich created JavaScript in 1995, but the ECMAScript we know today was standardized two years later. Since then, there have been only a handful of releases, which looks stagnant compared to the multiple new versions of Python released each year during the same period.
Notice the gap between ES3 and ES5, which lasted an entire decade! Due to political conflicts and disagreements in the technical committee,ES4 never made its way to web browsers, but it was used by Macromedia (later Adobe) as a base for ActionScript.
The first major overhaul to JavaScript came in 2015 with the introduction of ES6, also known as ES2015 orECMAScript Harmony. It brought a lot of new syntactical constructs, which made the language more mature, safe, and convenient for the programmer. It also marked a turning point in the ECMAScript release schedule, which now promises a new version every year.
Such a fast pace means that you can’t assume the latest language version has been adopted by all major web browsers since it takes time to roll out updates. That’s whytranspiling andpolyfills prevail. Today, pretty much any modern web browser can support ES5, which is the default target for the transpilers.
To run a Python program, you first need to download, install, and possibly configure itsinterpreter for your platform. Some operating systems provide an interpreter out of the box, but it may not be the version that you’re looking to use. There are alternative Python implementations, includingCPython,PyPy,Jython,IronPython, orStackless Python. You can also choose from multiple Pythondistributions, such asAnaconda, that come with preinstalled third-party packages.
JavaScript is different. There’s no stand-alone program to download. Instead, every major web browser ships with some kind ofJavaScript engine and an API, which together make the runtime environment. In the previous section, you learned about Node.js, which allows for running JavaScript code outside of the browser. You also know about the possibility to embed JavaScript in other programming languages.
A language ecosystem consists of its runtime environment, frameworks, libraries, tools, and dialects as well as its best practices and unwritten rules. Which combination you choose will depend on your particular use case.
In the old days, you didn’t need much more than a good code editor to write JavaScript. You’d download a few libraries likejQuery,Underscore.js, orBackbone.js, or rely on a Content Delivery Network (CDN) to provide them for your clients. Today, the number ofquestions you need to answer and the tools you need to acquire to start building even the simplest website can be daunting.
The build process for a front-end app is as complicated as it is for a back-end app, if not more so. Your web project goes through linting, transpilation, polyfilling, bundling, minification, and more. Heck, even the CSS style sheets are no longer sufficient and need to be compiled from an extension language by a preprocessor such asSass orLess.
To alleviate that, some frameworks offer utilities that set up the default project structure, generate configuration files, and download dependencies for you. As an example, you can create a newReact app with this short command, provided that you already have the latest Node.js on your computer:
$npxcreate-react-apptodo
At the time of writing, this command took several minutes to finish and installed a whopping 166 MB in 1,815 packages! Compare this tostarting a Django project in Python, which is instantaneous:
$django-adminstartprojectblog
The modern JavaScript ecosystem is enormous and keeps evolving, which makes it impossible to give a thorough overview of its elements. You’ll encounter plenty of foreign tools as you’re learning JavaScript. However, the concepts behind some of them will sound familiar. Here’s how you can map them back to Python:
Python | JavaScript | |
---|---|---|
Code Editor / IDE | PyCharm,VS Code | Atom,VS Code,WebStorm |
Code Formatter | black | Prettier |
Dependency Manager | Pipenv ,poetry | bower (deprecated),npm ,yarn |
Documentation Tool | Sphinx | JSDoc ,sphinx-js |
Interpreter | bpython ,ipython ,python | node |
Library | requests ,dateutil | axios ,moment |
Linter | flake8 ,pyflakes ,pylint | eslint ,tslint |
Package Manager | pip ,twine | bower (deprecated),npm ,yarn |
Package Registry | PyPI | npm |
Package Runner | pipx | npx |
Runtime Manager | pyenv | nvm |
Scaffolding Tool | cookiecutter | cookiecutter ,Yeoman |
Test Framework | doctest ,nose ,pytest | Jasmine ,Jest ,Mocha |
Web Framework | Django,Flask,Tornado | Angular,React,Vue.js |
This list isn’t exhaustive. Besides, some of the tools mentioned above have overlapping capabilities, so it’s hard to make an apples-to-apples comparison in each category.
Sometimes there isn’t a direct analogy between Python vs JavaScript. For example, while you may be used to creating isolatedvirtual environments for your Python projects, Node.js handles that out of the box by installing dependencies into a local folder.
Conversely, JavaScript projects may require additional tools that are unique to front-end development. One such tool isBabel, whichtranspiles your code according to various plugins grouped into presets. It can handle experimental ECMAScript features as well as TypeScript and even React’sJSX extension syntax.
Another category of tool is themodule bundler, whose role is to consolidate multiple independent source files into one that can be easily consumed by a web browser.
During development, you want to break down your code into reusable, testable, and self-containedmodules. That’s reasonable for an experienced Python programmer. Unfortunately, JavaScript didn’t originally come with support for modularity. You still need to use a separate tool for that, although this requirement is changing. Popular choices for module bundlers arewebpack,Parcel, andBrowserify, which can also handle static assets.
Then you havebuild automation tools such asGrunt andgulp. They are vaguely similar toFabric andAnsible in Python, although they’re used locally. These tools automate boring tasks such as copying files or running the transpiler.
In a large-scale single-page application (SPA) with a lot of interactive UI elements, you may need a specialized library such asRedux orMobX forstate management. These libraries aren’t tied to any particular front-end framework but can be quickly hooked up.
As you can see, learning the JavaScript ecosystem is an endless journey.
Both languages take advantage of automatic heapmemory management to eliminate human error and to reduce cognitive load. Nevertheless, this doesn’t completely free you from the risk of getting amemory leak, and it adds some performance overhead.
Note: A memory leak occurs when a piece of memory that is no longer needed remains unnecessarily occupied and there is no way to deallocate it since it’s no longer reachable from your code. A common source of memory leaks in JavaScript areglobal variables andclosures that hold strong references to defunct objects.
The orthodox CPython implementation usesreference counting as well as non-deterministicgarbage collection (GC) to deal with reference cycles. Occasionally, you may be forced to manually allocate and reclaim the memory when you venture into writing a customC extension module.
In JavaScript, the actual implementation of memory management is also left to your particular engine and version since it’s not a part of the language specification. The basic strategy for garbage collection is usually themark-and-sweep algorithm, but various optimization techniques exist.
For example, the heap can be organized into generations that separate short-lived objects from long-lived ones. Garbage collection can run concurrently to offload the main thread of execution. Taking an incremental approach can help avoid bringing the program to a complete stop while the memory is cleaned up.
You must be itching to learn about the JavaScript syntax, but first let’s take a quick look at itstype system. It’s one of the most important components that define any programming language.
Both Python and JavaScript aredynamically typed because theycheck types at runtime, when the application is executing, rather than at compile time. It’s convenient because you aren’t forced to declare a variable’s type such asint
orstr
:
>>>data=42>>>data='This is a string'
Here, you reuse the same variable name for two different kinds of entities that have distinct representations in computer memory. First it’s an integer number, and then it’s a piece of text.
Note: It’s worth noting that some statically typed languages, such as Scala, also don’t require an explicit type declaration as long as it can beinferred from the context.
Dynamic typing is often misunderstood as not having any types whatsoever. This is coming from languages in which a variable works like a box that can only fit a certain type of object. In both Python and JavaScript, the type information is tied not to the variable but to the object it points to. Such a variable is merely an alias, a label, or a pointer to some object in memory.
A lack of type declarations is great for prototyping, but in larger projects it quickly becomes a bottleneck from the maintenance point of view. Dynamic typing is less secure due to a higher risk of bugs going undetected inside of infrequently exercised code execution paths.
Moreover, it makes reasoning about the code much more difficult both for humans and forcode editors. Python addressed this problem by introducingtype hinting, which you can sprinkle variables with:
data:str='This is a string'
By default, type hints provide only informative value since the Python interpreter doesn’t care about them at runtime. However, you can add a separate utility, such as astatic type checker, to your tool chain to get an early warning about mismatched types. The type hints are completely optional, which makes it possible to combine dynamically typed code with statically typed code. This approach is known asgradual typing.
The idea of gradual typing was borrowed from TypeScript, which is essentially JavaScript with types that you can transpile back to plain old JavaScript.
Another common feature of both languages is the use ofduck typing for testing type compatibility. However, an area where Python vs JavaScript are significantly different is the strength of their type-checking mechanisms.
Python demonstratesstrong typing by refusing to act upon objects with incompatible types. For example, you can use the plus (+
) operator to add numbers or toconcatenate strings, but you can’t mix the two:
>>>'3'+2Traceback (most recent call last): File"<stdin>", line1, in<module>TypeError:can only concatenate str (not "int") to str
The interpreter won’t implicitly promote one type to another. You have to decide for yourself and make a suitable type casting manually. If you wanted an algebraic sum, then you’d do this:
>>>int('3')+25
To join the two strings together, you’d cast the second operand accordingly:
>>>'3'+str(2)>>>'32'
JavaScript, on the other hand, usesweak typing, which automatically coerces types according to a set of rules. Unfortunately, these rules are inconsistent and hard to remember as they depend onoperator precedence.
Taking the same example as before, JavaScript will implicitly convert numbers to strings when you use the plus (+
) operator:
>'3'+2'32'
That’s great as long as it’s the desired behavior. Otherwise, you’ll be pulling your hair out trying to find the root cause of a logical error. But it gets even more funky than that. Let’s see what happens if you change the operator to something else:
>'3'-21
Now it’s the other operand that gets converted to a number so the end result isn’t a string. As you can see, weak typing can be quite surprising.
The strength of type checking isn’t just black and white. Python lies somewhere in the middle of this spectrum. For instance, it’ll happily add an integer to a floating-point number, whereas theSwift programming language would raise an error in such a situation.
Note: Strong vs weak typing is independent from static vs dynamic typing. For instance, theC programming language is statically and weakly typed at the same time.
To recap, JavaScript is dynamically as well as weakly typed and supports duck typing.
In Python, everything is anobject, whereas JavaScript makes a distinction betweenprimitive andreference types. They differ in a couple of ways.
First, there are only a few predefined primitive types that you need to care about because you can’t make your own. The majority of built-in data types that come with JavaScript are reference types.
These are the onlyprimitive types available in #"https://github.com/tc39/proposal-bigint">proposal to include a newBigInt
numeric type, which some browsers already support, in ES11. Other than that, any custom data types that you might define are going to be reference types.
Variables of primitive types are stored in a special memory area called thestack, which is fast but has a limited size and is short-lived. Conversely, objects with reference types are allocated on the heap, which is only restricted by the amount of physical memory available on your computer. Such objects have a much longer life cycle but are slightly slower to access.
Primitive types are bare values without any attributes or methods to call. However, as soon as you try to access one using dot notation, the JavaScript engine will instantly wrap a primitive value in the corresponding wrapper object:
>'Lorem ipsum'.length11
Even though a string literal in JavaScript is a primitive data type, you can check its.length
attribute. What happens under the hood is that your code is replaced with a call to theString
object’sconstructor:
>newString('Lorem ipsum').length11
A constructor is a special function that creates a new instance of a given type. You can see that the.length
attribute is defined by theString
object. This wrapping mechanism is known asautoboxing and was copied directly from the Java programming language.
The other and more tangible difference between primitive and reference types is how they’re passed around. Specifically, whenever you assign or pass a value of a primitive type, you actually create a copy of that value in memory. Here’s an example:
>x=42>y=x>x++// This is short for x += 1>console.log(x,y)4342
The assignmenty = x
creates a new value in memory. Now you have two distinct copies of the number42
referenced byx
andy
, so incrementing one doesn’t affect the other.
However, when you pass a reference to an object literal, then both variables point to the same entity in memory:
>x={name:'Person1'}>y=x>x.name='Person2'>console.log(y){name:'Person2'}
Object
is a reference type in JavaScript. Here, you’ve got two variables,x
andy
, referring to the same instance of aPerson
object. The change made to one of the variables is reflected in the other variable.
Last but not least, primitive types areimmutable, which means that you can’t change their state once they are initialized. Every modification, such as incrementing a number or making text uppercase, results in a brand-new copy of the original value. While this is a bit wasteful, there are plenty of good reasons to use immutable values, includingthread safety, simpler design, and consistent state management.
Note: To be fair, this is almost identical to how Python deals with passing objects despite its lack of primitive types. Mutable types such aslist
anddict
don’t create copies, whereas immutable types such asint
andstr
do.
To check if a variable is a primitive type or a reference type in JavaScript, you can use the built-intypeof
operator:
>typeof'Lorem ipsum''string'>typeofnewString('Lorem ipsum')'object'
For reference types, thetypeof
operator always returns a generic"object"
string.
Note: Always use thetypeof
operator to check if a variable isundefined
. Otherwise, you may find yourself in trouble:
>typeofnoSuchVariable==='undefined'true>noSuchVariable===undefinedReferenceError:noSuchVariableisnotdefined
Comparing a non-existing variable to any value will throw an exception!
If you want to obtain a more detailed information about a particular type, then you have a couple of options:
>today=newDate()>today.constructor.name'Date'>todayinstanceofDatetrue>Date.prototype.isPrototypeOf(today)true
You can try checking an object’s constructor name using theinstanceof
operator, or you can test if it’s derived from a particular parent type with the.prototype
property.
Python and JavaScript areobject-oriented programming languages. They both allow you to express code in terms of objects that encapsulate identity, state, and behavior. While most programming languages, including Python, useclass-based inheritance, JavaScript is one of a few that don’t.
Note: A class is a template for objects. You can think of classes like cookie cutters or object factories.
To create hierarchies of custom types in JavaScript, you need to become familiar withprototypal inheritance. That is often one the most challenging concepts to understand when you make a switch from a more classical inheritance model. If you have twenty minutes, then you can watch a greatvideo on prototypes that clearly explains the concept.
Note: Contrary to Python,multiple inheritance isn’t possible in JavaScript because any given object can have only one prototype. That said, you can useproxy objects, which were introduced in ES6, to mitigate that.
The gist of the story is that there are no classes in JavaScript. Well, technically, you can use theclass
keyword that was introduced in ES6, but it’s purely asyntactic sugar to make things easier for newcomers. Prototypes are still used behind the scenes, so it’s worthwhile to get a closer look at them, which you’ll have a chance to do later on.
Lastly,functions are an interesting part of the JavaScript and Python type systems. In both languages, they’re often referred to asfirst-class citizens orfirst-class objects because the interpreter doesn’t treat them any differently than other data types. You can pass a function as an argument, return it from another function, or store it in a variable just like a regular value.
This is a very powerful feature that allows you to definehigher-order functions and to take full advantage of thefunctional paradigm. For languages in which functions are special entities, you can work around this with the help of design patterns such as thestrategy pattern.
JavaScript is even more flexible than Python in regard to functions. You can define an anonymousfunction expression full of statements with side effects, whereas Python’slambda function must contain exactly one expression and no statements:
letcountdown=5;constid=setInterval(function(){if(countdown>0){console.log(`${countdown--}...`);}elseif(countdown===0){console.log('Go!');clearInterval(id);}},1000);
The built-insetInterval()
lets you execute a given function periodically in time intervals expressed in milliseconds until you callclearInterval()
with the corresponding ID. Notice the use of aconditional statement and the mutation of a variable from the outer scope of the function expression.
JavaScript and Python are both high-level scripting languages that share a fair bit of syntactical similarities. This is especially true of their latest versions. That said, JavaScript was designed to resemble Java, whereas Python was modeled after theABC andModula-3 languages.
One of the hallmarks of Python is the use of mandatoryindentation to denote a block of code, which is quite unusual and frowned upon by new Python converts. Many popular programming languages, including JavaScript, usecurly brackets or special keywords instead:
functionfib(n){if(n>1){returnfib(n-2)+fib(n-1);}return1;}
In JavaScript, every block of code consisting of more than one line needs an opening{
and a closing}
, which gives you the freedom to format your code however you like. You can mix tabs with spaces and don’t need to pay attention to your bracket placement.
Unfortunately, this can result in messy code and sectarian conflicts between developers with different style preferences. This makes code reviews problematic. Therefore, you should always establish coding standards for your team and use them consistently, preferably in an automated way.
Note: You could simplify the function body above by taking advantage of theternary if (?:
), which is sometimes called theElvis operator because it looks like the hairstyle of the famous singer:
return(n>1)?fib(n-2)+fib(n-1):1;
This is equivalent to aconditional expression in Python.
Speaking of indentation, it’s customary for JavaScript code to be formatted usingtwo spaces per indentation level instead of therecommended four in Python.
To reduce friction for those making a switch from Java or anotherC-family programming language, JavaScript terminates statements with a familiarsemicolon (;
). If you’ve ever programmed in one of those languages, then you’ll know that putting a semicolon after an instruction becomes muscle memory:
alert('hello world');
Semicolons aren’t required in JavaScript, though, because the interpreter will take a guess and insert one for you automatically. In most cases, it’ll be right, but sometimes it may lead to peculiar results.
Note: You can use semicolons in Python, too! Although they’re not very popular, they help isolate multiple statements on a single line:
importpdb;pdb.set_trace()
People have strong opinions on whether to use the semicolon explicitly or not. While there are a few corner cases in which it matters, it’s largely just a convention.
Identifiers, such as variable or function names, must bealphanumeric in JavaScript and Python. In other words, they can only contain letters, digits, and a few special characters. At the same time, they can’t start with a digit. While non-Latin characters are allowed, you should generally avoid them:
foo
,foo42
,_foo
,$foo
,fößar
42foo
Names in both languages arecase sensitive, so variables likefoo
andFoo
are distinct. Nonetheless, the naming conventions in JavaScript are slightly different than in Python:
Python | JavaScript | |
---|---|---|
Type | ProjectMember | ProjectMember |
Variable, Attribute, or Function | first_name | firstName |
In general, Python recommends using lower_case_with_underscores, also known assnake_case, for compound names, so that individual words get separated with an underscore character (_
). The only exception to that rule is classes, whose names should follow the CapitalizedWords, or Pascal case, style. JavaScript also uses CapitalizedWords for types but mixedCase, orlower camelCase, for everything else.
JavaScript has single-line as well as multilinecomments:
x++;// This is a single-line comment/* This whole paragraph is a comment and will be ignored.*/
You can start a comment anywhere on a line using a double slash (//
), which is similar to Python’s hash sign (#
). While there are no multiline comments in Python, you can simulate them by enclosing a fragment of code within a triple quote ('''
) to create amultiline string. Alternatively, you can wrap it in anif
statement that never evaluates toTrue
:
ifFalse:...
You can use this trick, for example, to temporarily disable an existing block of code during debugging.
To define string literals in JavaScript, you can use a pair of single quotes ('
) or double quotes ("
) interchangeably, just like in Python. However, for a long time, there was no way to define multiline strings in JavaScript. Only ES6 in 2015 broughttemplate literals, which look like a hybrid off-strings and multiline strings borrowed from Python:
varname='John Doe';varmessage=`Hi${name.split(' ')[0]},We're writing to you regarding...Kind regards,Xyz`;
A template starts with a backtick (`), also known as the grave accent, instead of regular quotes. Tointerpolate a variable or any legal expression, you have to use the dollar sign followed by a pair of matching curly brackets:${...}
. This is different from Python’s f-strings, which don’t require the dollar sign.
When you define a variable in JavaScript the same way that you would normally do in Python, you’re implicitly creating aglobal variable. Since global variables break encapsulation, you should rarely need them! The correct way to declare variables in JavaScript has always been through thevar
keyword:
x=42;// This is a global variable. Did you really mean that?vary=15;// This is global only when declared in a global context.
Unfortunately, this doesn’t declare a truly local variable, and it has its own problems that you’ll find out about in the upcoming section. Since ES6, there’s been a better way to declarevariables andconstants with thelet
andconst
keywords, respectively:
>letname='John Doe';>constPI=3.14;>PI=3.1415;TypeError:Assignmenttoconstantvariable.
Unlike constants, variables in JavaScript don’t need an initial value. You can provide one later:
letname;name='John Doe';
When you leave off the initial value, you create what’s called a variabledeclaration rather than a variabledefinition. Such variables automatically receive a special value ofundefined
, which is one of the primitive types in JavaScript. This is different in Python, where you always define variables except forvariable annotations. But even then, these variables aren’t technically declared:
name:strname='John Doe'
Such an annotation doesn’t affect the variable life cycle. If you referred toname
before the assignment, then you’d receive aNameError
exception.
If you’ve been complaining about Python not having a properswitch statement, then you’ll be happy to learn that JavaScript does:
// As with C, clauses will fall through unless you break out of them.switch(expression){case'kilo':value=bytes/2**10;break;case'mega':value=bytes/2**20;break;case'giga':value=bytes/2**30;break;default:console.log(`Unknown unit: "${expression}"`);}
The expression can evaluate to any type, including a string, which wasn’t always the case in the older Java versions that influenced JavaScript. By the way, did you notice the familiarexponentiation operator (**
) in the code snippet above? It wasn’t available in JavaScript until ES7 in 2016.
There’s no nativeenumeration type in pure JavaScript, but you can use theenum
type in TypeScript or emulate one with something similar to this:
constSauce=Object.freeze({BBQ:Symbol('bbq'),CHILI:Symbol('chili'),GARLIC:Symbol('garlic'),KETCHUP:Symbol('ketchup'),MUSTARD:Symbol('mustard')});
Freezing an object prevents you from adding or removing its attributes. This is different from a constant, which can be mutable! A constant will always point to the same object, but the object itself might change its value:
>constfruits=['apple','banana'];>fruits.push('orange');// ['apple', 'banana', 'orange']>fruits=[];TypeError:Assignmenttoconstantvariable.
You can add anorange
to the array, which is mutable, but you can’t modify the constant that is pointing to it.
Until ES6, you could only define afunction or an anonymousfunction expression using thefunction
keyword:
functionadd(a,b){returna+b;}letadd=function(a,b){returna+b;};
However, to reduce the boilerplate code and to fix a slight problem with binding functions to objects, you can now use thearrow function in addition to the regular syntax:
letadd=(a,b)=>a+b;
Notice that there’s nofunction
keyword anymore, and the return statement is implicit. The arrow symbol (=>
) separates the function’s arguments from its body. People sometimes call it thefat arrow function because it was originally borrowed from CoffeeScript, which also has athin arrow (->
) counterpart.
Arrow functions are most suitable for small, anonymous expressions likelambdas in Python, but they can contain multiple statements with side effects if needed:
letadd=(a,b)=>{constresult=a+b;returnresult;}
When you want to return anobject literal from an arrow function, you need to wrap it in parentheses to avoid ambiguity with a block of code:
letadd=(a,b)=>({result:a+b});
Otherwise, the function body would be confused for a block of code without anyreturn
statements, and the colon would create alabeled statement rather than a key-value pair.
Starting with ES6, function arguments can havedefault values like in Python:
>functiongreet(name='John'){…console.log('Hello',name);…}>greet();HelloJohn
Unlike Python, however, the default values are resolved every time the function is called instead of only when it’s defined. This makes it possible to safely use mutable types as well as to dynamically refer to other arguments passed at runtime:
>functionfoo(a,b=a+1,c=[]){…c.push(a);…c.push(b);…console.log(c);…}>foo(1);[1,2]>foo(5);[5,6]
Every time you callfoo()
, its default arguments are derived from the actual values passed to the function.
When you want to declare a function withvariable number of parameters in Python, you take advantage of the special*args
syntax. The JavaScript equivalent would be therest parameter defined with thespread (...
) operator:
>functionaverage(...numbers){…if(numbers.length>0){…constsum=numbers.reduce((a,x)=>a+x);…returnsum/numbers.length;…}…return0;…}>average();0>average(1);1>average(1,2);1.5>average(1,2,3);2
The spread operator can also be used to combine iterable sequences. For example, you can extract the elements of one array into another:
constredFruits=['apple','cherry'];constfruits=['banana',...redFruits];
Depending on where you place the spread operator in the target list, you may prepend or append elements or insert them somewhere in the middle.
To unpack an iterable into individual variables or constants, you can use thedestructuring assignment:
>constfruits=['apple','banana','orange'];>const[a,b,c]=fruits;>console.log(b);banana
Similarly, you can destructure and evenrename object attributes:
constperson={name:'John Doe',age:42,married:true};const{name:fullName,age}=person;console.log(`${fullName} is${age} years old.`);
This helps avoid name collisions for variables defined within one scope.
with
StatementsThere’s an alternative way to drill down to an object’s attributes using the slightly oldwith
statement:
constperson={name:'John Doe',age:42,married:true};with(person){console.log(`${name} is${age} years old.`);}
It works like a construct inObject Pascal, in which a local scope gets temporarily augmented with attributes of the given object.
Note: Thewith
statements in Python vs JavaScript arefalse friends. In Python, you use awith
statement to manage resources throughcontext managers.
Since this might be obscure, thewith
statement is generally discouraged and is even unavailable instrict mode.
Since ES6, JavaScript has had theiterable anditerator protocols as well asgenerator functions, which look almost identical to Python’siterables, iterators, andgenerators. To turn a regular function into a generator function, you need to add an asterisk (*
) after thefunction
keyword:
function*makeGenerator(){}
You can’t makegenerator functions out of arrow functions, though.
When you call a generator function, it won’t execute the body of that function. Instead, it returns a suspendedgenerator object that conforms to the iterator protocol. To advance your generator, you can call.next()
, which is similar to Python’s built-innext()
:
>constgenerator=makeGenerator();>const{value,done}=generator.next();>console.log(value);undefined>console.log(done);true
As a result, you’ll always get a status object with two attributes: the subsequent value and a flag that indicates if the generator has been exhausted. Python throws theStopIteration
exception when there are no more values in the generator.
To return some value from your generator function, you can use either theyield
keyword or thereturn
keyword. The generator will keep feeding values until there are no moreyield
statements, or until youreturn
prematurely:
letshouldStopImmediately=false;function*randomNumberGenerator(maxTries=3){lettries=0;while(tries++<maxTries){if(shouldStopImmediately){return42;// The value is optional}yieldMath.random();}}
The above generator will keep yielding random numbers until it reaches the maximum number of tries or you set a flag to make it terminate early.
The equivalent of theyield from
expression in Python, whichdelegates the iteration to another iterator or an iterable object, is theyield*
expression:
>function*makeGenerator(){…yield1;…yield*[2,3,4];…yield5;…}>constgenerator=makeGenerator()>generator.next();{value:1,done:false}>generator.next();{value:2,done:false}>generator.next();{value:3,done:false}>generator.next();{value:4,done:false}>generator.next();{value:5,done:false}>generator.next();{value:undefined,done:true}
Interestingly, it’s legal toreturn
andyield
at the same time:
function*makeGenerator(){returnyield42;}
However, due to a grammar limitation, you’d have to use parentheses to achieve the same effect in Python:
defmake_generator():return(yield42)
To explain what’s going on, you can rewrite that example by introducing a helper constant:
function*makeGenerator(){constmessage=yield42;returnmessage;}
If you knowcoroutines in Python, then you’ll remember that generator objects can be bothproducers andconsumers. You can send arbitrary values into a suspended generator by providing an optional argument to.next()
:
>function*makeGenerator(){…constmessage=yield'ping';…returnmessage;…}>constgenerator=makeGenerator();>generator.next();{value:"ping",done:false}>generator.next('pong');{value:"pong",done:true}
The first call to.next()
runs the generator until the firstyield
expression, which happens to return"ping"
. The second call passes a"pong"
that is stored in the constant and immediately returned from the generator.
The nifty mechanism explored above was the basis forasynchronous programming and the adoption of theasync
andawait
keywords in Python. JavaScript followed the same path by bringing inasynchronous functions with ES8 in 2017.
While a generator function returns a special kind of iterator, the generator object,asynchronous functions always return apromise, which was first introduced in ES6. A promise represents the future result of an asynchronous call such asfetch()
from theFetch API.
When you return any value from an asynchronous function, it’s automatically wrapped in a promise object that can be awaited in another asynchronous function:
asyncfunctiongreet(name){return`Hello${name}`;}asyncfunctionmain(){constpromise=greet('John');constgreeting=awaitpromise;console.log(greeting);// "Hello John"}main();
Typically, you’dawait
and assign the result in one go:
constgreeting=awaitgreet('John');
Although you can’t completely get rid of promises with asynchronous functions, they significantly improve your code readability. It starts to look like synchronous code even though your functions can be paused and resumed multiple times.
One notable difference from the asynchronous code in Python is that, in JavaScript, you don’t need to manually set up theevent loop, which runs in the background implicitly. JavaScript is inherently asynchronous.
You know from an earlier part of this article that JavaScript doesn’t have a concept of classes. Instead, it knows about objects. You can create new objects usingobject literals, which look likePython dictionaries:
letperson={name:'John Doe',age:42,married:true};
It behaves like a dictionary in that you can access individual attributes using dot syntax or square brackets:
>person.age++;>person['age'];43
Object attributes don’t need to be enclosed in quotes unless they contain spaces, but that isn’t a common practice:
>letperson={…'full name':'John Doe'…};>person['full name'];'John Doe'>person.fullname;SyntaxError:Unexpectedidentifier
Just like a dictionary and some objects in Python, objects in JavaScript havedynamic attributes. That means you can add new attributes or delete existing ones from an object:
>letperson={name:'John Doe'};>person.age=42;>console.log(person);{name:"John Doe",age:42}>deleteperson.name;true>console.log(person);{age:42}
Starting from ES6, objects can have attributes withcomputed names:
>letperson={…['full'+'Name']:'John Doe'…};>person.fullName;'John Doe'
Python dictionaries and JavaScript objects are allowed to contain functions as their keys and attributes. There are ways to bind such functions to their owner so that they behave like class methods. For example, you can use a circular reference:
>letperson={…name:'John Doe',…sayHi:function(){…console.log(`Hi, my name is${person.name}.`);…}…};>person.sayHi();Hi,mynameisJohnDoe.
sayHi()
is tightly coupled to the object it belongs to because it refers to theperson
variable by name. If you were to rename that variable at some point, then you’d have to go through the whole object and make sure to update all occurrences of that variable name.
A slightly better approach takes advantage of the implicitthis
variable that is exposed to functions. The value ofthis
can be different depending on who’s calling the function:
>letjdoe={…name:'John Doe',…sayHi:function(){…console.log(`Hi, my name is${this.name}.`);…}…};>jdoe.sayHi();Hi,mynameisJohnDoe.
After replacing a hard-codedperson
withthis
, which is similar to Python’sself
, it won’t matter what the variable name is, and the result will be the same as before.
Note: The example above won’t work if you replace the function expression with an arrow function, because the latter has different scoping rules for thethis
variable.
That’s great, but as soon as you decide to introduce more objects of the samePerson
kind, you’ll have to repeat all attributes and redefine all functions in each object. What you’d rather have is a template forPerson
objects.
The canonical way of creating custom data types in JavaScript is to define aconstructor, which is an ordinary function:
functionPerson(){console.log('Calling the constructor');}
As a convention, to denote that such a function has a special meaning, you’d capitalize the first letter to follow CapitalizedWords instead of the usual mixedCase.
On the syntactical level, however, it’s just a function that you can call normally:
>Person();Callingtheconstructorundefined
What makes it special ishow you call it:
>newPerson();CallingtheconstructorPerson{}
When you add thenew
keyword in front of the function call, it’ll implicitly return a brand-new instance of a JavaScript object. That means your constructor shouldn’t contain thereturn
statement.
While the interpreter is responsible for allocating memory for and scaffolding a new object, the role of a constructor is to give the object an initial state. You can use the previously mentionedthis
keyword to refer to a new instance under construction:
functionPerson(name){this.name=name;this.sayHi=function(){console.log(`Hi, my name is${this.name}.`);}}
Now you can create multiple distinctPerson
entities:
constjdoe=newPerson('John Doe');constjsmith=newPerson('John Smith');
Alright, but you’re still duplicating function definitions across all instances of thePerson
type. The constructor is just a factory that hooks the same values to individual objects. It’s wasteful and could lead to inconsistent behavior if you were to change it at some point. Consider this:
>constjdoe=newPerson('John Doe');>constjsmith=newPerson('John Smith');>jsmith.sayHi=_=>console.log('What?');>jdoe.sayHi();Hi,mynameisJohnDoe.>jsmith.sayHi();What?
Since every object gets a copy of its attributes, including functions, you must carefully update all instances to keep a uniform behavior. Otherwise, they’ll do different things, which typically isn’t what you want. Objects might have a different state, but their behavior usually won’t change.
As a rule of thumb, you should move the business logic from the constructor, which is concerned about data, to theprototype object:
functionPerson(name){this.name=name;}Person.prototype.sayHi=function(){console.log(`Hi, my name is${this.name}.`);};
Every object has a prototype. You can access your custom data type’s prototype by referring to the.prototype
attribute of your constructor. It’ll already have a few predefined attributes, such as.toString()
, that are common to all objects in JavaScript. You can add more attributes with your custom methods and values.
When JavaScript is looking for an object’s attribute, it begins by trying to find it in that object. Upon failure, it moves on to the respective prototype. Therefore, attributes defined in a prototype are shared across all instances of the corresponding type.
Prototypes are chained, so the attribute lookup continues until there are no more prototypes in the chain. This is analogous to type hierarchy through inheritance.
Not only can you create methods in one place thanks to the prototypes, but you can also createstatic attributes by attaching them to one:
>Person.prototype.PI=3.14;>newPerson('John Doe').PI;3.14>newPerson('John Smith').PI;3.14
To illustrate the power of prototypes, you may try to extend the behavior of existing objects, or even a built-in data type. Let’s add a new method to thestring
type in JavaScript by specifying it in the prototype object:
String.prototype.toSnakeCase=function(){returnthis.replace(/\s+/g,'').split(/(?<=[a-z])(?=[A-Z])/g).map(x=>x.toLowerCase()).join('_');};
It usesregular expressions to transform the text into snake_case. Suddenly, string variables, constants, and even string literals can benefit from it:
>"loremIpsumDolorSit".toSnakeCase();'lorem_ipsum_dolor_sit'
However, this is a double-edged sword. In a similar way, someone could override one of the existing methods in a prototype of a popular type, which would break the assumptions made elsewhere. Suchmonkey patching can be useful intesting but is otherwise very dangerous.
Since ES6, there’s been an alternative way to define prototypes that uses a much more familiar syntax:
classPerson{constructor(name){this.name=name;}sayHi(){console.log(`Hi, my name is${this.name}.`);}}
Even though this looks like you’re defining aclass, it’s only a convenient high-level metaphor for specifying custom data types in JavaScript. Behind the scenes, there are no real classes! For that reason, some people go so far as to advocate against using this new syntax at all.
You can havegetters andsetters in your class, which are similar to the Python’sclass properties:
>classSquare{…constructor(size){…this.size=size;// Triggers the setter…}…setsize(value){…this._size=value;// Sets the private field…}…getarea(){…returnthis._size**2;…}…}>constbox=newSquare(3);>console.log(box.area);9>box.size=5;>console.log(box.area);25
When you omit the setter, you create a read-only property. That’s misleading, however, because you can still access the underlyingprivate
field like you can in Python.
A common pattern to encapsulate internal implementation in JavaScript is anImmediately Invoked Function Expression (IIFE), which can look like this:
>constodometer=(function(initial){…letmileage=initial;…return{…get:function(){returnmileage;},…put:function(miles){mileage+=miles;}…};…})(33000);>odometer.put(65);>odometer.put(12);>odometer.get();33077
In other words, it’s an anonymous function that calls itself. You can use the newer arrow function to create an IIFE too:
constodometer=((initial)=>{letmileage=initial;return{get:_=>mileage,put:(miles)=>mileage+=miles};})(33000);
This is how JavaScript historically emulatedmodules to avoid name collisions in the globalnamespace. Without an IIFE, which uses closures and function scope to expose only a limited public-facing API, everything would be accessible from the calling code.
Sometimes you want to define a factory or a utility function that logically belongs to your class. In Python, you have the@classmethod
and@staticmethod
decorators, which allow you to associatestatic methods with the class. To achieve the same result in JavaScript, you need to use thestatic
method modifier:
classColor{staticbrown(){returnnewColor(244,164,96);}staticmix(color1,color2){returnnewColor(...color1.channels.map((x,i)=>(x+color2.channels[i])/2));}constructor(r,g,b){this.channels=[r,g,b];}}constcolor1=Color.brown();constcolor2=newColor(128,0,128);constblended=Color.mix(color1,color2);
Note that there’s no way of defining static class attributes at the moment, at least not without additional transpiler plugins.
Chaining prototypes can resembleclass inheritance when youextend
one class from another:
classPerson{constructor(firstName,lastName){this.firstName=firstName;this.lastName=lastName;}fullName(){return`${this.firstName}${this.lastName}`;}}classGentlemanextendsPerson{signature(){return'Mr. '+super.fullName()}}
In Python, you could extend more than one class, but that it isn’t possible in JavaScript. To reference attributes from theparent class, you can usesuper()
, which has to be called in the constructor to pass arguments up the chain.
Decorators are yet another feature that JavaScript copied from Python. They’re still technically aproposal that is subject to change, but you can test them out using anonline playground or a local transpiler. Be warned, however, that they require a bit of configuration. Depending on the chosen plugin and its options, you’ll get different syntax and behavior.
Several frameworks already use a custom syntax for decorators, which need to be transpiled into plain JavaScript. If you opt for theTC-39 proposal, then you’ll be able to decorate only classes and their members. It seems there won’t be any special syntax for function decorators in JavaScript.
It took ten days for Brendan Eich to create a prototype of what later became JavaScript. After it was presented to the stakeholders at a business meeting, the language was considered production ready and didn’t go through a lot of changes for many years.
Unfortunately, that made the language infamous for its oddities. Some people didn’t even regard JavaScript as a “real” programming language, which made it a victim of many jokes and memes.
Today, the language is much friendlier than it used to be. Nevertheless, it’s worth knowing what to avoid, since a lot of legacy JavaScript is still out there waiting to bite you.
Python’slists and tuples are implemented as arrays in thetraditional sense, whereas JavaScript’sArray
type has more in common with Python’sdictionary. What’s an array, then?
In computer science, an array is a data structure that occupies a contiguous block of memory, and whose elements are ordered and have homogeneous sizes. This way, you can access them randomly with a numerical index.
In Python, a list is an array ofpointers that are typically integer numbers, which reference heterogeneous objects scattered around in various regions of memory.
Note: For low-level arrays in Python, you might be interested in checking out the built-inarray
module.
JavaScript’s array is an object whose attributes happen to be numbers. They’re not necessarily stored next to each other. However, they keep the right order during iteration.
When you delete an element from an array in JavaScript, you make a gap:
>constfruits=['apple','banana','orange'];>deletefruits[1];true>console.log(fruits);['apple',empty,'orange']>fruits[1];undefined
The array doesn’t change its size after the removal of one of its elements:
>console.log(fruits.length);3
Conversely, you can put a new element at a distant index even though the array is much shorter:
>fruits[10]='watermelon';>console.log(fruits.length);11>console.log(fruits);['apple',empty,'orange',empty×7,'watermelon']
This wouldn’t work in Python.
Python is clever aboutsorting data because it can tell the difference between element types. When you sort a list of numbers, for example, it’ll put them in ascending order by default:
>>>sorted([53,2020,42,1918,7])[7, 42, 53, 1918, 2020]
However, if you wanted to sort a list of strings, then it would magically know how to compare the elements so that they appear inlexicographical order:
>>>sorted(['lorem','ipsum','dolor','sit','amet'])['amet', 'dolor', 'ipsum', 'lorem', 'sit']
Things get complicated when you start to mix different types:
>>>sorted([42,'not a number'])Traceback (most recent call last): File"<stdin>", line1, in<module>TypeError:'<' not supported between instances of 'str' and 'int'
By now, you know that Python is a strongly typed language and doesn’t like mixing types. JavaScript, on the other hand, is the opposite. It’ll eagerly convert elements of incompatible types according to some obscure rules.
You can use.sort()
to do the sorting in #"Code block" data-syntax-language="js" >
>['lorem','ipsum','dolor','sit','amet'].sort();['amet','dolor','ipsum','lorem','sit']
It turns out that sorting strings works as expected. Let’s see how it copes with numbers:
>[53,2020,42,1918,7].sort();[1918,2020,42,53,7]
What happened here is that the array elements got implicitly converted to strings and were sorted lexicographically. To prevent that, you have to provide your custom sortingstrategy as a function of two elements to compare, for example:
>[53,2020,42,1918,7].sort((a,b)=>a-b);[7,42,53,1918,2020]
The contract between your strategy and the sorting method is that your function should return one of three values:
This is a common pattern present in other languages, and it was also theold way of sorting in Python.
At this point, you know that semicolons in JavaScript are optional because the interpreter will insert them automatically at the end of each instruction if you don’t do so yourself.
This can lead to surprising results under some circumstances:
functionmakePerson(name){return({fullName:name,createdAt:newDate()})}
In this example, you might expect the JavaScript engine to insert a missing semicolon at the very end of your function, right after the closing parenthesis of the object literal. However, when you call the function, this happens:
>constjdoe=makePerson('John Doe');>console.log(jdoe);undefined
Your function changed the intended action by returning anundefined
becausetwo semicolons were inserted instead of one:
functionmakePerson(name){return;({fullName:name,createdAt:newDate()});}
As you can see, relying on the fact that semicolons are optional introduces some risk of errors in your code. On the other hand, it won’t help if you start putting semicolons everywhere.
To fix this example, you need to change your code formatting so that the returned value begins on the same line as thereturn
statement:
functionmakePerson(name){return{fullName:name,createdAt:newDate()};}
In some situations, you can’t rely on automatic semicolon insertion and you need to put one explicitly instead. For example, you can’t leave out the semicolon when you start a new line with a parenthesis:
consttotal=2+3(4+5).toString()
This produces a runtime error due to the lack of a semicolon, which makes the two lines collapse into one:
consttotal=2+3(4+5).toString();
A numeric literal can’t be called like a function.
Loops in JavaScript are particularly confusing because there are so many of them and they look alike, whereas Python has just two. The primary type of loop in JavaScript is thefor
loop, which was transplanted from Java:
constfruits=['apple','banana','orange'];for(leti=0;i<fruits.length;i++){console.log(fruits[i]);}
It has three parts, all of which are optional:
let i = 0
i < fruits.length
i++
The first part executes only once before the loop starts, and it typically sets the initial value for the counter. Then, after each iteration, the cleanup part runs to update the counter. Right after that, the condition is evaluated to determine if the loop should continue. This is roughly equivalent to iterating over a list of indices in Python:
fruits=['apple','banana','orange']foriinrange(len(fruits)):print(fruits[i])
Notice how much work Python does for you. On the other hand, having the loop internals exposed gives you a lot of flexibility. This type of loop is generallydeterministic because you know how many times it’ll iterate from the beginning.
In JavaScript, you can make the conventionalfor
loop non-deterministic and eveninfinite by omitting one or more of its parts:
for(;;){// An infinite loop}
However, a more idiomatic way to make such an iteration would involve thewhile
loop, which is quite similar to the one you’d find in Python:
while(true){constage=prompt('How old are you?');if(age>=18){break;}}
In addition to this, JavaScript has ado...while
loop, which is guaranteed to run at least once because it checks the condition after its body. You can rewrite this example in the following way:
letage;do{age=prompt('How old are you?');}while(age<18);
Apart from stopping an iteration midway with thebreak
keyword, you can skip to the next iteration using thecontinue
keyword as you would in Python:
for(leti=0;i<10;i++){if(i%2===0){continue;}console.log(i);}
What you can’t do, though, is use theelse
clause on loops.
You might be tempted to try out thefor...in
loop in JavaScript, thinking it would iterate over values like a Pythonfor
loop. Although it looks similar and has a similar name, it actually behaves very differently!
Afor...in
loop in JavaScript iterates over attributes of the given object, including the ones found in the prototype chain:
>constobject={name:'John Doe',age:42};>for(constattributeinobject){…console.log(`${attribute} =${object[attribute]}`);…}name=JohnDoeage=42
Should you want to exclude attributes attached to the prototype, you can callhasOwnProperty()
. It will test whether a given attribute belongs to an object instance.
When you feed thefor...in
loop with an array, it’ll iterate over the array’s numeric indices. As you know by now, arrays in JavaScript are just glorified dictionaries:
>constfruits=['apple','banana','orange'];…for(constfruitinfruits){…console.log(fruit);…}012
On the other hand, arrays expose.forEach()
, which can substitute for a loop:
constfruits=['apple','banana','orange'];fruits.forEach(fruit=>console.log(fruit));
This is ahigher-order function that accepts a callback that will run for every element in the array. This pattern fits a bigger picture since JavaScript takes a functional approach to iteration in general.
Note: To test if a single attribute is defined in an object, use thein
operator:
>'toString'in[1,2,3];true>'__str__'in[1,2,3];false
Finally, when the ES6 specification introduced the iterable and iterator protocols, it allowed the implementation of a long-awaited loop that would iterate over sequences. However, since thefor...in
name was already taken, they had to come up with a different one.
Thefor...of
loop is the closest relative to thefor
loop in Python. With it, you can iterate over any iterable object, including strings and arrays:
constfruits=['apple','banana','orange'];for(constfruitoffruits){console.log(fruit);}
This is probably the most intuitive way for a Python programmer to iterate in JavaScript.
new
Let’s go back to thePerson
type defined earlier:
functionPerson(name){this.name=name;this.sayHi=function(){console.log(`Hi, my name is${this.name}.`);}}
If you forget to call that constructor correctly, with thenew
keyword in front of it, then it’ll fail silently and leave you with anundefined
variable:
>letbob=Person('Bob');>console.log(bob);undefined
There’s a trick to protect yourself against this mistake. When you omit thenew
keyword, there won’t be any object to bind to, so thethis
variable inside the constructor will point to theglobal object, such as thewindow
object in the web browser. You can detect that and delegate to a valid constructor invocation:
>functionPerson(name){…if(this===window){…returnnewPerson(name);…}…this.name=name;…this.sayHi=function(){…console.log(`Hi, my name is${this.name}.`);…}…}>letperson=Person('John Doe');>console.log(person);Person{name:'John Doe',sayHi:ƒ}
This is the only reason you might want to include areturn
statement in your constructor.
Note: The triple equals sign (===
) is intentional and has to do with the weak typing in JavaScript. You’ll learn more about itbelow.
Unless you’re already at the global scope, your variables automatically become global when you don’t precede their declarations with one of these keywords:
var
let
const
It’s easy to fall into this trap, especially when you’re coming from Python. For example, such a variable defined in a function will become visible outside of it:
>functioncall(){…global=42;…letlocal=3.14…}>call();>console.log(global);42>console.log(local);ReferenceError:localisnotdefined
Interestingly, the rules determining whether you declare a local or a global variable in Python aremuch more complicated than this. There are alsoother kinds of variable scope in Python.
This quirk is only present in legacy code, which uses thevar
keyword for variable declaration. You’ve learned that when a variable is declared like that, it won’t be global. But it isn’t going to have a local scope either.
No matter how deep in the function a variable is defined, it’ll be scoped to the entire function:
>functioncall(){…if(true){…for(leti=0;i<10;i++){…varnotGlobalNorLocal=42+i;…}…}…notGlobalNorLocal--;…console.log(notGlobalNorLocal);…}>call();50
The variable is visible and still alive at the top level of the function right before exiting. However, nested functions don’t expose their variables to the outer scope:
>functioncall(){…functioninner(){…varnotGlobalNorLocal=42;…}…inner();…console.log(notGlobalNorLocal);…}>call();ReferenceError:notGlobalNorLocalisnotdefined
It works the other way around, though. Inner functions can see the variables from the outer scope, but it gets even more interesting when you return the inner function for later use. This creates aclosure.
This one is related to theprevious quirk and, again, applies to variables declared with the infamousvar
keyword.
Let’s begin with a little riddle:
varx=42;functioncall(){console.log(x);// A = ???varx=24;console.log(x);// B = ???}call();console.log(x);// C = ???
Consider what the result will be:
42
, B =24
, C =42
42
, B =24
, C =24
24
, B =24
, C =42
24
, B =24
, C =24
SyntaxError: Identifier 'x' has already been declared
While you’re thinking about your answer, let’s take a closer look at what hoisting is. In short, it’s an implicit mechanism in JavaScript that moves variable declarations to the top of the function, but only those that use thevar
keyword. Keep in mind that it movesdeclarations rather than definitions.
In some programming languages, like C, all variables must be declared at the beginning of a function. Other languages, such as Pascal, go even further by dedicating a special section for variable declarations. JavaScript tried to mimic that.
Alright, ready? The correct answer is none of the above! It’ll print the following:
undefined
24
42
To clarify the reasoning behind these results, you can manually perform the hoisting that JavaScript would do in this situation:
varx=42;functioncall(){varx;console.log(x);// A = undefinedx=24;console.log(x);// B = 24}call();console.log(x);// C = 42
The global variable is temporarily masked by the local variable because the name lookup goes outward. The declaration ofx
inside the function is moved up. When a variable is declared but not initialized, it has theundefined
value.
Variables aren’t the only construct that is affected by hoisting. Normally, when you define a function in JavaScript, you can call it even before its definition:
call();// Prints "hello"functioncall(){console.log('hello');}
This won’t work in an interactive shell where individual pieces of code are evaluated immediately.
Now, when you declare a variable using thevar
keyword and assign a functionexpression to that variable, it’ll be hoisted:
call();// TypeError: call is not a functionvarcall=function(){console.log('hello');};
As a result, your variable will remainundefined
until you initialize it.
Function signatures don’t exist in JavaScript. Whichever formal parameters you declare, they have no impact on function invocation.
Specifically, you can pass any number of arguments to a function that doesn’t expect anything, and they’ll just be ignored:
>functioncurrentYear(){…returnnewDate().getFullYear();…}>currentYear(42,'foobar');2020
You can also refrain from passing arguments that are seemingly required:
>functiontruthy(expression){…return!!expression;…}>truthy();false
Formal parameters serve as a documentation and allow you to refer to arguments by name. Otherwise, they’re not needed. Within any function, you have access to a specialarguments
variable, which represents the actual parameters that were passed:
>functionsum(){…return[...arguments].reduce((a,x)=>a+x);…}>sum(1,2,3,4);10
arguments
is an array-like object that is iterable and has numeric indices, but unfortunately it doesn’t come with.forEach()
. To wrap it in an array, you can use the spread operator.
This used to be the only way of definingvariadic functions in JavaScript before the rest parameter in ES6.
JavaScript is a weakly typed programming language, which is manifested in its ability to cast incompatible types implicitly.
This can give false positives when you compare two values:
if('2'==2){// Evaluates to true
In general, you should prefer thestrict comparison operator (===
) to be safe:
>'2'===2;false>'2'!==2;true
This operator compares both the values and thetypes of their operands.
Python has a fewdata types to represent numbers:
int
float
complex
The previous Python generation also had thelong
type, which was eventually merged intoint
.
Other programming languages are even more generous, giving you fine-grained control over memory consumption, value range, floating-point precision, and the treatment ofsign.
JavaScript has just one numeric type: theNumber
, which corresponds to Python’sfloat
data type. Under the hood, it’s a 64-bit double-precision number that conforms to theIEEE 754 specification. This was simple and sufficient for early web development, but it can cause a few problems today.
Note: To get the integer part of a floating-point number in JavaScript, you can use the built-inparseInt()
.
First of all, it’s remarkablywasteful in most situations. If you were to represent pixels of a single FHD video frame with JavaScript’sNumber
, then you’d have to allocate about 50 MB of memory. In a programming language with support for a stream ofbytes, such as Python, you’d need a fraction of that amount of memory.
Secondly, floating-point numbers suffer from arounding error due to how they’re represented in computer memory. As such, they’re unsuitable for applications requiringhigh precision, such as monetary calculations:
>0.1+0.2;0.30000000000000004
They’reunsafe in representing very big and very small numbers:
>constx=Number.MAX_SAFE_INTEGER+1;>consty=Number.MAX_SAFE_INTEGER+2;>x===y;true
But that’s not the worst part. After all, computer memory is getting cheaper by the day, and there are ways to circumvent the rounding error.
When Node.js became popular, people started using it to write back-end applications. They needed a way of accessing the local file system. Some operating systems identify files by arbitrary integer numbers. Occasionally, these numbers wouldn’t have an exact representation in JavaScript, so youcouldn’t open the file, or you’d read some random file without knowing.
To address the problem of handlingbig numbers in JavaScript, there’s going to be another primitive type that can reliably represent integer numbers of any size. Some web browsers already support thisproposal:
>constx=BigInt(Number.MAX_SAFE_INTEGER)+1n;>consty=BigInt(Number.MAX_SAFE_INTEGER)+2n;>x===y;false
Since you can’t mix the newBigInt
data type with regular numbers, you have to either wrap them or use a special literal:
>typeof42;'number'>typeof42n;'bigint'>typeofBigInt(42);'bigint'
Apart from that, theBigInt
number will be compatible with two somewhat-related typed arrays for signed and unsigned integers:
BigInt64Array
BigUint64Array
While a regularBigInt
can store arbitrarily large numbers, the elements of these two arrays are limited to just 64 bits.
null
vsundefined
Programming languages provide ways to represent the absence of a value. Python hasNone
, Java hasnull
, and Pascal hasnil
, for example. In JavaScript, you get not onlynull
but alsoundefined
.
It may seem odd to have more than one way to represent missing values when one is already too much:
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. (…) This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
—Tony Hoare
The difference betweennull
andundefined
is quite subtle. Variables that are declared but uninitialized will implicitly get the value ofundefined
. Thenull
value, on the other hand, is never assigned automatically:
letx;// undefinedlety=null;
At any time, you can manually assign theundefined
value to a variable:
letz=undefined;
This distinction betweennull
andundefined
was often used to implement default function arguments before ES6. One of the possible implementations was this:
functionfn(required,optional){if(typeofoptional==='undefined'){optional='default';}// ...}
If—for whatever reason—you wanted to keep an empty value for the optional parameter, then you couldn’t passundefined
explicitly because it would get overwritten by the default value again.
To differentiate between these two scenarios, you would pass anull
value instead:
fn(42);// optional = "default"fn(42,undefined);// optional = "default"fn(42,null);// optional = null
Apart from having to deal withnull
andundefined
, you may sometimes experience aReferenceError
exception:
>foobar;ReferenceError:foobarisnotdefined
This indicates that you’re trying to refer to a variable that hasn’t been declared in the current scope, whereasundefined
means declared but uninitialized, andnull
means declared and initialized but with an empty value.
this
Methods in Python must declare a specialself
parameter unless they’restatic or class methods. The parameter holds a reference to a particular instance of the class. Its name can be anything because it’s always passed as the first positional argument.
In JavaScript, like in Java, you can take advantage of a specialthis
keyword, which corresponds to the current instance. But what doescurrent instance mean? It depends on how you invoke your function.
Recall the syntax for object literals:
>letjdoe={…name:'John Doe',…whoami:function(){…console.log(this);…}…};>jdoe.whoami();{name:"John Doe",whoami:ƒ}
Usingthis
in a function lets you refer to a particular object that owns that function without hard-coding a variable name. It doesn’t matter if the function is defined in place as an anonymous expression or if it’s a regular function like this one:
>functionwhoami(){…console.log(this);…}>letjdoe={name:'John Doe',whoami};>jdoe.whoami();{name:"John Doe",whoami:ƒ}
What matters is the object that you’re calling the function on:
>jdoe.whoami();{name:"John Doe",whoami:ƒ}>whoami();Window{…}
In the first line, you callwhoami()
through an attribute of thejdoe
object. The value ofthis
is the same as thejdoe
variable in that case. However, when you call that same function directly,this
becomes theglobal object instead.
Note: This rule doesn’t apply to a constructor function invoked with thenew
keyword. In such a case, the function’sthis
reference will point to the newly created object.
You can think of JavaScript functions as methods attached to the global object. In a web browser,window
is the global object, so in reality, the code snippet above is short for this:
>jdoe.whoami();{name:"John Doe",whoami:ƒ}>window.whoami();Window{…}
Do you see a pattern here?
By default, the value ofthis
inside a function depends on the object sitting in front of the dot operator. As long as you control how your function gets called, everything will be fine. It becomes a problem only when you don’t call the function yourself, which is a common case for callbacks.
Let’s define another object literal to demonstrate this:
constcollection={items:['apple','banana','orange'],type:'fruit',show:function(){this.items.forEach(function(item){console.log(`${item} is a${this.type}`);});}};
collection
is a collection of elements that have a common type. Currently,.show()
doesn’t work as expected because it doesn’t reveal an element type:
>collection.show();appleisaundefinedbananaisaundefinedorangeisaundefined
Althoughthis.items
correctly refers to the array of fruits, the callback function seems to have received a differentthis
reference. That’s because the callback isn’t tied to the object literal. It’s as if it were defined elsewhere:
functioncallback(item){console.log(`${item} is a${this.type}`);}constcollection={items:['apple','banana','orange'],type:'fruit',show:function(){this.items.forEach(callback);}};
The most straightforward way to work around this would be to replacethis
in the callback with a custom variable or a constant:
constcollection={items:['apple','banana','orange'],type:'fruit',show:function(){constthat=this;this.items.forEach(function(item){console.log(`${item} is a${that.type}`);});}};
You persist the value ofthis
in a local constant, which the callback then refers to. Since the callback is defined as an inner function, it can access variables and constants from the outer scope. This wouldn’t have been possible with a stand-alone function.
Now the result is correct:
>collection.show();appleisafruitbananaisafruitorangeisafruit
This pattern is so common that.forEach()
accepts an optional parameter for substitutingthis
in the callback:
constcollection={items:['apple','banana','orange'],type:'fruit',show:function(){this.items.forEach(function(item){console.log(`${item} is a${this.type}`);},this);}};collection.show();
This is more elegant than the custom hack that you saw before, and it’s also more universal because it lets you pass a regular function. While not every built-in method in JavaScript is so gracious, there are three more ways to tinker with thethis
reference:
.apply()
.bind()
.call()
These are methods available on function objects. With.apply()
and.call()
, you can invoke a function while injecting arbitrary context to it. They both work the same way but pass arguments using different syntaxes:
>functionwhoami(x,y){…console.log(this,x,y);…}>letjdoe={name:'John Doe'};>whoami.apply(jdoe,[1,2]);{name:"John Doe"}12>whoami.call(jdoe,1,2);{name:"John Doe"}12
Of the three,.bind()
is the most powerful because it allows you to permanently change the value ofthis
for future invocations. It works a bit differently as it returns a new function that is bound to the given context:
>constnewFunction=whoami.bind(jdoe);>newFunction(1,2);{name:"John Doe"}12>newFunction(3,4);{name:"John Doe"}34
This can be useful in solving the earlier problem of the unbound callback:
constcollection={items:['apple','banana','orange'],type:'fruit',show:function(){this.items.forEach(callback.bind(this));}};
Nothing that you’ve just read aboutthis
in JavaScript applies to arrow functions in ES6. That’s good, by the way, because it removes a lot of ambiguity. There’s no context binding in arrow functions, nor is there an implicitthis
reference available. Instead,this
is treated as an ordinary variable and is subject to lexical scoping rules.
Let’s rewrite one of the previous examples using arrow functions:
>constcollection={…items:['apple','banana','orange'],…type:'fruit',…show:()=>{…this.items.forEach((item)=>{…console.log(`${item} is a${this.type}`);…});…}…};>collection.show();TypeError:Cannotreadproperty'forEach'ofundefined
If you trade both functions for their arrow counterparts, then you’ll get an exception because there’s nothis
variable in the scope anymore. You might keep the outer function so that itsthis
reference can be picked up by the callback:
constcollection={items:['apple','banana','orange'],type:'fruit',show:function(){this.items.forEach((item)=>{console.log(`${item} is a${this.type}`);});}};collection.show();
As you can see, arrow functions aren’t a complete replacement for traditional functions.
This section was merely the tip of the iceberg. To find out more about quirky JavaScript behaviors, take a look attricky code examples, which are also available as an installable Node.js module. Another great source of wisdom is Douglas Crockford’s book#"#whats-next" title="Permanent link">
As a Pythonista, you know that mastering a programming language and its ecosystem is only the beginning of your path to success. There are more abstract concepts to grasp along the way.
If you’re planning to do any sort of client-side development, then you can’t escape getting familiar with theDOM.
Note: You might have used the same DOM interface before to handle XML documents in Python.
To allow formanipulating HTML documents in JavaScript, web browsers expose a standard interface called the DOM, which comprises various objects and methods. When a page loads, your script can gain access to the internal representation of the document through a predefineddocument
instance:
constbody=document.body;
It’s a global variable available to you anywhere in your code.
Every document is a tree of elements. Totraverse this hierarchy, you can start at the root and use the following attributes to move in different directions:
.parentElement
.previousElementSibling
.nextElementSibling
.children
,.firstElementChild
,.lastElementChild
These attributes are conveniently available on all elements in the DOM tree, which would be perfect forrecursive traversal:
consthtml=document.firstElementChild;constbody=html.lastElementChild;constelement=body.children[2].nextElementSibling;
Most attributes will benull
if they don’t lead to an element in the tree. The only exception is the.children
property, which always returns an array-like object that can be empty.
Frequently, you won’t know where an element is. Thedocument
object, as well as every other element in the tree, has a few methods forelement lookup. You can search elements by tag name, ID attribute, CSS class name, or even using a complexCSS selector.
You can look for one element at a time or multiple elements at once. For example, to match elements against a CSS selector, you’d call one of these two methods:
.querySelector(selector)
.querySelectorAll(selector)
The first one returns the first occurrence of the matching element ornull
, while the second method always returns an array-like object with all the matching elements. Calling these methods on thedocument
object will cause the entire document to be searched. You can restrict the scope of the search by calling the same methods on a previously found element:
constdiv=document.querySelector('div');// The 1st div in the whole documentdiv.querySelectorAll('p');// All paragraphs inside that div
Once you have a reference to an HTML element, you can do a bunch of things with it, such as:
You can alsocreate new elements and add them to the DOM tree:
constparent=document.querySelector('.content');constchild=document.createElement('div');parent.appendChild(child);
The most challenging part about using DOM is getting skilled at building accurate CSS selectors. You can practice and learn using one of manyinteractive playgrounds available online.
The DOM interface is a set of primitive building blocks for creating interactive user interfaces. It gets the job done, but as your client-side code grows, it becomes increasingly difficult to maintain. The business logic and the presentation layer start to intersect, violating theseparation of concerns principle, while code duplication piles up.
Web browsers add fuel to the fire by not having a unified interface across the board. Sometimes, a feature that youseek to use isn’t available on all major browsers or is implemented in different ways. To ensure consistent behavior, you need to include an appropriateboilerplate code that hides the implementation details.
To deal with these problems, people started sharing JavaScriptlibraries that encapsulated common patterns and made the browser API a little less jarring. The most popular library of all time by far isjQuery, which until recently dwarfed today’s front-endframeworks in popularity:
Although this isn’t a completely honest apples-and-oranges comparison, it shows just how popular jQuery used to be. In peak time, it had more hits than all the other major frameworks and libraries combined. Despite being slightly old and unfashionable, it’s still used in a lot of legacy projects and sometimes even in recent ones.
Note: A library contains low-level utility functions that you can call, whereas a framework has total control over your code life cycle. A framework also imposes a certain application structure and is heavyweight in comparison with a library.
jQuery has good documentation and a minimal API as there’s only one function to remember, the versatile$()
. You can use that function to create and search for elements, change their style, handle events, and much more.
Modern web browsers aremuch better in terms of consistency and support for emerging web standards. So much so, in fact, that some people choose to develop client-side code invanilla JavaScript without the help of any front-end framework.
So why do most people tend to use a front-end framework, anyway?
There are pros and cons to using frameworks, but the biggest advantage seems to be that they allow you to operate on a higher level of abstraction. Instead of thinking in terms of document elements, you can build self-contained and reusablecomponents, which increases your productivity as a programmer.
That, combined with declarativestate management provided by the framework, allows you to tackle the complexity of a highly interactive client-side JavaScript application. If you insist on not using a JavaScript framework for long enough, then you typically end up unwittingly building your own.
As for which popular framework to choose, that depends on your goal. To stay relevant, you should invest time into learning pure JavaScript first since front-end frameworks come and go notoriously fast.
If you’re looking to land a job as a front-end developer or even as a full-stack engineer, then you should take a look at the job descriptions. Chances are they’ll expect someone with experience in one of these frameworks:
At the time of writing this article, these were arguably the most popular JavaScript frameworks.
In this tutorial, you learned about JavaScript’sorigins, its alternatives, and where the language is headed. You compared Python vs JavaScript by taking a closer look at their similarities and differences in syntax, runtime environments, associated tools, and jargon. Finally, you learned how to avoid fatalmistakes in JavaScript.
You’re now able to:
Try usingNode.js for your next scripting project or building an interactive client-side application in the web browser. When you combine what you’ve just learned with your existing Python knowledge, the sky’s the limit.
In this tutorial, you’ve learned how JavaScript and Python differ, and you’ve dipped a toe into programming with JavaScript. If you’d like to continue your JavaScript journey, perhaps you’d like to learn a bit more. Below, you can check out some related questions, as well as some resources for further study:
It boils down to personal preference and experience. Most programming languages share the same fundamental concepts, so once you know one language like Python, then learning another one will become easier. Ultimately, you can break down any computer program to a sequence of instructions, conditionals, and loops. Different languages may use alternative syntax, but they express the same underlying idea or algorithm.
If you’re deciding on your first programming language to learn, then it doesn’t really matter which one you choose. At this level of your education, it’s more important to just pick one and focus on learning the universal fundamentals of programming. However, if you know that you’re drawn more toward front-end programming, then learning JavaScript first might set you on the right path.
JavaScript is the language native to web browsers, so you’ll need to learn it at some point if you’re serious about web programming. It lets you interact with the browser and create dynamic content for your web applications. JavaScript is also a popular choice for building mobile or desktop apps.
Becoming acquinated with JavaScript is one of many stepping stones toward becoming a successful web developer. In addition to learning JavaScript, you should at least know about HTML, CSS, the HTTP protocol, browser policies and their differences, JavaScript frameworks, and the entire ecosystem of tools around it.
There are many excellent resources for learning JavaScript, including paid and free options, which you can find online.Mozilla Developer Network (MDN) Web Docs is one of the most comprehensive and up-to-date knowledgebases for JavaScript, featuring tutorials and reference materials for people at different skill levels.
You’ll also find free e-books, such asEloquent JavaScript by Marijn Haverbeke,Speaking JavaScript by Dr. Axel Rauschmayer, or theYou Don’t Know JS Yet book series by Kyle Simpson.
To put your newfound knowledge to the test, start by rewriting one of your existingPython scripts into JavaScript, as this will help you focus on the language differences without thinking about implementation details.
If you have a web project with a Python back end, such as aREST API implemented inFlask orDjango, then you may alsobuild a JavaScript front end for it. Remember, practice is the key to mastering any programming language.
Good luck and happy coding!
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Python vs JavaScript for Python Developers
🐍 Python Tricks 💌
Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.
AboutBartosz Zaczyński
Bartosz is an experienced software engineer and Python educator with an M.Sc. in Applied Computer Science.
» More about BartoszMasterReal-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
MasterReal-World Python Skills
With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
What Do You Think?
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.
Keep Learning
Already have an account?Sign-In
Almost there! Complete this form and click the button below to gain instant access:
5 Thoughts On Python Mastery