Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Tuesday, 4 August 2026

Kubernetes Debugging Scenario: Node.JS CronJob dies with a V8 JavaScript heap OOM

Problem Scenario


A Node.js batch job running as a Kubernetes CronJob aborts with FATAL ERROR: Ineffective mark-compacts near heap limit at ~4 GB. No NODE_OPTIONS, no resources block. Each scheduled run leaves several failed pods behind.


Knowledge required to fix the problem (Q&A)


Detailed Q&A


1. Node.js / V8 memory model

Q: What does --max-old-space-size actually control, and what does it not control? 

It caps V8's old space — the long-lived generation of the JS heap. It does not cap new space (--max-semi-space-size), code space, large object space, or external/off-heap memory such as Buffer and ArrayBuffer allocations, native addon memory, thread-pool stacks, or glibc malloc arenas. So a process with a 6 GB old-space ceiling can easily have an RSS well above 6 GB.

V8 is Google's open source high-performance JavaScript and WebAssembly engine, written in C++. It is used in Chrome and in Node.js, among others.

--max-old-space-size sets the maximum memory limit (in megabytes) allocated to the Old Generation heap space inside V8, the JavaScript engine powering Node.js.

When V8 allocates memory for your application, it divides the JavaScript heap into distinct regions based on object lifecycle. This flag configures the largest region where long-lived objects reside.

What It Measures & Controls

--max-old-space-size explicitly caps memory allocated for:

  • Old Generation JavaScript Objects: Objects, arrays, functions, closures, and strings that have survived initial garbage collection cycles in the Young Generation space and were promoted to the Old Generation.
  • Old Pointer Space & Old Data Space: Regions holding objects that contain pointers to other objects and raw data (like numbers or unboxed scalars).

What It Does NOT Control

  • A common misconception is that --max-old-space-size caps the entire Resident Set Size (RSS) or system memory footprint of your Node.js process. It does not limit:
  • Node.js Buffers (ArrayBuffers): Since Node.js v8.0+, binary Buffer allocations use off-heap C++ memory backing stores (ArrayBuffer). While the JavaScript wrapper object lives on the V8 heap, the underlying raw bytes do not count toward the old space limit.
  • Native C++ Allocations: Memory used by native C++ add-ons, libuv threads, or external libraries compiled into Node.
  • Other V8 Heap Spaces:
    • New Space (Nursery/Young Generation): Where new allocations land (--max-semi-space-size).
    • Code Space: JIT-compiled bytecode and machine code.
    • Map/Cell Spaces: V8 internal hidden classes and metadata.
  • Call Stack Memory: Memory used by execution contexts and local variables on the stack.

Because of off-heap memory, a Node.js process with --max-old-space-size=2048 (2 GB) can easily consume 3 GB or more of total system RAM (RSS).

What Happens When the Limit Is Reached

  1. Aggressive Garbage Collection: As old space usage approaches the limit, V8 triggers blocking, high-overhead Mark-Sweep-Compact garbage collection cycles to reclaim dead objects.
  2. Process Crash: If V8 cannot free enough memory to fit the next allocation below the configured threshold, Node.js crashes with a fatal error:

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

Default Values & Usage

Default Behavior: In modern Node.js versions, V8 dynamically sets the limit based on total available system RAM—typically around 2 GB to 4 GB on 64-bit systems if unspecified.

Command Line Flag:

node --max-old-space-size=4096 app.js

Environment Variable:

export NODE_OPTIONS="--max-old-space-size=4096"

 

Q: Why did the process die at ~4064 MB when nobody configured a heap limit? 

V8 picks a default heap ceiling from the memory it believes is available, and on 64-bit builds that lands at roughly 4 GB. The Mark-Compact 4064.3 MB line in the log is the giveaway that it hit that default ceiling rather than any limit you set.


Q: Why doesn't Node just size its heap to the container's memory limit? 

Historically V8 read host RAM, not the cgroup limit, so a Node process in a 512 Mi container would happily set a multi-gigabyte heap and get OOM-killed. Newer Node versions do consult cgroup constraints, but the behaviour varies by version — which is why the defensive answer is always to set the flag explicitly rather than rely on auto-detection.


Q: The workload starts with npm run start. Does setting NODE_OPTIONS in the container env actually reach the Node process? 

Yes — NODE_OPTIONS is an environment variable, so it's inherited by every child process npm spawns. Two caveats worth naming: it also applies to the npm wrapper process itself (harmless, just an extra reservation), and not every V8 flag is permitted inside NODE_OPTIONS. The alternative is passing the flag in the npm script itself, which is more surgical but easier to lose.



2. Diagnosis: which kind of OOM is this?

Q: How do you tell a V8 heap OOM from a kernel OOMKill from a kubelet eviction?

Signal V8 heap OOM OOMKilled Evicted
Log line FATAL ERROR: Ineffective mark-compacts near heap limit none from the app — killed mid-flight none from the app
Signal / exit SIGABRT, exit 134 SIGKILL, exit 137 pod deleted
Pod status Error OOMKilled in lastState.terminated.reason Failed, reason Evicted
Fix direction raise heap ceiling or reduce allocation raise container limit set requests so you aren't the first target

