DEV Community

Cover image for 101 JavaScript Concepts You Need to Know
Manish Dnyandeo Salunke
Manish Dnyandeo Salunke

Posted on

101 JavaScript Concepts You Need to Know

Introduction

JavaScript is one of the most popular programming languages used in web development today. It is the backbone of modern web applications and is used in a variety of contexts from front-end to back-end, and even in mobile application development. However, JavaScript also has a reputation for being a quirky and sometimes difficult language to understand due to its unique blend of functional and object-oriented programming characteristics. In this blog post, we will cover over 100 JavaScript concepts that every developer should know.

1. Variables

In JavaScript, variables are used to store data that can be manipulated and referenced throughout your code. JavaScript supports several ways of declaring variables, including the var, let, and const keywords. Understanding the differences between these declarations is crucial for writing predictable and bug-free code.

2. Data Types

JavaScript has a few basic data types including Number, String, Boolean, Object, Null, and Undefined. Each of these types has its own set of behaviors and characteristics. For instance, objects in JavaScript are mutable and can be changed after they are created, while numbers, strings, and booleans are immutable.

3. Control Flow

Control flow in JavaScript refers to the order in which the computer executes statements in a script. Statements like if, else, switch, for, while, and do while are used to control the flow of execution in a program, allowing the program to branch in different directions based on conditions and loop over code blocks.

4. Functions

Functions are reusable pieces of code that can be defined once and used throughout your code. They can take parameters and return values. In JavaScript, functions are objects, which means they have properties and can even be assigned to variables and passed around.

5. Scope

In JavaScript, scope refers to the accessibility or visibility of variables, functions, and objects in some particular part of your code during runtime. JavaScript has both local scope and global scope. Understanding how scope works is critical to writing effective code and avoiding potential bugs.

300 React JS Interview Questions and Answers

6. Closures

A closure is a JavaScript function that has access to its own scope, the scope of the outer function, and global variables. Closures are created every time a function is created, at function creation time. They're a powerful feature in JavaScript that can be used for a variety of tasks, including data privacy, factory functions, and more.

7. Hoisting

Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their containing scope during the compile phase. Understanding hoisting will help you avoid common mistakes related to the order of your code.

8. Event Loop

JavaScript has a concurrency model based on an event loop, which is responsible for executing the code, collecting and processing events, and executing queued sub-tasks. This model allows JavaScript to be very efficient in handling asynchronous operations, making it ideal for tasks like handling user input or fetching data from a server.

9. Promises and Async/Await

Promises are objects that represent the eventual completion or failure of an asynchronous operation. They are a powerful tool for organizing and managing asynchronous code. Async/await is syntactic sugar on top of promises, providing a more convenient and readable way to handle asynchronous code.

10. Prototypes and Inheritance

JavaScript is a prototype-based language and uses prototypal inheritance rather than classical inheritance. Every object in JavaScript has a prototype property that points to the object it should inherit properties and methods from. Understanding prototypal inheritance is fundamental to understanding how objects work in JavaScript.

11. this keyword

The this keyword in JavaScript is a complex concept that behaves differently depending on the context in which it is used. It can refer to an object that owns the code of the current scope, the object that a method is associated with, the global object in non-strict mode, or other contexts depending on the way a function is called.

12. DOM Manipulation

The Document Object Model (DOM) is a programming interface for web documents. It represents the structure of a web page and allows JavaScript to manipulate the content, structure, and styles of a webpage. Understanding the DOM is crucial for making interactive web pages.

900+ JavaScript Interview Questions and Answers

13. Event Handling

JavaScript allows you to make web pages interactive by responding to user actions or events like clicks, mouse movements, key presses, and more. This is done using event listeners and event handlers. Understanding events is key to creating dynamic and interactive user experiences.

14. AJAX and Fetch API

AJAX (Asynchronous JavaScript and XML) and the Fetch API are techniques used to communicate with a server and retrieve data asynchronously without refreshing the page. They are essential for creating dynamic and responsive web applications.

15. Error Handling

Error handling in JavaScript is done using the try, catch, finally blocks. It helps to catch programming errors and exceptions and handle them gracefully. This is crucial for building robust and resilient applications.

