{"id":521408,"date":"2024-10-28T13:35:46","date_gmt":"2024-10-28T12:35:46","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=webstorm&#038;p=521408"},"modified":"2025-04-17T08:34:34","modified_gmt":"2025-04-17T07:34:34","slug":"javascript-best-practices-2024","status":"publish","type":"webstorm","link":"https:\/\/blog.jetbrains.com\/zh-hans\/webstorm\/2024\/10\/javascript-best-practices-2024","title":{"rendered":"JavaScript Best Practices"},"content":{"rendered":"\n<p>JavaScript is undoubtedly <a href=\"https:\/\/www.jetbrains.com\/lp\/devecosystem-2023\/languages\/\" target=\"_blank\" rel=\"noopener\">the most used programming language<\/a> in the world and is hugely influential on one of the largest technologies we have all come to rely on \u2013 the internet. With this power comes great responsibility, and the JavaScript ecosystem has been rapidly evolving, making it incredibly hard to keep up with the latest JavaScript best practices.<br><br>In this blog post, we will cover several key best practices in modern JavaScript for cleaner, more maintainable, and more performant code.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Project-defined practices trump all other practices<\/h2>\n\n\n\n<p>The project where you write code can have its own strict rules. Project rules are more relevant than any suggestion from any best practice article \u2013 this one included! If you would like to use a specific practice, make sure to sync it with the project rules and codebase and that everyone on your team is also on board.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Use up-to-date JavaScript<\/h2>\n\n\n\n<p>JavaScript was invented on December 4, 1995. Since that time, it has been almost endlessly evolving. On the internet, you can find a lot of outdated suggestions and practices. Be careful and verify if a practice that you would like to use is up to date.<\/p>\n\n\n\n<p>Also, be careful when using the very latest JavaScript features. It is better to start using new JavaScript features that have been through at least <a href=\"https:\/\/tc39.es\/process-document\/\" target=\"_blank\" rel=\"noopener\">Ecma TC39 Stage 3<\/a>.<\/p>\n\n\n\n<p>That said, here is a compilation of some of the current common best practices for JavaScript all in one place:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Declaring variables&nbsp;<\/h3>\n\n\n\n<p>You may encounter code that uses many var declarations. This may be on purpose, but if it is old code, it could be because this was the old approach.<\/p>\n\n\n\n<p><strong>Advice:<\/strong> Use <code>let<\/code> and <code>const<\/code> instead of <code>var<\/code> to declare your variables. An IDE like <a href=\"https:\/\/www.jetbrains.com\/webstorm\/\" target=\"_blank\" rel=\"noopener\">WebStorm<\/a> can assist you with adopting modern JavaScript practices by highlighting outdated patterns, helping you ensure you are using the latest practices in your code.&nbsp;<\/p>\n\n\n\n<p><br><strong>Why it matters:<\/strong> Although <code>var<\/code> is still available, <code>let<\/code> and <code>const<\/code> provide <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Statements\/block#description\" target=\"_blank\" rel=\"noopener\">block-scoping<\/a>, which is more predictable and reduces unexpected errors that can happen when declaring variables with <code>var<\/code>, which is function-scoped.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">for (let j = 1; j &lt; 5; j++) {\n  console.log(j);\n}\nconsole.log(j);\n\/\/ you get 'Uncaught ReferenceError: j is not defined'\n\n\/\/If we did this using var:\nfor (var j = 1; j &lt; 5; j++) {\n  console.log(j);\n}\n\/\/ &lt;-- logs the numbers 1 to 4\nconsole.log(j);\n\/\/You\u2019d get 5 as it still exists outside the loop<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Classes instead of <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Function\/prototype\" target=\"_blank\" rel=\"noopener\">Function: prototype<\/a><\/h3>\n\n\n\n<p>In many old codebases or articles about OOP in JavaScript, you may run into the function prototype approach for emulation of classes. For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">function Person(name) {\n  this.name = name;\n}\nPerson.prototype.getName = function () {\n  return this.name;\n}\n\nconst p = new Person('A');\nconsole.log(p.getName()); \/\/ 'A'\n<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> This approach uses <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Inheritance_and_the_prototype_chain#constructors\" target=\"_blank\" rel=\"noopener\">constructors to control the prototype chain<\/a>. However, in such cases, using <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Classes\" target=\"_blank\" rel=\"noopener\">classes<\/a> is almost always better.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Person {\n  constructor(name) {\n    this.name = name;\n  }\n  getName() {\n    return this.name;\n  }\n}\n\nconst p = new Person('A');\nconsole.log(p.getName()); \/\/ 'A'\n<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> The main reason to use classes is that they have much cleaner syntax.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Private class fields<\/h3>\n\n\n\n<p>In older JavaScript code, it was common to use an underscore (<code>_<\/code>) as a convention to denote private properties or methods in classes. However, this doesn&#8217;t actually enforce privacy \u2013 it just serves as a signal to developers that something is meant to be private.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Person {\n  constructor(name) {\n    this._name = name; \/\/ Conventionally treated as private, but not truly private\n  }\n\n\n  getName() {\n    return this._name;\n  }\n}\n\nconst p = new Person('A');\nconsole.log(p.getName()); \/\/ 'A'\nconsole.log(p._name); \/\/ 'A' (still accessible from outside)<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> When you really need private fields in classes, JavaScript now has real private fields using the <code>#<\/code> syntax. This is an official language feature that enforces true privacy.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Person {\n  #name\n  constructor(name) {\n    this.#name = name;\n  }\n  getName() {\n    return this.#name\n  }\n}\nconst p = new Person('A');\nconsole.log(p.getName()); \/\/ 'A'<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> Using real private fields ensures that the data is truly encapsulated, preventing accidental or malicious access from outside the class. The underscore convention only provides a visual cue and can easily be misused, while <code>#<\/code> private fields guarantee privacy by design. This results in more robust and maintainable code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Arrow function expressions<\/h2>\n\n\n\n<p>Arrow functions are often used to make callback functions or anonymous functions more concise and readable. They are especially useful when working with higher-order functions like <code>map<\/code>, <code>filter<\/code>, or <code>reduce<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const numbers = [1, 2];\n\n\/\/ Using arrow function\nnumbers.map(num => num * 2);\n\n\/\/ Instead of\nnumbers.map(function (num) {\n  return num * 2;\n});\n<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> Arrow functions provide a more concise syntax, especially when the function body is a single expression. They also automatically bind the <code>this<\/code> context, which can be particularly helpful in class methods where this can easily get lost.<\/p>\n\n\n\n<p>Consider this example with a class:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Person {\n  name = 'A';\n\n  \/\/ Arrow function retains the 'this' context\n  getName = () => this.name;\n}\n\nconst getName = new Person().getName;\nconsole.log(getName()); \/\/ 'A'\n<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> Arrow functions enhance readability by removing boilerplate code, making callback functions and inline expressions much more concise. In addition, they are particularly valuable when working with classes or event handlers, as they automatically bind <code>this<\/code> to the surrounding lexical scope. This avoids common bugs related to <code>this<\/code> in traditional function expressions, especially in asynchronous or callback-heavy code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Nullish coalescing (??)<\/h2>\n\n\n\n<p>In JavaScript, developers have often used the logical OR (<code>||<\/code>) operator to assign default values when a variable is <code>undefined<\/code> or <code>null<\/code>. However, this can behave unexpectedly when the variable holds values like <code>0<\/code>, <code>false<\/code>, or an empty string (<code>\"\"<\/code>), because <code>||<\/code> treats them as falsy and substitutes the default value.<\/p>\n\n\n\n<p>For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const value = 0;\nconst result = value || 10;\nconsole.log(result); \/\/ 10 (unexpected if 0 is a valid value)<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> Use the nullish coalescing operator (<code>??<\/code>) instead of <code>||<\/code> when resolving default values. It only checks for <code>null<\/code> or <code>undefined<\/code>, leaving other falsy values (like <code>0<\/code>, <code>false<\/code>, <code>\"\"<\/code>) intact.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const value = 0;\nconst result = value ?? 10;\nconsole.log(result); \/\/ 0 (expected behavior)<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> The <code>??<\/code> operator provides a more precise way of handling default values in cases where <code>null<\/code> or <code>undefined<\/code> should trigger the fallback. It prevents errors caused by using <code>||<\/code>, which may unintentionally override valid falsy values. Using nullish coalescing results in more predictable behavior, improving both code clarity and reliability.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Optional chaining (<code>?.<\/code>):<\/h2>\n\n\n\n<p>When dealing with deeply nested objects or arrays, it&#8217;s common to have to check whether each property or array element exists before trying to access the next level. Without optional chaining, this requires verbose and repetitive code.<\/p>\n\n\n\n<p>For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const product = {};\n\n\/\/ Without optional chaining\nconst tax = (product.price &amp;&amp; product.price.tax) ?? undefined;<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> The optional chaining operator (?.) simplifies this process by automatically checking if a property or method exists before attempting to access it. If any part of the chain is null or undefined, it will return undefined rather than throwing an error.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const product = {};\n\n\/\/ Using optional chaining\nconst tax = product?.price?.tax;<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> Optional chaining reduces the amount of boilerplate code and makes it easier to work with deeply nested structures. It ensures your code is cleaner and less error-prone by handling <code>null<\/code> or <code>undefined<\/code> values gracefully, without the need for multiple checks. This leads to more readable and maintainable code, especially when working with dynamic data or complex objects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><code>async\/await<\/code><\/h2>\n\n\n\n<p>In older JavaScript, handling asynchronous operations often relied on callbacks or chaining promises, which could quickly lead to complex, hard-to-read code. For example, using <code>.then()<\/code> for promise chaining could make the flow harder to follow, especially with multiple asynchronous operations:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">function fetchData() {\n  return fetch('https:\/\/api.example.com\/data')\n    .then(response => response.json())\n    .then(data => {\n      console.log(data);\n    })\n    .catch(error => {\n      console.error(error);\n    });\n}<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> Use <code>async<\/code> and <code>await<\/code> to make your asynchronous code look more like regular, synchronous code. This improves readability and makes error handling easier with <code>try...catch<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">async function fetchData() {\n  try {\n    const response = await fetch('https:\/\/api.example.com\/data');\n    const data = await response.json();\n    console.log(data);\n  } catch (error) {\n    console.error(error);\n  }\n}\n<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> The <code>async\/await<\/code> syntax simplifies working with asynchronous operations by removing the need for chaining <code>.then()<\/code> and <code>.catch()<\/code>. It makes your code more readable, more maintainable, and easier to follow, especially when dealing with multiple async calls. Error handling is also more straightforward with <code>try...catch<\/code>, leading to cleaner and more predictable logic.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Interaction with object keys and values<\/h2>\n\n\n\n<p>In older JavaScript code, interacting with the keys and values of an object often involved manual looping with <code>for...in<\/code> or <code>Object.keys()<\/code>, followed by accessing values through bracket notation or dot notation. This can lead to verbose and less intuitive code.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const obj = { a: 1, b: 2, c: 3 };\n\n\/\/ Older approach with Object.keys()\nObject.keys(obj).forEach(key => {\n  console.log(key, obj[key]);\n});<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> Use modern methods such as <code>Object.entries()<\/code>, <code>Object.values()<\/code>, and <code>Object.keys()<\/code> for working with object keys and values. These methods simplify the process and return useful structures like arrays, making your code more concise and easier to work with.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const obj = { a: 1, b: 2, c: 3 };\n\n\/\/ Using Object.entries() to iterate over key-value pairs\nObject.entries(obj).forEach(([key, value]) => {\n  console.log(key, value);\n});\n\n\/\/ Using Object.values() to work directly with values\nObject.values(obj).forEach(value => {\n  console.log(value);\n});\n<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> Using modern object methods such as <code>Object.entries()<\/code>, <code>Object.values()<\/code>, and <code>Object.keys()<\/code> results in cleaner, more readable code. These methods reduce the amount of boilerplate needed for iterating over objects and improve code clarity, especially when dealing with complex or dynamic data structures. They also support easier transformations of objects into other forms (e.g. arrays), making data manipulation more flexible and efficient.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Check the array type of a variable<\/h2>\n\n\n\n<p>In the past, developers used various non-straightforward methods to check if a variable was an array. These included approaches like checking the constructor or using <code>instanceof<\/code>, but they were often unreliable, especially when dealing with different execution contexts (like <code>iframes<\/code>).<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const arr = [1, 2, 3];\n\/\/ Older approach\nconsole.log(arr instanceof Array); \/\/ true, but not always reliable across different contexts<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> Use the modern <code>Array.isArray()<\/code> method, which provides a simple and reliable way to check whether a variable is an array. This method works consistently across different environments and execution contexts.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const arr = [1, 2, 3];\nconsole.log(Array.isArray(arr)); \/\/ true<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> <code>Array.isArray()<\/code> is a clear, readable, and reliable way to check for arrays. It eliminates the need for verbose or error-prone methods like <code>instanceof<\/code>, ensuring your code handles array detection correctly, even in complex or cross-environment scenarios. This leads to fewer bugs and more predictable behavior when working with different types of data structures.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><code>Map<\/code><\/h2>\n\n\n\n<p>In earlier JavaScript, developers often used plain objects to map keys to values. However, this approach has limitations, especially when keys are not strings or symbols. Plain objects can only use strings or symbols as keys, so if you need to map non-primitive objects (like arrays or other objects) to values, it becomes cumbersome and error-prone.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const obj = {};\nconst key = { id: 1 };\n\n\/\/ Trying to use a non-primitive object as a key\nobj[key] = 'value';\nconsole.log(obj); \/\/ Object automatically converts key to a string: '[object Object]: value'<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> Use <code>Map<\/code> when you need to map non-primitive objects or when a more robust data structure is required. Unlike plain objects, <code>Map<\/code> allows any type of value \u2013 primitives and non-primitives alike \u2013 as keys.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const map = new Map();\nconst key = { id: 1 };\n\n\/\/ Using a non-primitive object as a key in a Map\nmap.set(key, 'value');\nconsole.log(map.get(key)); \/\/ 'value'<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> <code>Map<\/code> is a more flexible and predictable way of associating values with any kind of key, whether primitive or non-primitive. It preserves the type and order of keys, unlike plain objects, which convert keys to strings. This leads to more powerful and efficient handling of key-value pairs, especially when working with complex data or when you need fast lookups in larger collections.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Symbols for hidden values<\/h2>\n\n\n\n<p>In JavaScript, objects are typically used to store key-value pairs. However, when you need to add &#8220;hidden&#8221; or unique values to an object without risking name collisions with other properties, or you want to keep them somewhat private from external code, using <code>Symbol<\/code> can be very helpful. Symbols create unique keys that are not accessible via normal enumeration or accidental property lookup.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const obj = { name: 'Alice' };\n\nconst hiddenKey = Symbol('hidden');\nobj[hiddenKey] = 'Secret Value';\n\nconsole.log(obj.name); \/\/ 'Alice'\nconsole.log(obj[hiddenKey]); \/\/ 'Secret Value'\n<\/pre>\n\n\n\n<p><strong>Advice: <\/strong>Use <code>Symbol<\/code> when you want to add non-enumerable, hidden properties to an object. Symbols are not accessible during typical object operations like <code>for...in<\/code> loops or <code>Object.keys()<\/code>, making them perfect for internal or private data that shouldn\u2019t be exposed accidentally.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const obj = { name: 'Alice' };\n\nconst hiddenKey = Symbol('hidden');\nobj[hiddenKey] = 'Secret Value';\n\nconsole.log(Object.keys(obj)); \/\/ ['name'] (Symbol keys won't appear)\nconsole.log(Object.getOwnPropertySymbols(obj)); \/\/ [Symbol(hidden)] (accessible only if specifically retrieved)<\/pre>\n\n\n\n<p><strong>Why it matters<\/strong>: Symbols allow you to safely add unique and &#8220;hidden&#8221; properties to objects without worrying about key collisions or exposing internal details to other parts of the codebase. They can be especially useful in libraries or frameworks where you might need to store metadata or internal states without affecting or interfering with other properties. This ensures better encapsulation and reduces the risk of accidental overwrites or misuse.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Check <code>Intl<\/code> API before using extra formatting libraries<\/h2>\n\n\n\n<p>In the past, developers often relied on third-party libraries for tasks like formatting dates, numbers, and currencies to suit different locales. While these libraries provide powerful functionality, they add extra weight to your project and may duplicate features already built into JavaScript.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Using a library for currency formatting\nconst amount = 123456.78;\n\/\/ formatLibrary.formatCurrency(amount, 'USD');<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> Before reaching for an external library, consider using the built-in ECMAScript Internationalization API (<code>Intl<\/code>). It provides robust out-of-the-box functionality for formatting dates, numbers, currencies, and more based on locale. This can often cover most of your internationalization and localization needs without the extra overhead of third-party libraries.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const amount = 123456.78;\n\n\/\/ Using Intl.NumberFormat for currency formatting\nconst formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });\nconsole.log(formatter.format(amount)); \/\/ $123,456.78<\/pre>\n\n\n\n<p>You can also use it for dates:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const date = new Date();\nconst dateFormatter = new Intl.DateTimeFormat('en-GB', { year: 'numeric', month: 'long', day: 'numeric' });\nconsole.log(dateFormatter.format(date)); \/\/ \"15 October 2024\"<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> The <code>Intl<\/code> API provides native and highly optimized support for internationalization, making it unnecessary to import large libraries for simple formatting needs. By using built-in features, you can keep your project lightweight, reduce dependencies, and still offer comprehensive locale-based formatting solutions. This not only improves performance but also reduces the maintenance burden associated with third-party libraries.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common practices<\/h2>\n\n\n\n<p>Now, let\u2019s look at some common practices that should be best practices.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Use strict equality (<code>===<\/code>) if possible<\/h3>\n\n\n\n<p>One of the trickiest and most surprising behaviors in JavaScript comes from the loose equality operator (<code>==<\/code>). It performs type coercion, which means it tries to convert operands to the same type before comparing them. This can lead to strange and unexpected results, as demonstrated in the famous &#8220;WTFJS&#8221; cases from talks like Brian Leroux&#8217;s:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">console.log([] == ![]); \/\/ true (this is surprising!)<\/pre>\n\n\n\n<p>In this case, the loose equality operator (<code>==<\/code>) converts both sides in unexpected ways, leading to unintuitive results.<\/p>\n\n\n\n<p><strong>Advice:<\/strong> Whenever possible, use strict equality (<code>===<\/code>) instead of loose equality (<code>==<\/code>). Strict equality does not perform type coercion \u2013 it compares both value and type directly, which leads to more predictable and reliable behavior.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">console.log([] === ![]); \/\/ false (as expected)<\/pre>\n\n\n\n<p>Here\u2019s a more typical example to highlight the difference:<br><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Loose equality (==) performs type coercion\nconsole.log(0 == ''); \/\/ true\n\n\n\/\/ Strict equality (===) compares both value and type\nconsole.log(0 === ''); \/\/ false (as expected)<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> Using strict equality (<code>===<\/code>) helps avoid the unexpected behavior that comes with type coercion in JavaScript. It makes comparisons more predictable and reduces the risk of subtle bugs, especially when dealing with different data types like numbers, strings, or booleans. It\u2019s a good practice to default to <code>===<\/code> unless you have a specific reason to use loose equality and understand the implications.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Explicitly handle expressions in <code>if<\/code> statements:<\/h3>\n\n\n\n<p>In JavaScript, the if statement implicitly converts the result of the expression it evaluates into a &#8220;truthy&#8221; or &#8220;falsy&#8221; value. This means that values like <code>0<\/code>, <code>\"\"<\/code> (empty string), <code>null<\/code>, <code>undefined<\/code>, and <code>false<\/code> are all treated as falsy, while most other values (even things like <code>[]<\/code> or <code>{}<\/code>) are truthy. This implicit casting can sometimes lead to unexpected results if you\u2019re not careful.<\/p>\n\n\n\n<p>For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const value = 0;\n\nif (value) {\n  console.log('This will not run because 0 is falsy.');\n}<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> It\u2019s a good practice to make the conditions in if statements explicit, especially when the values you&#8217;re working with might not behave as expected in a truthy\/falsy evaluation. This makes the code more predictable and easier to understand.<\/p>\n\n\n\n<p>For instance, instead of relying on implicit type coercion:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const value = 0;\n\n\/\/ Implicit check (may behave unexpectedly for some values)\nif (value) {\n  console.log('This won\u2019t run');\n}<\/pre>\n\n\n\n<p>You can use explicit conditions:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Explicitly check for the type or value you expect\nif (value !== 0) {\n  console.log('This will run only if value is not 0.');\n}<\/pre>\n\n\n\n<p>Or, when checking for <code>null<\/code> or <code>undefined<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const name = null;\n\nif (name != null) { \/\/ Explicitly checking for null or undefined\n  console.log('Name is defined');\n} else {\n  console.log('Name is null or undefined');\n}<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> By explicitly defining the conditions in your <code>if<\/code> statements, you reduce the chances of unexpected behavior from JavaScript\u2019s automatic type coercion. This makes your code clearer and helps prevent bugs when working with potentially ambiguous values like <code>0<\/code>, <code>false<\/code>, <code>null<\/code>, or <code>\"\"<\/code>. It\u2019s a good practice to be explicit about what conditions you\u2019re checking for, especially in complex logic.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Don\u2019t use built-in <code>Number <\/code>for sensitive calculations<\/h3>\n\n\n\n<p>JavaScript&#8217;s built-in <code>Number<\/code> type is a floating-point number based on the IEEE 754 standard. While this is efficient for most purposes, it can lead to surprising inaccuracies, particularly with decimal arithmetic. This is not a problem specific to JavaScript, but it can cause serious issues when you&#8217;re working with sensitive data such as financial calculations.<\/p>\n\n\n\n<p>For example, you might encounter this famous floating-point problem:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">console.log(0.1 + 0.2); \/\/ 0.30000000000000004<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> When precision is critical \u2013 such as in financial calculations \u2013 avoid using the standard <code>Number<\/code> type for arithmetic. Instead, use specialized libraries like <code>decimal.js<\/code> or <code>big.js<\/code> that are designed to handle precise decimal calculations without floating-point errors.<\/p>\n\n\n\n<p>Here\u2019s how it works with a library like <code>decimal.js<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const Decimal = require('decimal.js');\n\nconst result = new Decimal(0.1).plus(0.2);\nconsole.log(result.toString()); \/\/ '0.3'<\/pre>\n\n\n\n<p>These libraries ensure that the calculations are precise and that rounding errors won\u2019t impact the result, making them ideal for sensitive tasks like handling money.<\/p>\n\n\n\n<p><strong>Why it matters:<\/strong> Inaccurate calculations can lead to serious issues when working with things like financial data, where even tiny discrepancies matter. JavaScript\u2019s floating-point math can produce unexpected results, and while improvements are being made to the language, for now, it&#8217;s best to rely on libraries like <code>decimal.js<\/code> or <code>big.js<\/code> to ensure precision. By using these libraries, you avoid common pitfalls and ensure that your calculations are accurate, trustworthy, and suitable for critical applications.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Be careful with JSON and big integers<\/h3>\n\n\n\n<p>JavaScript has limits when it comes to handling very large numbers. The maximum safe integer in JavaScript is <code>9007199254740991<\/code> (also known as <code>Number.MAX_SAFE_INTEGER<\/code>). Numbers larger than this may lose precision and produce incorrect results. This becomes a problem when working with APIs or systems outside of JavaScript, where big numbers \u2013 such as database <code>id<\/code> fields \u2013 can easily exceed JavaScript\u2019s safe range.<\/p>\n\n\n\n<p>For example, when parsing JSON with a large number:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">console.log(\n  JSON.parse('{\"id\": 9007199254740999}')\n); \n\/\/ Output: { id: 9007199254741000 } (Precision lost)<\/pre>\n\n\n\n<p><strong>Advice:<\/strong> To avoid this precision issue when dealing with large numbers from JSON data, you can use the <code>reviver<\/code> parameter of <code>JSON.parse()<\/code>. This allows you to manually handle specific values \u2013 like <code>id<\/code> fields \u2013 and preserve them in a safe format, such as strings.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">console.log(\n  JSON.parse(\n    '{\"id\": 9007199254740999}',\n    (key, value, ctx) => {\n      if (key === 'id') {\n        return ctx.source; \/\/ Preserve the original value as a string\n      }\n      return value;\n    }\n  )\n); \n\/\/ Output: { id: '9007199254740999' }<\/pre>\n\n\n\n<p><strong>Using BigInt:<\/strong> JavaScript introduced <code>BigInt<\/code> to safely work with numbers larger than <code>Number.MAX_SAFE_INTEGER<\/code>. However, BigInt cannot be directly serialized to JSON. If you attempt to stringify an object containing <code>BigInt<\/code>, you\u2019ll get a <code>TypeError<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const data = { id: 9007199254740999n };\n\ntry {\n  JSON.stringify(data);\n} catch (e) {\n  console.log(e.message); \/\/ 'Do not know how to serialize a BigInt'\n}<\/pre>\n\n\n\n<p>To handle this, use the replacer parameter in <code>JSON.stringify()<\/code> to convert <code>BigInt<\/code> values to strings:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const data = { id: 9007199254740999n };\n\n\nconsole.log(\n  JSON.stringify(data, (key, value) => {\n    if (typeof value === 'bigint') {\n      return value.toString() + 'n'; \/\/ Append 'n' to denote BigInt\n    }\n    return value;\n  })\n);\n\/\/ Output: {\"id\":\"9007199254740999n\"}<\/pre>\n\n\n\n<p><strong>\u26a0\ufe0f Important consideration:<\/strong> If you use these techniques for handling large integers with JSON, ensure that both the client and server sides of your application agree on how to serialize and deserialize the data. For example, if the server sends an <code>id<\/code> as a string or <code>BigInt<\/code> with a specific format, the client must be prepared to handle that format during deserialization.<\/p>\n\n\n\n<p><strong>Why it matters:<\/strong> JavaScript\u2019s number precision limits can lead to serious bugs when working with large numbers from external systems. By using techniques like <code>BigInt<\/code> and the <code>reviver\/replacer<\/code> parameters of <code>JSON.parse()<\/code> and <code>JSON.stringify()<\/code>, you can ensure that large integers are handled correctly, avoiding data corruption. This is especially important in cases where precision is crucial, such as dealing with large ids or financial transactions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Use JSDoc for helping code readers and editors<\/h3>\n\n\n\n<p>When working with JavaScript, functions and object signatures often lack documentation, making it harder for other developers (or even your future self) to understand what parameters and objects contain or how a function should be used. Without proper documentation, code can be ambiguous, especially if object structures aren\u2019t clear:<\/p>\n\n\n\n<p>For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const printFullUserName = user =>\n  \/\/ Does user have the `middleName` or `surName`?\n  `${user.firstName} ${user.lastName}`;<\/pre>\n\n\n\n<p>In this case, without any documentation, it\u2019s not immediately clear what properties the user object should have. Does <code>user <\/code>contain <code>middleName<\/code>? Should <code>surName<\/code> be used instead of <code>lastName<\/code>?<\/p>\n\n\n\n<p><strong>Advice:<\/strong> By using JSDoc, you can define the expected structure of objects, function parameters, and return types. This makes it easier for code readers to understand the function and also helps code editors provide better autocompletion, type checking, and tooltips.<\/p>\n\n\n\n<p>Here\u2019s how you can improve the previous example with JSDoc:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * @typedef {Object} User\n * @property {string} firstName\n * @property {string} [middleName]  \/\/ Optional property\n * @property {string} lastName\n *\/\n\n\n\/**\n * Prints the full name of a user.\n * @param {User} user - The user object containing name details.\n * @return {string} - The full name of the user.\n *\/\nconst printFullUserName = user =>\n  `${user.firstName} ${user.middleName ? user.middleName + ' ' : ''}${user.lastName}`;\n<\/pre>\n\n\n\n<p><strong>Why it matters:<\/strong> JSDoc improves the readability and maintainability of your code by clearly indicating what types of values are expected in functions or objects. It also enhances the developer experience by enabling autocompletion and type-checking in many editors and IDEs. This reduces the likelihood of bugs and makes it easier for new developers to onboard and understand the code.<\/p>\n\n\n\n<p>With JSDoc, editors can provide hints, autocompletion for object properties, and even warn developers when they misuse a function or provide the wrong parameter type, making your code both more understandable and robust.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Use tests<\/h2>\n\n\n\n<p>As your codebase grows, manually verifying that new changes don\u2019t break important functionality becomes time-consuming and error-prone. Automated testing helps ensure that your code works as expected and allows you to make changes with confidence.<\/p>\n\n\n\n<p>In the JavaScript ecosystem, there are many testing frameworks available, but as of Node.js version 20, you no longer need an external framework to start writing and running tests. Node.js now includes a built-in stable test runner.<\/p>\n\n\n\n<p>Here\u2019s a simple example using Node.js\u2019s built-in test runner:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import { test } from 'node:test';\nimport { equal } from 'node:assert';\n\n\n\/\/ A simple function to test\nconst sum = (a, b) => a + b;\n\n\/\/ Writing a test for the sum function\ntest('sum', () => {\n  equal(sum(1, 1), 2); \/\/ Should return true if 1 + 1 equals 2\n});\n<\/pre>\n\n\n\n<p>You can run this test with the following command:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">node --test<\/pre>\n\n\n\n<p>This built-in solution simplifies the process of writing and running tests in Node.js environments. You no longer need to configure or install additional tools like Jest or Mocha, though those options are still great for larger projects.<\/p>\n\n\n\n<p><strong>E2E testing in browsers:<\/strong> For end-to-end (E2E) testing in browsers, Playwright is an excellent tool that allows you to easily automate and test interactions within the browser. With Playwright, you can test user flows, simulate interactions across multiple browsers (such as Chrome, Firefox, and Safari), and ensure that your app behaves as expected from the user\u2019s perspective.<\/p>\n\n\n\n<p><strong>Other environments:<\/strong> Bun and Deno, two alternative JavaScript runtimes, also provide built-in test runners similar to Node.js, making it easy to write and run tests without extra setup.<\/p>\n\n\n\n<p><strong>Why it matters:<\/strong> Writing tests saves time in the long run by catching bugs early and reducing the need for manual testing after every change. It also gives you confidence that the new features or refactoring won\u2019t introduce regressions. The fact that modern runtimes like Node.js, Bun, and Deno include built-in test runners means you can start writing tests right away, with minimal setup. Testing tools like Playwright help ensure your application works seamlessly in real-world browser environments, adding an extra layer of assurance for critical user interactions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Final Thoughts<\/h2>\n\n\n\n<p>Though this may seem like a lot to take in. Hopefully, it has given you insight into some areas that you haven\u2019t otherwise considered and would like to implement into your JavaScript projects. Again, feel free to bookmark this and come back to it anytime you need to refer to it. JavaScript conventions are constantly changing and evolving, as are the frameworks. Keeping up with the latest tools and best practices will continuously improve and optimize your code, but it can be difficult to do. We\u2019d recommend following along with what is going on in the ECMAScript releases, as this often points to new conventions that are then generally adopted in the latest JavaScript code. <a href=\"https:\/\/github.com\/tc39\" target=\"_blank\" rel=\"noopener\">TC39<\/a> usually has proposals for the latest ECMAScript versions, which you can follow along with too.<\/p>\n\n\n\n<p>By embracing these modern JavaScript best practices, you\u2019re not just writing code that works \u2013 you\u2019re creating cleaner, more efficient, and more maintainable solutions. Whether it\u2019s using newer syntax like <code>async\/await<\/code>, avoiding pitfalls with floating-point numbers, or leveraging the powerful <code>Intl<\/code> API, these practices will help you stay up-to-date and confident in your codebase. As the JavaScript ecosystem continues to evolve, taking the time to adopt best practices now will save you from future headaches and set you up for long-term success.<\/p>\n\n\n<p>Want to put these best practices into action? An IDE like <a href=\"https:\/\/www.jetbrains.com\/webstorm\/\" target=\"_blank\" rel=\"noopener\">WebStorm<\/a> can make it easier to write clean and more performant code.<\/p>\n\n\n<p>That\u2019s it for today! We hope that this has been useful \u2013 the comments are open for questions, discussions, and sharing advice. Happy coding!<\/p>\n\n\n<p><em>The WebStorm team<\/em><\/p>","protected":false},"author":743,"featured_media":521758,"comment_status":"closed","ping_status":"closed","template":"","categories":[6711,601],"tags":[227,197,1013,3682],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/webstorm\/521408"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/webstorm"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/types\/webstorm"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/users\/743"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/comments?post=521408"}],"version-history":[{"count":10,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/webstorm\/521408\/revisions"}],"predecessor-version":[{"id":561894,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/webstorm\/521408\/revisions\/561894"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media\/521758"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media?parent=521408"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/categories?post=521408"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/tags?post=521408"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/cross-post-tag?post=521408"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}