The ticket's evidence — the mark-compact message plus signal SIGABRT — puts it firmly in column one. That matters because raising the container limit alone would have changed nothing: V8 would still have aborted at 4 GB.


Q: How would you size the flag rather than guessing? 

Instrument before you tune. --trace-gc shows the heap trajectory over the run; process.memoryUsage() sampled periodically distinguishes heapUsed from external; --heapsnapshot-near-heap-limit=1 writes a snapshot right before the abort that you can open in Chrome DevTools to find the retaining structure. That tells you whether the working set is genuinely ~6 GB or whether one unbounded array is the whole problem.

Q: Is raising the heap the right fix at all? 

Usually it's a mitigation, not a fix. A benchmark job whose memory scales with input size will hit any ceiling you pick — the durable fix is streaming, batching, or paginating so peak memory is bounded by chunk size rather than dataset size. Raising the flag is defensible as a stopgap; the honest version says so in the ticket and files the follow-up. Note that in this case the real numbers came out at 10 GB heap with a 5-hour runtime, which is a fairly loud hint that the algorithm is the underlying issue.


3. Kubernetes resource management

Q: What's the difference between a memory request and a memory limit? 

The request is what the scheduler reserves — it decides which node the pod fits on and is the baseline the kubelet uses when deciding who to evict. The limit is enforced at runtime by the cgroup; exceed it and the kernel OOM-kills the container. Memory, unlike CPU, is incompressible: there's no throttling, only killing.

Q: What QoS class does a pod with no resources block get, and why does that matter here? 

BestEffort — the first thing evicted under node memory pressure, and it contributes nothing to the scheduler's accounting so the node can be oversubscribed into pressure in the first place. Setting requests equal to limits gives Guaranteed; requests below limits gives Burstable.

Q: How do you choose the relationship between the heap flag and the container limit? 

Limit strictly above heap ceiling, with headroom for everything --max-old-space-size doesn't cover — off-heap buffers, native memory, the npm and node process overhead, plus GC working room. The ticket proposed 6 GB heap under a 7 Gi limit; what actually shipped was 10 GB heap under a 12 Gi limit. Too tight and you convert a clean SIGABRT into a much harder-to-debug OOMKill.

Q: What's the risk of setting limits.memory well above requests.memory? 

You're overcommitting the node. It schedules against the request but can consume up to the limit, so several such pods on one node can drive it into memory pressure and trigger evictions of unrelated workloads. Matching them costs you scheduling flexibility but makes the blast radius predictable.

Q: You set requests.memory: 8Gi and the pod never starts. What's your first check? 

Whether any node has 8 Gi of allocatable memory free — allocatable is capacity minus kube-reserved, system-reserved, and eviction thresholds. The pod sits Pending with an Insufficient memory scheduling event. Big-request batch jobs are a classic case for a dedicated or autoscaling node pool.


4. CronJobs and Jobs

Q: What produced seven Error pods from one scheduled run? 

backoffLimit retries on failure, and a deterministic OOM fails identically every time — so the Job burned through its retries producing one dead pod each. The fix applied was a podFailurePolicy that fails the Job on the application's exit code instead of retrying, plus ttlSecondsAfterFinished so finished Jobs get garbage-collected rather than accumulating.

Q: What does podFailurePolicy require to work? 

restartPolicy: Never on the pod template, and rules matching on either container exit codes (onExitCodes) or pod conditions (onPodConditions, e.g. DisruptionTarget). Actions are FailJob, Ignore, Count, and FailIndex. The point is distinguishing retryable infrastructure failures from deterministic application failures — retrying a heap OOM six times is pure waste.

Q: Which CronJob fields govern history and overlap? 

successfulJobsHistoryLimit / failedJobsHistoryLimit for retained Job objects, ttlSecondsAfterFinished on the Job for automatic cleanup, concurrencyPolicy (Allow / Forbid / Replace) for overlapping runs, startingDeadlineSeconds for missed schedules, and activeDeadlineSeconds as a wall-clock kill switch. For a job that runs five hours, concurrencyPolicy: Forbid deserves a hard look.

Q: The schedule is 30 10 */14 * *. Does that run every 14 days? 

No — and this is the trap. Step values in day-of-month are evaluated within each month, so it fires on the 1st, 15th, and 29th, then resets. The gap between the 29th and the following 1st is two or three days, not fourteen. Genuine "every N days" needs an external scheduler or a daily run that no-ops based on a stored timestamp.


5. Container memory accounting

Q: When you read a container's memory usage, what are you actually seeing? 

Under cgroup v2 the kubelet reports working set derived from memory.current minus inactive file cache; memory.max is the hard limit. Crucially memory.current includes page cache, so a process doing heavy file I/O can look alarming without any anonymous-memory problem. RSS is anonymous plus mapped pages for the process specifically, and glibc often doesn't return freed memory to the OS — so RSS is sticky and lags real usage downward.

Q: Why is container_memory_rss a poor alerting signal for some workloads? 

Because it only captures what lives in RSS. For a JVM or Node process the heap is anonymous memory and RSS tracks it reasonably; for something like Percona MongoDB, where WiredTiger's cache sits in the OS page cache rather than RSS, the metric is structurally blind to the thing you care about — you want cache fill percentage instead. Matching the metric to the workload's memory architecture is the actual skill.


6. Verification