16. Regular Expressions

Regular Expressions, or RegEx, are patterns used to match character combinations in strings. In JavaScript, regular expressions are also objects and can be used with methods like test() and exec(). They are incredibly useful for tasks like form validation, searching, and text manipulation.

17. JSON

JSON, or JavaScript Object Notation, is a lightweight data-interchange format that is easy for humans to read and write and easy for machines to parse and generate. In JavaScript, JSON is a native data type, and methods like JSON.parse() and JSON.stringify() allow for easy conversion between JSON and JavaScript objects.

18. Template Literals

Template literals are string literals that allow embedded expressions. You can use multi-line strings and string interpolation features with them. They were introduced in ES6 and provide a more powerful and flexible way to work with strings.

19. Destructuring Assignment

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. This feature was also introduced in ES6 and provides a concise way to work with complex data structures.

20. Modules

Modules in JavaScript are reusable pieces of code that can be exported from one program and imported for use in another program. Modules are particularly useful for maintaining a clean and organized codebase and are a critical aspect of building scalable applications.

21. Callbacks

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function. Callbacks are a fundamental aspect of JavaScript and are central to many asynchronous operations.

22. Array Methods

JavaScript provides a wide array of methods to work with arrays. These include methods to iterate over arrays (like forEach, map, filter, reduce), methods to check the contents of an array (every, some, includes), and methods to manipulate arrays (push, pop, shift, unshift, splice).

23. Spread and Rest Operators

The spread operator (...) allows an iterable to be expanded in places where zero or more arguments or elements are expected. The rest parameter syntax (...) is used to represent an indefinite number of arguments as an array. Both concepts were introduced in ES6 and provide a more concise and flexible way to work with arrays and function arguments.

24. Arrow Functions

Arrow functions provide a compact syntax to write functions in JavaScript. They are especially handy when dealing with functions that are to be passed as callbacks or when maintaining the context of this.

25. Equality: == vs ===

JavaScript provides two comparison operators == and ===. The == operator will compare for equality after doing any necessary type conversions. The === operator will not do the conversion, so if two values are not the same type === will simply return false. Both are incredibly important to understand as they behave differently and can lead to bugs if misunderstood.

26. Type Coercion

Type coercion in JavaScript refers to the automatic or implicit conversion of values from one data type to another. It can occur in various situations, like in comparisons, in arithmetic operations, or implicitly when you try to manipulate values of different types.

27. null vs undefined

null and undefined in JavaScript both represent the absence of value. However, they are used differently. undefined typically means a variable has been declared but not defined yet. null is an assignment value that represents no value or no object.

28. Truthy and Falsy Values

In JavaScript, a truthy value is a value that is considered true when encountered in a Boolean context. JavaScript uses type coercion to translate truthy or falsy values when we use them in a Boolean context. All values are truthy unless they are defined as falsy (i.e., except for false, 0, -0, 0n, "", null, undefined, and NaN).

29. JavaScript Engine and Runtime

JavaScript engines are used to execute JavaScript code. Different browsers use different engines like V8 (Chrome, Edge), SpiderMonkey (Firefox), and JavaScriptCore (Safari). A JavaScript runtime is where the code is executed and interacts with the engine and the environment (like a browser or a Node.js server).

30. Web Storage: localStorage, sessionStorage

Web storage objects localStorage and sessionStorage allow to save key/value pairs in the browser. localStorage stores data with no expiration date, and the data is not sent back to the server. sessionStorage does the same thing but persists only for as long as the window or tab is open.

31. Function Expressions vs Function Declarations

In JavaScript, functions can be defined using function declarations or function expressions. Function declarations are hoisted to the top of their scope and can be invoked before their declaration. Function expressions are not hoisted, and they can be anonymous or named.

32. IIFE (Immediately Invoked Function Expression)

An Immediately Invoked Function Expression (IIFE) is a JavaScript function that runs as soon as it is defined. It is a design pattern which is also known as a Self-Executing Anonymous Function and contains two major parts - the function enclosed within (), and the group () that causes the function to be executed.

33. Higher Order Functions