Q: How do you prove the fix worked? 

Trigger a manual run (kubectl create job --from=cronjob/experience-benchmarks) and confirm the Job reaches Complete with no SIGABRT and no Error pods. Then compare peak usage against the limit — completing at 95% of the ceiling is luck, not a fix. Verification model: live CronJob spec matches main, last three runs all Complete, runtimes recorded, zero Error pods.

Q: How do you confirm what's actually running in prod matches what's in the repo? 

Diff the live object against the manifest — kubectl get cronjob experience-benchmarks -o yaml against deploy/prod.yml. Drift between a merged PR and the running cluster is exactly the kind of gap that lets a "fixed" ticket keep failing, and it's the check that would have surfaced the tickets overlap before any code was written.

Q: What should you have checked before writing a single line for this ticket? 

Whether the problem still existed. The ticket sat in Backlog for four days, a ticket shipped a superset of the fix during that window, and the work that followed would have lowered the heap from 10 GB to 6 GB — reintroducing the OOM. Reading main and the live spec before implementing is the cheapest step in the whole process and the one that was skipped.


Brief Q&A


V8 / Node

Q: What does --max-old-space-size cap? Only V8's old space. Not new space, code space, or off-heap memory (Buffer, ArrayBuffer, native addons). RSS can exceed it substantially.

Q: Why die at ~4 GB with no flag set? That's V8's default ceiling on 64-bit. Node has historically sized it from host RAM, not the cgroup limit — so always set it explicitly.

Q: Does NODE_OPTIONS reach a process started via npm run start? Yes, it's inherited by child processes. It also applies to the npm wrapper itself.

Diagnosis

Q: Distinguish the three OOM flavours. V8 heap OOM → mark-compact message, SIGABRT, exit 134, pod Error. Kernel kill → no app log, SIGKILL, exit 137, OOMKilled. Eviction → pod Failed, reason Evicted. Only the first is fixed by the heap flag.

Q: How do you size the flag instead of guessing? --trace-gc for the trajectory, process.memoryUsage() for heap vs. external, --heapsnapshot-near-heap-limit=1 for a snapshot at the abort.

Q: Is raising the heap the real fix? Usually a stopgap. If memory scales with input size, any ceiling eventually fails — stream or batch so peak is bounded by chunk size.

Kubernetes resources

Q: Request vs. limit? Request drives scheduling and eviction ranking; limit is cgroup-enforced. Memory is incompressible — no throttling, only killing.

Q: No resources block means what QoS? BestEffort — first evicted under node pressure, and invisible to scheduler accounting. Requests == limits gives Guaranteed.

Q: How do heap ceiling and container limit relate? Limit strictly above the heap, with headroom for off-heap and process overhead. Too tight converts a clean SIGABRT into a harder-to-debug OOMKill.

Q: Request set high and the pod won't schedule? Check node allocatable (capacity minus reserved and eviction thresholds). Expect Pending with Insufficient memory.

Jobs / CronJobs

Q: Why several failed pods per run? backoffLimit retries, and a deterministic OOM fails identically each time. Use podFailurePolicy with onExitCodes (requires restartPolicy: Never) to fail fast, plus ttlSecondsAfterFinished for cleanup.

Q: Does 30 10 */14 * * run every 14 days? No. Day-of-month steps reset monthly → the 1st, 15th, and 29th. True "every N days" needs external scheduling.

Verification

Q: How do you prove it's fixed? Trigger a manual run from the CronJob, confirm Complete with no failed pods, and compare peak usage to the limit — finishing at 95% of the ceiling is luck.

Q: What do you check before writing any code? That the problem still exists. Diff the live object against the repo manifest; a stale ticket can lead you to lower limits that a since-merged fix raised.

Friday, 10 July 2020

Using ESLint

ESLint is one of most popular linters for JavaScript and TypeScript (via typescript-eslint).




To use, we need to install it first. It can be installed locally (per project) or globally. To install it globally:

$ npm install -g eslint

We can verify that package is installed:

$ npm -g list  | grep eslint
├─┬ eslint@7.4.0
│ ├─┬ eslint-scope@5.1.0
│ ├─┬ eslint-utils@2.1.0
│ │ └── eslint-visitor-keys@1.3.0 deduped
│ ├── eslint-visitor-keys@1.3.0
│ │ └── eslint-visitor-keys@1.3.0 deduped

...and also check its command line args:

$ eslint --help
eslint [options] file.js [file.js] [dir]

Basic configuration:
  --no-eslintrc                   Disable use of configuration from .eslintrc.*
  -c, --config path::String       Use this configuration, overriding .eslintrc.* config options if present
  --env [String]                  Specify environments
  --ext [String]                  Specify JavaScript file extensions
  --global [String]               Define global variables
  --parser String                 Specify the parser to be used
  --parser-options Object         Specify parser options
  --resolve-plugins-relative-to path::String  A folder where plugins should be resolved from, CWD by default

Specifying rules and plugins:
  --rulesdir [path::String]       Use additional rules from this directory
  --plugin [String]               Specify plugins
  --rule Object                   Specify rules

Fixing problems:
  --fix                           Automatically fix problems
  --fix-dry-run                   Automatically fix problems without saving the changes to the file system
  --fix-type Array                Specify the types of fixes to apply (problem, suggestion, layout)