A Higher Order Function is a function that receives another function as an argument or returns a new function. Higher order functions are a big part of functional programming in JavaScript and they include functions like map, filter, reduce, and sort.

34. Pure Functions

A Pure function is a function where the return value is only determined by its input values, without observable side effects. This is how functions in math work: Math.cos(x) will, for the same value of x, always return the same result.

35. Bitwise Operators

JavaScript supports bitwise operators as part of the language syntax. Bitwise operators perform operations at the bit level. These operators are not commonly used and are usually only seen in optimized code.

36. Recursion

Recursion in JavaScript is a process in which a function calls itself as a subroutine. This allows the function to be used repeatedly to solve a subproblem that is part of a larger problem. Recursive functions can be very effective for tasks such as traversing tree-like data structures.

37. Map, Set, WeakMap, WeakSet

JavaScript ES6 introduced new data structures like Map, Set, WeakMap, and WeakSet. Map is a collection of keyed data items, just like an Object, but allows keys of any type. Set is a collection of unique values. WeakMap is just like Map, but it doesn't prevent JavaScript from removing its items, while WeakSet is a variant of Set that only holds objects, and where the objects may be removed by garbage collection.

38. Generators

Generators are a special kind of function that can be paused and resumed, allowing other code to run in the meantime. A generator looks like a function but behaves like an iterator. They are defined using the function* syntax.

39. Symbols

Symbols are a new primitive type introduced in ES6. They are completely unique identifiers. Just like their primitive counterparts (Number, String, Boolean), they can be created using the factory function Symbol() which returns a Symbol.

40. Iterables and Iterators

Iterable is a concept from JavaScript's Symbol.iterator. An object is iterable if it defines its iteration behavior, such as what values are looped over in a for...of construct. Some built-in types, such as Array or Map, have a default iteration behavior, while other types (like Object) do not.

41. Proxy and Reflect

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc). The Reflect object is similar to Proxy but it allows you to easily forward default operations to the original object.

42. Transpiling and Babel

Transpiling is the process of converting source code from one language or language version to another. Babel is a popular JavaScript transpiler that can transform ES6 (and beyond) code into ES5 code, which is compatible with more environments.

43. Polyfill

A polyfill is a piece of code (usually JavaScript on the Web) used to provide modern functionality on older browsers that do not natively support it. They are a bridge that allow you to leverage APIs from newer browsers in older browsers.

44. Web Workers

Web Workers is a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface. Workers utilize thread-like message passing to achieve parallelism.

45. Service Workers

Service Workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available). They are intended, among other things, to enable the creation of effective offline experiences, intercept network requests and take appropriate action based on whether the network is available.

46. WebSockets

WebSockets is a communication protocol which provides full-duplex communication channels over a single TCP connection. It allows real-time data transfer from and to the server. This protocol is used for creating real-time web applications.

47. CORS (Cross-Origin Resource Sharing)

CORS is a mechanism that allows many resources (e.g., fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain from which the resource originated. It's a mechanism supported in HTML5 that manages XMLHttpRequest access to a domain different.

48. requestAnimationFrame

requestAnimationFrame is a method that tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint.

49. The Event Loop

The event loop is what allows JavaScript to be asynchronous and have non-blocking I/O, despite the fact that JavaScript is not multi-threaded. It's a loop that waits for events and dispatches them when they happen.

Angular Interview Questions and Answers

50. Tail Call Optimization

Tail call optimization (TCO) is a feature in JavaScript (strict mode only) where, if the last statement in a function is a call to a function, the JavaScript engine can optimize the stack memory by clearing the current stack frame and reusing it for the next function call.

51. Typed Arrays and ArrayBuffers

Typed Arrays are array-like objects and provide a mechanism for accessing raw binary data. ArrayBuffer objects are used to represent a generic, fixed-length raw binary data buffer. You can't directly manipulate the contents of an ArrayBuffer; instead, you create one of the typed array objects or a DataView object which represents the buffer in a specific format.

52. Binary Data: Blob & File APIs

JavaScript has built-in support for binary data. The Blob object represents a blob, which is a file-like object of immutable, raw data; they can be read as text or binary data. The File API, which includes the FileReader object, allows web applications to read and manipulate File objects.

53. URL and Base64 Encoding

JavaScript provides the encodeURI(), encodeURIComponent(), and btoa() methods for URL and Base64 encoding. These methods are useful when dealing with strings that need to be included in URLs or used in a context that requires Base64-encoded data.

54. JavaScript Design Patterns

JavaScript design patterns are reusable solutions applied to commonly occurring problems in writing JavaScript web applications. Some of the popular JavaScript design patterns are Module, Prototype, Observer, and Singleton.

55. Testing: Unit Tests, Integration Tests, and E2E Tests

Testing is a fundamental part of the JavaScript development process. JavaScript developers typically use testing frameworks like Jest or Mocha to write unit tests (tests for individual functions or components), integration tests (tests for code modules or services), and end-to-end (E2E) tests (tests for user journeys across the entire application).

56. Strict Mode

Strict mode is a way to opt into a restricted variant of JavaScript. Adding "use strict"; at the beginning of a file, script, or function, will trigger the JavaScript engine to interpret the code in strict mode. Strict mode can help catch some common coding errors before they are run.

57. Meta-programming: new.target, Reflect, Proxy

Meta-programming in JavaScript involves using programs to manipulate their own structures. new.target, Reflect, and Proxy are all part of JavaScript's meta-programming capabilities. new.target lets you detect if a function or constructor was called using the new operator, Reflect is an object that provides methods for interceptable JavaScript operations, and Proxy is used to define custom behavior for fundamental operations.

58. Web Assembly (WASM)

WebAssembly (often abbreviated as WASM) is a binary instruction format for a stack-based virtual machine. It is designed as a portable target for the compilation of high-level languages like C, C++, and Rust, enabling deployment on the web for client and server applications.

59. Memory Management and Garbage Collection

JavaScript abstracts most of the memory management details from the developer by providing automatic garbage collection. That means that as a developer you can allocate memory (i.e., create objects) and you don't have to worry about freeing up the memory when it's no longer needed; the JavaScript garbage collector will handle that.

60. Event Bubbling and Capturing

Event bubbling and capturing are two ways of event propagation in the HTML DOM API. Bubbling is the event starts from the target element to upwards (from the deepest target element to document object), and capturing is the event starts from top to target element (from the document object to the deepest target element).

61. Execution Context and Stack

In JavaScript, the execution context is an abstract concept that holds code that is currently running. It consists of the creation and execution phases where the JavaScript engine first creates variables, functions, and arguments and then executes the code. JavaScript uses a data structure called the Execution Stack to keep track of its execution contexts.

62. Block Binding and Block Scope

JavaScript ES6 introduced Block Scope which can be created with let and const. These variables actually exist only within the block in which they are defined. This is a different behavior compared to var, which defines a variable globally or locally to an entire function regardless of block scope.

63. Function Chaining

Function chaining is a technique used for simplifying code where multiple methods are called on the same object. JavaScript allows for function chaining by ensuring that all functions that are part of the chain return the object itself, so the next function can be called on it.

64. Mixins

A mixin is a class whose methods can be used by other classes without the need for inheritance. In JavaScript, mixins are a tool which allows developers to include behavior from multiple sources in a class's prototype.

65. Debouncing and Throttling Techniques

Debouncing and throttling are techniques to control how many times a function can be executed. Debouncing ensures that a function is not executed again until a certain amount of time has passed without it being called. Throttling ensures a function is not called more than once in a specified time period.

66. Functional Programming

Functional Programming (FP) in JavaScript is a coding style that focuses on building the software by composing pure functions, avoiding shared state, mutable data, and side-effects. JavaScript is not a functional language, but it has some features of it like first-class functions and closures.

67. Object-Oriented Programming (OOP)

JavaScript supports Object-Oriented Programming (OOP) with prototypal inheritance, which is a bit different from classical OOP languages like Java or C++. However, with the introduction of classes in ES6, JavaScript now has a much closer resemblance to classical OOP languages.

68. Currying

Currying is a transformation of functions that translates a function from callable as f(a, b, c) into callable as f(a)(b)(c). Currying doesn't call a function. It just transforms it. In JavaScript, this can be easily done with binding.

69. Data Immutability

Immutability is a core principle in functional programming, and it also plays a major part in working with React and Redux. In JavaScript, we commonly use the const keyword to prevent reassigning, and methods like .map(), .filter(), .reduce(), etc. to prevent mutating arrays.

70. Isomorphic JavaScript

Isomorphic JavaScript, also known as Universal JavaScript, is code that can run both in the server and the client. This has led to the ability to build apps that render full pages on the server with response to navigation but then after page load, turn into Single-Page-Applications (SPAs).

71. Single-Threaded Model with Event Loop

JavaScript follows a single-threaded model with event loop model. This model allows JavaScript to be very efficient in handling asynchronous operations and offers much greater control over how and when different parts of your program are executed.

72. JavaScript Timers: setTimeout, setInterval, requestAnimationFrame

JavaScript has several methods for scheduling and controlling the execution of code: setTimeout for running code after a specified delay, setInterval for running code at fixed intervals, and requestAnimationFrame for running code just before the next repaint for smoother animations.

73. Real-Time Communication with WebRTC

WebRTC (Web Real-Time Communication) is a technology which enables Web applications and sites to capture and optionally stream audio and/or video media, as well as to exchange arbitrary data between browsers without requiring an intermediary.

74. JavaScript modules: import / export

JavaScript has a built-in module system for importing and exporting code between different files. The import statement is used to import functions, objects, or values from another file, while the export statement is used to export functions, objects, or values from the file they reside in.

Vue JS Interview Questions and Answers

75. JavaScript Symbols

Symbols are a new primitive type in JavaScript. They are unique and can be used as identifiers for object properties. They can also be used to implement private properties and methods in an object.

76. JavaScript Decorators

Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

77. Error Handling: throw, try, catch, finally

Error handling in JavaScript is facilitated through the use of throw, try, catch, and finally constructs. throw is used to create a custom error. The try statement allows you to define a block of code to be tested for errors while it's being executed. The catch statement allows you to define a block of code to be executed if an error occurs in the try block. The finally statement lets you execute code after try and catch, regardless of the result.

78. Memoization

Memoization is a technique of caching the results of expensive function calls and reusing the cached result when the same inputs occur again. It is a powerful optimization technique that can drastically improve the performance of your JavaScript application.

79. JavaScript Engines: V8, SpiderMonkey, JavaScriptCore

JavaScript engines are programs that execute JavaScript code. The most popular JavaScript engines are V8 (Chrome and Node.js), SpiderMonkey (Firefox), and JavaScriptCore (Safari).

80. JavaScript Runtime Environment: Node.js and Browsers

A JavaScript runtime environment is a platform that interprets JavaScript code. Examples of JavaScript runtime environments include web browsers like Chrome, Firefox, Safari, and Edge, and server-side environments like Node.js.

81. Web APIs: DOM, Event, Fetch

Web APIs are not part of the JavaScript language per se, but they are part of the larger web platform. The DOM API allows JavaScript to interact with the HTML and CSS of a webpage. The Event API enables JavaScript to listen and respond to browser events. The Fetch API provides an interface for fetching resources across the network.

82. JavaScript Package Managers: npm, yarn

Package managers are tools that allow you to manage dependencies in your project. npm (Node Package Manager) and Yarn are two popular JavaScript package managers. They allow you to install, update, and manage external packages from the npm registry.

83. Task Runners: grunt, gulp

Task runners in JavaScript are used to automate repetitive tasks like minification, compilation, unit testing, and linting. Grunt and Gulp are two popular JavaScript task runners.

84. Module Bundlers: webpack, rollup, parcel

Module bundlers are tools that take pieces of JavaScript and their dependencies and bundle them into a single file, typically for use in the browser. Webpack, Rollup, and Parcel are popular JavaScript module bundlers.

85. Transpilers: Babel, TypeScript

Transpilers take source code written in one language and produce the equivalent code in another. Babel is a JavaScript compiler that can convert ECMAScript 2015+ code into a backward compatible version of JavaScript that can be run by older JavaScript engines. TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.

86. JavaScript Testing Frameworks: Jest, Mocha, Jasmine

JavaScript testing frameworks provide an automated way to ensure that your JavaScript code is working as expected. Jest, Mocha, and Jasmine are some of the most popular JavaScript testing frameworks. They provide features for structuring tests, assertions, mocking, and more.

87. JavaScript Linting: ESLint, JSLint, Prettier

Linting tools analyze code for potential errors and enforce a certain coding style. ESLint, JSLint, and Prettier are popular linting tools in the JavaScript ecosystem. They help to maintain code consistency and prevent bugs.

88. Front-End Libraries and Frameworks: React, Angular, Vue.js

Front-end libraries and frameworks like React, Angular, and Vue.js are powerful tools for building complex user interfaces in JavaScript. They provide abstractions for DOM manipulation and state management, making it easier to create dynamic web applications.

89. Server-Side JavaScript: Node.js, Express.js

Server-side JavaScript allows the use of JavaScript on the server. Node.js is a runtime that allows JavaScript to be run outside of the browser, and Express.js is a minimal web application framework for Node.js.

90. Mobile Application Development: React Native, Ionic

React Native and Ionic are popular frameworks for building mobile applications using JavaScript. React Native allows you to build native mobile apps using JavaScript and React, while Ionic allows you to build hybrid mobile apps with web technologies.

91. Desktop Application Development: Electron, NW.js

Electron and NW.js are popular frameworks for building desktop applications using JavaScript, HTML, and CSS. Electron is maintained by GitHub and used in popular apps like Atom and Visual Studio Code, while NW.js was previously known as Node-WebKit and allows you to call Node.js modules directly from DOM.

92. Static Site Generators: Gatsby, Next.js

Static site generators like Gatsby and Next.js allow you to create static HTML websites with JavaScript and modern front-end tools. These sites can be easily deployed and hosted, and they provide better performance and security than traditional dynamic sites.

93. Headless CMS: Strapi, Ghost, Contentful

A headless CMS is a back-end content management system built as a content repository that makes content accessible via a RESTful API for display on any device. Strapi, Ghost, and Contentful are popular headless CMS that can be used with JavaScript.

94. GraphQL: Apollo, Relay

GraphQL is a query language for APIs and a runtime for executing those queries with your existing data. Apollo and Relay are popular JavaScript libraries for working with GraphQL.

95. State Management: Redux, MobX

Redux and MobX are libraries used for state management in JavaScript applications. They help you manage global state - state that is used across many components or modules.

96. Asynchronous JavaScript: Callbacks, Promises, Async/Await

Asynchronous JavaScript refers to the use of asynchronous operations in JavaScript. These operations allow JavaScript to perform other tasks while waiting for asynchronous operations to complete. Callbacks, Promises, and Async/Await are different ways to handle asynchronous operations in JavaScript.

97. JavaScript Data Structures: Arrays, Stack, Queue

JavaScript provides built-in data structures like Arrays, and you can easily implement other data structures like Stacks and Queues using JavaScript. Understanding these data structures is crucial for solving complex problems.

98. JavaScript Algorithms: Sorting, Searching

Sorting and searching are fundamental algorithms in computer science, and JavaScript is no exception. JavaScript provides built-in methods for sorting arrays, and searching can be performed using a variety of techniques.

99. Web Performance: Minification, Lazy Loading, Caching

Improving web performance is a crucial part of web development. Techniques like minification (to reduce the size of code), lazy loading (to delay loading resources until they are needed), and caching (to save and reuse frequently used data) can be used to enhance the performance of a web page.

100. Accessibility: ARIA, Semantic HTML

Accessibility in web development means making web pages accessible to all users, including those with disabilities. ARIA (Accessible Rich Internet Applications) and Semantic HTML are tools that developers can use to improve accessibility in web pages.

101. Internationalization and Localization

Internationalization is the process of designing and preparing your app to be usable in different languages. Localization means the adaptation of a product, application or document content to meet the language, cultural and other requirements of a specific target market (a locale).

500+ Python Coding Challenges

Top comments (1)

Collapse
 
fruntend profile image
fruntend

Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍

Some comments may only be visible to logged-in visitors. Sign in to view all comments.