Ignoring files:
  --ignore-path path::String      Specify path of ignore file
  --no-ignore                     Disable use of ignore files and patterns
  --ignore-pattern [String]       Pattern of files to ignore (in addition to those in .eslintignore)

Using stdin:
  --stdin                         Lint code provided on <STDIN> - default: false
  --stdin-filename String         Specify filename to process STDIN as

Handling warnings:
  --quiet                         Report errors only - default: false
  --max-warnings Int              Number of warnings to trigger nonzero exit code - default: -1

Output:
  -o, --output-file path::String  Specify file to write report to
  -f, --format String             Use a specific output format - default: stylish
  --color, --no-color             Force enabling/disabling of color

Inline configuration comments:
  --no-inline-config              Prevent comments from changing config or rules
  --report-unused-disable-directives  Adds reported errors for unused eslint-disable directives

Caching:
  --cache                         Only check changed files - default: false
  --cache-file path::String       Path to the cache file. Deprecated: use --cache-location - default: .eslintcache
  --cache-location path::String   Path to the cache file or directory

Miscellaneous:
  --init                          Run config initialization wizard - default: false
  --env-info                      Output execution environment information - default: false
  --no-error-on-unmatched-pattern  Prevent errors when pattern is unmatched
  --debug                         Output debugging information
  -h, --help                      Show help
  -v, --version                   Output the version number
  --print-config path::String     Print the configuration for the given file




To initialise and configure ESLint launch configuration wizard:

$ eslint --init


ESLint configuration will be saved in file .eslintrc.json (if you opt json file to be used). Example content:

.eslintrc.json:

{
    "env": {
        "browser": true,
        "es2020": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 11
    },
    "rules": {
        "indent": [
            "error",
            4
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
}

If you use VS Code, you can install ESLint plugin which will pick up this configuration and automatically lint your code and show warnings and error son the go.

ESLint should be added to Node project as a dev dependency.

package.json (created by $npm init):

  "devDependencies": {
    "eslint": "^7.4.0"
  }


It is also possible to run this linter from the terminal. For example, to lint all files in the current project recursively with respect to .eslintrc.json:

$eslint .

...
  2:32009  error  Strings must use singlequote                                               quotes
  3:23     error  Strings must use singlequote                                               quotes
  3:42     error  Missing semicolon                                                          semi
  3:43     error  Strings must use singlequote                                               quotes
  3:70     error  Strings must use singlequote                                               quotes
  3:166    error  Strings must use singlequote                                               quotes
  3:207    error  Strings must use singlequote                                               quotes
  3:280    error  Strings must use singlequote                                               quotes
  3:338    error  Missing semicolon                                                          semi
  3:460    error  Missing semicolon                                                          semi
  3:555    error  Missing semicolon                                                          semi
...
  4:22578  error  Missing semicolon                                                          semi
  4:22601  error  Missing semicolon                                                          semi

✖ 11436 problems (11436 errors, 0 warnings)


To ignore rules in .eslintrc.json and run the linter only for JavaScript files and only to check one particular rule:

/my-project$ eslint . --ext .js --no-eslintrc --rule 'indent: ["error", 4, { "SwitchCase": 1 }]'

/my-project/path/to/file.js
   28:1  error  Expected indentation of 12 spaces but found 16  indent
   46:1  error  Expected indentation of 12 spaces but found 16  indent
  186:1  error  Expected indentation of 4 spaces but found 8    indent
  187:1  error  Expected indentation of 4 spaces but found 8    indent
  188:1  error  Expected indentation of 4 spaces but found 8    indent
  189:1  error  Expected indentation of 4 spaces but found 8    indent

Rules used here was indent.

References






Wednesday, 20 May 2020

Generator Functions in various Programming Languages

A generator is a special type of function which can pause its execution, yield result back to the caller and resume later, at caller’s convenience. And this happens as long as the generator has something to return, some value to yield back. Caller is usually iterating over yielded values in a loop, it takes a new value as soon as generator yields it. The main benefit of generators is that not all elements in a  sequence have to be kept in memory at the same time but only a single one. 



Generators in Python


Friday, 12 April 2019

Introduction to JavaScript

Here are some notes & links I used on my quest of learning JavaScript. You won't find anything spectacular here :)

JS in general


http://blog.thefirehoseproject.com/posts/exactly-makes-javascript-weird-programming-language/
https://www.crockford.com/javascript/javascript.html

Coding convention

http://www.crockford.com/javascript/code.html

When to use and when not semicolons?

https://stackoverflow.com/questions/444080/do-you-recommend-using-semicolons-after-every-statement-in-javascript
https://hackernoon.com/an-open-letter-to-javascript-leaders-regarding-no-semicolons-82cec422d67d
https://stackoverflow.com/questions/8528557/why-doesnt-a-javascript-return-statement-work-when-the-return-value-is-on-a-new

What are the rules for JavaScript's automatic semicolon insertion (ASI)?

https://stackoverflow.com/questions/2846283/what-are-the-rules-for-javascripts-automatic-semicolon-insertion-asi

Memory Management


Memory management model

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management

Causes of memory leaks

https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/
https://blog.sessionstack.com/how-javascript-works-memory-management-how-to-handle-4-common-memory-leaks-3f28b94cfbec

How to free up memory?

https://stackoverflow.com/questions/8467350/how-to-free-up-the-memory-in-javascript

JS in Front-End


What is DOM?

https://www.quora.com/What-is-meant-by-Document-Object-Model-and-how-does-it-works

Web page lifecycle

https://javascript.info/onload-ondomcontentloaded

How to check which events on the page/window have handlers attached?

https://github.com/deanoemcke/thegreatsuspender/issues/272
When does DOMContentLoaded fire? What does its handler usually do?

https://www.quora.com/What-does-it-mean-exactly-that-the-DOM-Document-Object-Model-is-ready
https://api.jquery.com/ready/
https://eager.io/blog/how-to-decide-when-your-code-should-run/

What is the vanilla JS equivalent of '$(document).ready()'?

https://stackoverflow.com/questions/2304941/what-is-the-non-jquery-equivalent-of-document-ready

When does window.onload fire? What does its handler usually do?

https://stackoverflow.com/questions/799981/document-ready-equivalent-without-jquery
https://api.jquery.com/ready/

How to prevent web page from closing?

https://www.maki-chan.de/preventclose.htm

window.onbeforeunload = function() { return "Would you really like to close your browser?"; }

Threading system


Is JS single or multi-threaded language and why?
How does event system work if it’s single-threaded?
Reentrancy in JavaScript


Statements 


for...in


  • for iterating over object properties

for (const prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    console.log(`obj.${prop} = ${obj[prop]}`);
  } 
}

for..in (MDN)


for...of

  • for iterating over arrays



Variables

Assigning and passing to functions by value and by reference

https://hackernoon.com/grasp-by-value-and-by-reference-in-javascript-7ed75efa1293

Is JavaScript a pass-by-reference or pass-by-value language?

It's always pass by value, but for objects the value of the variable is a reference. Because of this, when you pass an object and change its members, those changes persist outside of the function.

Objects

Built-in objects

undefined


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined

Error

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error


How to re-throw the error?


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
https://blog.joefallon.net/2018/09/typescript-try-catch-finally-and-custom-errors/.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
http://zduck.com/2013/non-enumerable-properties-in-javascript/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Data types


JavaScript is a weakly typed language, not un-typed language. Primitive types are passed into functions by value, while non-primitives are passed by reference. The typeof operator in JavaScript explicitly informs you of what the type of a variable is (hint is in the name of the operator). There are 5 primitive types (string, number, boolean, undefined, symbol) and two non-primitives (object, function) that are available in user land. [source]
Which 2 types of data types are defined by the latest ECMAScript?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures

List all primitives.


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures

null vs unsigned


https://codeburst.io/javascript-null-vs-undefined-20f955215a2

List all 6 falsy values in JS


  • false
  • 0 (zero)
  • “” (empty string)
  • null
  • undefined
  • NaN (Not A Number)


https://codeburst.io/javascript-null-vs-undefined-20f955215a2
JavaScript Showdown: == vs ===

Object


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object

What is an “object” in computer science?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures

Object​.assign()

What are the pros and cons of using object literal notation vs class notation? : javascript

Object literals vs constructors in JavaScript - Internal Pointers

Why Object Literals in JavaScript Are Cool

Object initializer - JavaScript | MDN


Object.hasOwnProperty()


Object.hasOwnProperty determines if the whole property is defined in the object itself or in the prototype chain.
Object.keys returns an array of string where its items are the own properties from a given object 
When you're using for (var key in obj) it will loop through the given object + its parent objects' properties on the prototype chain until it reaches the end of the chain. As you want to check only specific object's properties, you need to use hasOwnProperty.
This is not needed in for (var i = 0; i < length; i++) or data.forEach()


Inheritance and the prototype chain - JavaScript | MDN

Object.getPrototypeOf() - JavaScript | MDN

Why is JavaScript prototype property undefined on new objects? - Stack Overflow

How do I enumerate the properties of a JavaScript object? - Stack Overflow

object - Javascript what is property in hasOwnProperty? - Stack Overflow

 

Object.keys()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys


Computed property name syntax (ES6)

It's a shorthand for the someObject[someKey] assignment (from ES3/5):

var a = "b"
var c = {[a]: "d"}

is same as:

var a = "b"
var c = {}
c[a] = "d"

Square Brackets Javascript Object Key


Object.values()

What is the difference between Object.values() and for..in?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values


Remove a property in an object immutably

const {prop1, prop2, ...outputObject} = originalObject;

String


In JavaScript everything is an object (or may at least be treated as an object), except primitives (booleans, null, numbers, strings and the value undefined (and symbol in ES6)).

In JavaScript strings can be literals or objects.

Why does instanceof return false for some literals?

Primitives are a different kind of type than objects created from within Javascript.
Literal is a primitive => Literal is NOT an object!


template string


https://stackoverflow.com/questions/19105009/how-to-insert-variables-in-javascript-strings

How to make template string substitution in runtime?

https://stackoverflow.com/questions/30003353/can-es6-template-literals-be-substituted-at-runtime-or-reused
split()
https://medium.com/@jeanpan/javascript-splice-slice-split-745b1c1c05d2
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split

Array


How to find if array contains some element which matches some criteria?
https://www.w3schools.com/jsref/jsref_find.asp

jQuery - What happens if some operation is executed on the empty array?
e.g. what happens if find returns empty array in:
$footer_ip.find(".user--ip, .virtual--ip").addClass("animated fadeInUp");
https://stackoverflow.com/questions/5705067/what-happens-when-a-jquery-selector-wasnt-found

How to create an array?
https://stackoverflow.com/questions/1629539/use-of-square-brackets-around-javascript-variables

How to redefine Array.toString function?
https://stackoverflow.com/questions/1629539/use-of-square-brackets-around-javascript-variables

Simplify your JavaScript – Use .map(), .reduce(), and .filter()

.map()

  • takes 2 arguments:
    • a callback
    • an optional context (will be considered as this in the callback)
  • callback runs for each value in the array and returns each new value in the resulting array
  • resulting array will always be the same length as the original array
  • Whenever you see .forEach in your code, think about using .map


.reduce()

  • runs a callback for each element of an array
  • passes the result of this callback (the accumulator) from one array element to the other
  • accumulator can be pretty much anything (integer, string, object, etc.) and must be instantiated or passed when calling .reduce()
  • .reduce() is an easy way to generate a single value or object from an array

.filter()

  • if the callback function returns true, the current element will be in the resulting array. If it returns false, it won’t be.


How to remove an element from an array?
"delete" should not be used on arrays

How do I remove a property from a JavaScript object?
How to remove a property from a JavaScript object
3 Ways to clone objects in Javascript
Copying Objects in JavaScript
How to deep clone a JavaScript object
What is the most efficient way to deep clone an object in JavaScript?
How do I correctly clone a JavaScript object?
Clone a js object except for one key
How to handle immutability in JavaScript

Its Lodash features that JS doesn't have (or sometimes needs polyfills for), that you should definitely use when needed. Examples of that are object deep clone and object difference - two of the most important Lodash functionalities for me, personally. [ Lodash and usefulness of utility libraries ]



Hoisting


https://www.sitepoint.com/back-to-basics-javascript-hoisting/

Expressions and operators


Loose equality operator (==)

It tests for strict equality between two values. Both the type and the value you’re comparing have to be exactly the same.

https://codeburst.io/javascript-null-vs-undefined-20f955215a2

Double Equals(==) vs. Triple Equals(===)

Triple equals tests for loose equality and performs type coercion.

https://codeburst.io/javascript-double-equals-vs-triple-equals-61d4ce5a121a
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators
When is it OK to use == in JavaScript?

Destructuring assignment


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

[a, b, ...rest] = [10, 20, 30, 40, 50];


Spread syntax

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
https://zendev.com/2018/05/09/understanding-spread-operator-in-javascript.html
https://stackoverflow.com/questions/11704267/in-javascript-how-to-conditionally-add-a-member-to-an-object


Short-circuiting

https://codeburst.io/javascript-what-is-short-circuit-evaluation-ff22b2f5608c


Null Coalescing Operator

let a = b || c;

Regardless of the type of the first operand, if casting it to a Boolean results in false (like for null, undefined, 0, ""...but not for "false", "undefined", "null", "0", "empty", "deleted"... as they are all true since they are non-empty strings.), the assignment will use the second operand.

Is there a “null coalescing” operator in JavaScript?


The || operator in JavaScript doesn't necessarily return true or false. It's exact behavior is this:
If the first operand is truthy, it evaluates to the first operand. Otherwise, it evaluates to the second.

Why does (false || null) return null, while (null || false) returns false?



Functions

Built-in (global) functions


parseInt - what is the purpose of radix argument?
https://www.w3schools.com/jsref/jsref_parseint.asp
https://davidwalsh.name/parseint-radix
https://stackoverflow.com/questions/6611824/why-do-we-need-to-use-radix
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
https://eloquentjavascript.net/03_functions.html

How to verify that parsing some string as integer was successful?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt

Function.prototype.bind()


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Arrow functions

Arrow functions which return a single value don't need braces or parenthesis at all:

The exception is the case when it returns an empty object:

const f = () => {}; 
// f returns an empty object


This is same as:

const f = () => ({})

or

const f = () => { return {}; }

As shown above, if we want to use curly braces, we need to use return keyword.

http://2ality.com/2012/04/arrow-functions.html
Arrow function without curly braces

Self-Executing Anonymous Functions

http://markdalgleish.com/2011/03/self-executing-anonymous-functions/

Anonymous self-invoking function (often called "module's side effect") will run when the module is evaluated, which happens when module is imported at least once in some other module (by importing any of its exported variables, functions or classes). [Importing a self invoking function]
[Code Example]

I learned this hard way. Had a (Jest) unit test file which was supposed to test some function exported from some other file/module which also contained anonymous self-invoking function. Unit test file was importing named function but each time I'd run tests, this anonymous function would be executed.

Named Arguments



Cool Javascript 9: Named arguments — Functions that get and return Objects
This helped me to understand the code which omits some properties while cloning an object [source]:

const obj = {a: 1, b: 2, c: 3, d: 4};
const clone = (({b, c, ...others}) => ({...others}))(obj); // remove b and c

This solution puts on display the beauty of JavaScript. Function declared and called in same line. Object passed as an argument is deconstructed to match the object set as argument in function definition. Spread operator. No need for return statement...I am impressed!

How to get a subset of a javascript object's properties

Pure Functions


Pure functions are functions that accept an input and returns a value without modifying any data outside its scope(Side Effects). Its output or return value must depend on the input/arguments and pure functions must return a value.

Immutability comes when we want to preserve our state. To keep our state from changing we have to create a new instance of our state objects. Immutability makes our app state predictable, ups the performance rate of our apps and to easily track changes in state. [ Understanding Javascript Mutation and Pure Functions ]

JavaScript’s object arguments are references, which means that if a function were to mutate a property on an object or array parameter, that would mutate state that is accessible outside the function. Pure functions must not mutate external state. [ Master the JavaScript Interview: What is a Pure Function? ]

Reassigning parameters deoptimizes in many engines, specifically v8 - it's a horrible idea to do it. Variables are free, and creating new ones rather than reusing old ones makes code much clearer.
(In addition, nothing in JS is passed by reference, everything is passed by value, where objects are a form of "reference value" - this is a good read on the subject) [Thoughts on "Never mutate parameters"]

To mutate, or not to mutate, in JavaScript
Don’t change objects in functions
Do not change objects after construction.

Never mutate parameters. 

Never reassign parameters.


Closures


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

Events


Is addEventListener (using listeners) preferred over attaching a handler?

https://www.reddit.com/r/learnjavascript/comments/6erpv2/whats_the_difference_between_using_an_event/
https://teamtreehouse.com/community/addeventlistener-vs-things-like-onclick-onchange-etc-pure-javascript

What is the correct attaching a handler (if go down this route)?
How to avoid wrong double handler execution?
https://stackoverflow.com/questions/7794301/window-onunload-is-not-working-properly-in-chrome-browser-can-any-one-help-me
Describe event phases
(capturing, bubbling...)
https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Examples#Example_5:_Event_Propagation

jQuery - Why returning false from (click) event handler?
https://stackoverflow.com/questions/11184276/return-false-from-jquery-click-event

Event.stopPropagation()
https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

Promises


http://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects
https://italonascimento.github.io/applying-a-timeout-to-your-promises/
https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#list-of-active-timers
https://stackoverflow.com/questions/31324110/why-does-the-promise-constructor-require-a-function-that-calls-resolve-when-co
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
https://javascript.info/promise-chaining
JavaScript: async/await with forEach()

Should a Promise.reject message be wrapped in Error?
https://stackoverflow.com/questions/26020578/should-a-promise-reject-message-be-wrapped-in-error
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject#Using_the_static_Promise.reject_method
https://stackoverflow.com/questions/26711243/promise-resolve-vs-new-promiseresolve

resolve & reject

`resolve` and `reject` functions inside a Promise's executor

async-await

https://javascript.info/async-await

http://2ality.com/2016/10/async-function-tips.html

(Especially pay attention to point #3 - Returned Promises are not wrapped - which explains when and why await can be omitted)

JavaScript loops - how to handle async/await
JavaScript: async/await with forEach()


RegEx


Comes very handy when need to test string arguments. Instead of lengthy code which would use string functions, testing a string to match some format is a one-liner with RegEx:

test: ({ app_name, app_version }) =>
   /myapp/i.test(app_name) && /^12\.3/.test(app_version)

RegExp.prototype.test()


What does the forward slash mean within a JavaScript regular expression?
The slashes indicate the start and end of the regular expression.


Regular expressions have four optional flags that allow for global and case insensitive searching. 
  • To indicate a global search, use the g flag. 
  • To indicate a case-insensitive search, use the i flag. 
  • To indicate a multi-line search, use the m flag. 
  • To perform a "sticky" search, that matches starting at the current position in the target string, use the y flag. 

These flags can be used separately or together in any order, and are included as part of the regular expression.

To include a flag with the regular expression, use this syntax:

var re = /pattern/flags;



Web APIs


WindowOrWorkerGlobalScope

setTimeout()

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout

How to set text in a <span> element?

https://stackoverflow.com/questions/1358810/how-do-i-change-the-text-of-a-span-element-in-javascript#

querySelector vs querySelectorAll

https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
https://stackoverflow.com/questions/14377590/queryselector-and-queryselectorall-vs-getelementsbyclassname-and-getelementbyid

When to use (set) innerHTML and when innerText?
tbd...

Misc How Tos


How to declare a constant data member in a class?
https://stackoverflow.com/questions/32647215/declaring-static-constants-in-es6-classes

Best Practices

Airbnb JavaScript Style Guide

JavaScript Rules

https://www.w3schools.com/js/js_best_practices.asp

Check that there is no unnecessary console.log which goes into production.
Check that there are no unused variables.
Check return values of functions (is it null, undefined, empty array, error code etc...)

Make sure of the time order of events and functions...e.g.

document.addEventListener("DOMContentLoaded", function() {..xxx...});

If xxx is never executed but you can see that document has event handler for DOMContentLoaded event that means that the document.addEventListener("DOMContentLoaded" code was executed too late, after DOMContentLoaded event has already been fired!

var btn = document.querySelectorAll("a.btn")[0];

Use querySelector.

I prefer:

var btn = document.querySelector("a.btn");
btn.addEventListener('click', function(e) {
   const url = "secure://extensions/?id=dmfdacibleoapmpfdgonigdfinmekhgp";
   chrome.tabs.create({ url: url });
});

to:

var btn = document.querySelector("a.btn");
if (btn !== null) {
   btn.addEventListener('click', function(e) {
      const url = "secure://extensions/?id=dmfdacibleoapmpfdgonigdfinmekhgp";
      chrome.tabs.create({ url: url });
   });
} else {
   console.log(“WARNING: a.btn element not found.”);
}

as console.log message would probably be unnoticed in the log while exception thrown in case btn is null would be marked in red in console log.

use _xxx for private member variables

use no special naming for global variables because try to avoid globals outside of application constants

Consts write as MY_CONST.

This function has a lot of side effects on the DOM and global multimap. Not necessarily a problem, although the function name doesn't communicate this. A nicer way may be restructure the app to have a function that returns a state object and then hands this off to a function which generates/mutates the DOM. It's a more "functional" way of doing it anyway! Personal preference.

when you say "state object", do you mean data (e.g. optimalGateway and multimap) in this case?

I mean an object representing all data within the application. Granted, this is quite a Redux/React way of thinking and is harder to implement with a bunch of jquery, so it may not be appropriate. Still, if I'm calling a function called loadDataFromBackgroundPage at the top level with no return value, it is very unclear that is going to have side effects on the DOM. I would expect it to have a return value of that data, and no side effects. Then that data could be handed off to another function to handle the rendering.

$() equivalent to $(document).ready()

$(handler) is now preferred to $(document).ready(handler) (https://api.jquery.com/ready/)

I have two similar handlers for $(document).click in this code so I'm going to merge them.

Function name capitalisation consistency.

selectedGatewayId = vpnExtensionEngine.getGateways().find(g => g.city.name + ", " + g.country.name === selectedLocation).id;

If find returns undefined, we'll get an error here. Might be worth adding a check for this or catching the error.

All parseInt calls should specify the base. e.g. parseInt(unixTimestampString, 10)

(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt confirms this)

Another DOM ready handler. Can we condense these in to one?

var url = "https://extension.avastbrowser.com/vpn/about/";

Move to const?

replace var with let wherever applicable.

Instead of looping infinitely and throwing, can we adjust this condition?

That's certainly better approach as exception is used here to control the flow of execution (and not to signal exceptional behaviour).

use setTimeout vs setInterval and clearing it immediately in the callback
Callback has to be executed only once, after timeout so it makes more sense to use setTimeout

const serverApiVersionMajor = lightVpnControllerApiVersion.split(".")[0];
const serverApiVersionMinor = lightVpnControllerApiVersion.split(".")[1];

Array destructuring supported in Chrome 49+, so could rewrite these two lines as:

const [serverApiVersionMajor, serverApiVersionMinor] = lightVpnControllerApiVersion.split(".")

Arrow functions are used elsewhere, so could use one here as well for compactness. Personal preference. Also, you wouldn't need the .bind(this) as the arrow function doesn't create a closure.

Chrome 42+ supports the fetch API. More compact and returns a promise, so don't need to wrap it like this. https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch

General tips:
https://news.ycombinator.com/item?id=17466391

Stack Overflow:
https://stackoverflow.com/questions/tagged/javascript


Packages


https://socket.io/
http://tesseract.projectnaptha.com/

Cool Tips and Tricks


How to print the name of the JS file currently loaded in html?

console.log('Loading script: ' + document.currentScript.src);

https://stackoverflow.com/questions/22095529/hiding-everything-until-the-page-has-finished-loading?noredirect=1&lq=1
https://en.wikipedia.org/wiki/Flash_of_unstyled_content
https://en.wikipedia.org/wiki/Screen_reader
https://stackoverflow.com/questions/2690865/visibility-attribute-question

How to add a line break to the string in messages.json?

https://phraseapp.com/docs/guides/formats/chrome-json/
https://github.com/angular-translate/angular-translate/issues/595
https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_content_best_practices
Where should I put <script> tags in HTML markup?
Remove Render-Blocking JavaScript
JavaScript Where To

String literals:

console.log(‘Hello, world!’)

or

console.log("Hello, world!")

What is lexical scope?
What is "this"?
What is Lexical Scope Anyway?
How do I write a named arrow function in ES2015?

Why is the content of some js files wrapped inside a function which is wrapped inside anonymous self-executed function?

(function () {...})();

What is the purpose of wrapping whole Javascript files in anonymous functions like “(function(){ … })()”?
Self-Executing Anonymous Functions
Immediately Invoked Function Expression (IIFE)
What does “use strict” do in JavaScript, and what is the reasoning behind it?
Should I 'use strict' for every single javascript function I write?
Not recommended to use “use strict” in ES6?
Strict Mode
What is this Javascript “require”?
What is require?
Requiring modules in Node.js: Everything you need to know
Commonly accepted best practices around code organization in JavaScript
ECMA-262, 9th edition, June 2018


What is the difference between String.slice and String.substring?

Create a JavaScript array containing 1…N
Tasks, microtasks, queues and schedules
https://codeburst.io/javascript-quickie-dot-notation-vs-bracket-notation-333641c0f781
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence