惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cisco Talos Blog
Cisco Talos Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
雷峰网
雷峰网
The Register - Security
The Register - Security
The Cloudflare Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
I
InfoQ
博客园 - 三生石上(FineUI控件)
H
Help Net Security
博客园 - 司徒正美
Vercel News
Vercel News
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
云风的 BLOG
云风的 BLOG
B
Blog
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
L
LangChain Blog
人人都是产品经理
人人都是产品经理
GbyAI
GbyAI
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Recorded Future
Recorded Future
小众软件
小众软件
Martin Fowler
Martin Fowler
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Apple Machine Learning Research
Apple Machine Learning Research
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
Y
Y Combinator Blog
V
Visual Studio Blog
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
V
V2EX
Blog — PlanetScale
Blog — PlanetScale

Dmitri Pavlutin Blog

Pure Functions in JavaScript: A Beginner's Guide Record Type in TypeScript: A Quick Intro How to Write Comments in React: The Good, the Bad and the Ugly 4 Ways to Create an Enum in JavaScript React forwardRef(): How to Pass Refs to Child Components TypeScript Function Types: A Beginner's Guide How to Use v-model to Access Input Values in Vue Mastering Vue refs: From Zero to Hero Environment Variables in JavaScript: process.env 5 Must-Know Differences Between ref() and reactive() in Vue How to Destructure Props in Vue (Composition API) Triangulation in Test-Driven Development How to Use nextTick() in Vue Programming to Interface Vs to Implementation A Smarter JavaScript Mapper: array.flatMap() Array Grouping in JavaScript: Object.groupBy() How to Access ES Module Metadata using import.meta JSON Modules in JavaScript How to Trim Strings in JavaScript TypeScript Function Overloading How to Debounce and Throttle Callbacks in Vue How to Show/Hide Elements in Vue Sparse vs Dense Arrays in JavaScript How to Fill an Array with Initial Values in JavaScript Covariance and Contravariance in TypeScript What are Higher-Order Functions in JavaScript? How to Use TypeScript with React Components Index Signatures in TypeScript How to Use React useReducer() Hook unknown vs any in TypeScript A Guide to React Context and useContext() Hook How to Use Promise.any() 2 Ways to Remove a Property from an Object in JavaScript 'return await promise' vs 'return promise' in JavaScript How to Use Promise.allSettled() How to Use fetch() with JSON JavaScript Promises: then(f,f) vs then(f).catch(f) What is a Promise in JavaScript? How to Use Promise.all() A Simple Guide to Component Props in React Don't Stop Me Now: How to Use React useTransition() hook A Simple Explanation of JavaScript Variables: const, let, var ES Modules Dynamic Import How to Memoize with React.useMemo() How to Cleanup Async Effects in React Why Math.max() Without Arguments Returns -Infinity How to Debounce and Throttle Callbacks in React Don't Confuse Function Expressions and Function Declarations in JavaScript How to Use ES Modules in Node.js Solving a Mystery Behavior of parseInt() in JavaScript How to Use Array Reduce Method in JavaScript 3 Ways to Merge Arrays in JavaScript A Guide to Jotai: the Minimalist React State Management Library The Difference Between Values and References in JavaScript How to Implement a Queue in JavaScript A Helpful Algorithm to Determine "this" value in JavaScript React useRef() Hook Explained in 3 Steps 7 Interview Questions on "this" keyword in JavaScript. Can You Answer Them? How to Greatly Enhance fetch() with the Decorator Pattern 7 Interview Questions on JavaScript Closures. Can You Answer Them? What's a Method in JavaScript? array.sort() Does Not Simply Sort Numbers in JavaScript How to Solve the Infinite Loop of React.useEffect() The New Array Method You'll Enjoy: array.at(index) What's the Difference between DOM Node and Element? Why Promises Are Faster Than setTimeout()? Everything About Callback Functions in JavaScript How React Updates State 5 Mistakes to Avoid When Using React Hooks 5 Best Practices to Write Quality JavaScript Variables Type checking in JavaScript: typeof and instanceof operators 3 Ways to Check if a Variable is Defined in JavaScript React Forms Tutorial: Access Input Values, Validate, Submit Forms Prototypal Inheritance in JavaScript How to Timeout a fetch() Request How to Learn JavaScript If You're a Beginner A Simple Explanation of React.useEffect() A Simple Explanation of JavaScript Iterators How to Use React Controlled Inputs Everything about null in JavaScript How to Use Fetch with async/await Getting Started with Arrow Functions in JavaScript An Interesting Explanation of async/await in JavaScript Front-end Architecture: Stable and Volatile Dependencies Is it Safe to Compare JavaScript Strings? How to Access Object's Keys, Values, and Entries in JavaScript What Actually is a String in JavaScript? 3 Ways to Shallow Clone Objects in JavaScript (w/ bonuses) Checking if an Array Contains a Value in JavaScript JavaScript Event Delegation: A Beginner's Guide How to Parse URL in JavaScript: hostname, pathname, query, hash 3 Ways to Detect an Array in JavaScript How to Get the Screen, Window, and Web Page Sizes in JavaScript 3 Ways to Check If an Object Has a Property/Key in JavaScript How to Compare Objects in JavaScript Object.is() vs Strict Equality Operator in JavaScript Own and Inherited Properties in JavaScript 5 Differences Between Arrow and Regular Functions How to Use Object Destructuring in JavaScript Your Guide to React.useCallback()
Make your JavaScript Code Shine: Knockout Old ES5 Hacks (Part 2)
Dmitri Pavlutin · 2016-08-17 · via Dmitri Pavlutin Blog

Many developers were frustrated about JavaScript, especially before ECMAScript 2015 standard. And they had the right too: the language is known for its weakness and initial design drawbacks.

How did it happen?

At the beginning of the web era, no one did know how exactly the web will look in 10 - 15 years. And especially that a small Java's brother JavaScript will become the dominant language in this area, especially on the client side.
It is complicated to predict the divergent evolution of the Internet technology.

It's like fixing a giant rocket on the full speed, moving to Earth's orbit.

Web evolution is a moving rocket

When things started to clear up, it wasn't so simple to head into the right direction.
A lot of codebase was written already: the rocket was launched. The backward compatibility and the growing list of hacks slow down the web development. Because really it should be extensible, scalable, easy and fun.

What a developer can do? Knockout JavaScript hacks!
Now it's the right time, especially that ECMAScript 2015 and beyond standards are sufficient to start coding the correct way.

So, let's continue with another series of improvements!

1. String literal as template

Embedding variables into a string literal? Don't forget to pair + and '.

Embedding variables into a string literal was always a tedious task.

It's ok for one variable 'str 1 ' + var1. It becomes harder when the construction grows 'str 1 ' + var1 + ' str 2' + (var2 + var3) + ' str 3'. You have to spend time pairing + and ' characters, and the whole expression is difficult to understand.

Let's look at the neighbor. In Python you can format strings easily, because the string literal and variables are separated:


animal = 'monkey'

countFruits = 4

fruit = 'bananas'

'A %s has %d %s' % (animal, countFruits, fruit)

# => 'A monkey has 4 bananas'


Compare the Python style with JavaScript's ES5 style, where variables and literals are mixed. This mix creates a construction that is complicated to follow:


var animal = 'monkey';

var countFruits = 4;

var fruit = 'bananas';

'A ' + animal + ' has ' + countFruits + ' ' + fruit;

// => 'A monkey has 4 bananas'


Obviously the amount of concatenation operators + and single quotes ' does not make the entire string readable.

ECMAScript 2015 presents a new type of string literal called template literal. Fortunately it allows to embed JavaScript expressions into place holders ${expression} and create strings as easy as a pie.

Use template literals in JavaScript

Let's see how nicely literals are looking now:


var animal = 'monkey';

var countFruits = 4;

var fruit = 'bananas';

`A ${animal} has ${countFruits} ${fruit}`

// => 'A monkey has 4 bananas'


No more redundant concatenation operators and single quotes. The template literal makes the string easy to follow.

Additionally template literals allow to write strings in multiple lines:


var templateLiteral = `Line 1

Line 2

Line 3`;


And compose so called tagged template literals, by placing a function encodeUrlParams before the template string:


function encodeUrlParams(literals, ...substitutions) {

var str = '';

// run the loop only for the substitution count

for (var i = 0; i < substitutions.length; i++) {

str += literals[i];

str += encodeURIComponent(substitutions[i]);

}

// add the last literal

str += literals[literals.length - 1];

return str;

}

var redirectUrl = 'http://site2.com';

encodeUrlParams `http://example.com?redirect=${redirectUrl}`;

// => 'http://example.com?redirect=http%3A%2F%2Fsite2.com'


encodeUrlParams() is a function that encodes the strings to be used safely as URL parameters. Inside the function literals parameter is an array that contains the literals parts. substitutions contains the evaluation result of the expressions, which in our case are the params to be encoded.

Template literals availability:

2. Verify substring existence in a string

string.indexOf(substring) !== -1? Could be better!

In ES5, searching for a substring in a string does not provide a straightforward solution. The most appropriate way is to use string.indexOf(substring, [fromIndex]) method. Then compare the returned index with the hardcoded value -1, which indicates that the substring is not found:


var str = 'Hello World';

str.indexOf('Hello') !== -1; // => true

str.indexOf('Hi') !== -1; // => false


str.indexOf('Hello') returns 0: the index of 'Hello' substring. 0 !== -1 comparison evaluates to true.
For a missing substring 'Hi', the code str.indexOf('Hi') returns -1. So -1 !== -1 comparison evaluates to false.

Even the hack explanations are clumsy: something is not because something is not.

Finally ECMAScript 2015 provides a correct method to verify a substring existence: string.includes(substring, [fromIndex]). The method returns a boolean that indicates if substring is a substring of string, optionally starting from an index fromIndex.

Use string.includes(substring) string method instead of string.indexOf(substring) !== -1

The above example is now transformed to a clear solution:


var str = 'Hello World';

str.includes('Hello'); // => true

str.includes('Hi'); // => false


str.includes('Hello') evaluates to true, because 'Hello' is an existing substring.
And for a non-existing substring 'Hi' the code str.includes('Hi') returns false.

No more alternatives. Refactor your project by changing from string.indexOf(substring) !== -1 to string.includes(substring) and be happy as a clam.

.includes() string method availability:

3. Function call with dynamic list of arguments

array.push.call(array, elements)? We can do better.

Many JavaScript functions and methods accept a dynamic number of arguments. For example console.log(text1, text2, ...), array methods .push(), .concat(), etc.

They work nicely when you enumerate manually the arguments:


var numbers = [1, 2, 3];

numbers.push(4, 5);

numbers // => [1, 2, 3, 4, 5]


In the expression numbers.push(4, 5) two arguments are indicated: 4 and 5. These elements are pushed into numbers array.

Often an array holds the arguments. This case is not so simple. The option is to apply an indirect call using .apply() method of the function object. Let's take a try:


var numbers = [1, 2, 3];

var toPush = [4, 5];

numbers.push.apply(numbers, toPush);

numbers // => [1, 2, 3, 4, 5]


The indirect call to push some elements from an array looks clumsy: numbers.push.apply(numbers, toPush).
Calling an additional method .apply() is the first problem. Short code is always better than lengthy one.
The second problem is a redundant indication of the context .apply(numbers, ...). The whole indirect call expression in the end contains 2 times numbers object: numbers.push.apply(numbers, toPush).

To me the indirect call expression looks like a big lazy frog.

The spread operator ... makes the function invocation from an array of arguments wonderful.
Simply put three dots ... before the array when calling a function and its elements spread into invocation arguments. myFunction(...argsArray) is equivalent to myFunction(argsArray[0], argsArray[1], ..., argsArray[n]).

Use spread operator to call a function with arguments from an array

The above example can be improved significantly:


var numbers = [1, 2, 3];

var toPush = [4, 5];

numbers.push(...toPush);

numbers // => [1, 2, 3, 4, 5]


When numbers.push(...toPush) is executed, the spread operator ... spreads toPush array elements into arguments. Simple and beautiful as a sunrise.

A nice bonus: three dots ... accepts not only arrays, but any iterable objects (strings, maps, sets, etc). Also you can use the operator to setup the arguments for a constructor call new Date(...[2017, 8, 1]), which is not possible with indirect calls .apply().

Spread operator availability:

4. Class hierarchy

I feel a bit nervous when I write .prototype.

Today's trending says to favor composition over inheritance. I would add: to favor only when necessary!

As an example, you can find the class inheritance useful when extending React components.

ES5 standard does not permit obviously to create a class hierarchy. It happens because JavaScript has a prototypal-based inheritance, and the class inheritance is emulated on top of it.

Let's define a superclass and extend it in a subclass:


function Figure(notation) {

this.notation = notation;

}

Figure.prototype.getNotation = function() {

return 'Figure notation: ' + this.notation;

}

function Square(notation, sideLength) {

Figure.call(this, notation);

this.sideLength = sideLength;

}

Square.prototype = Object.create(Figure.prototype);

Square.prototype.constructor = Square;

Square.prototype.getArea = function() {

return Math.pow(this.sideLength, 2);

};

var mySquare = new Square('ABCD', 4);

mySquare.getNotation(); // => 'Figure notation: ABCD'

mySquare.getArea(); // => 16


Figure is a superclass. Square is a subclass of it, which inherits notiation property and getNotiation() method.
Square class defines its own sideLength property and getArea() method.
As seen in the example, to mimic the class inheritance a lot of additional glue code is added:

  • In the Square constructor Figure.call(this, notation) makes a parent constructor call
  • Square.prototype = Object.create(Figure.prototype) creates a new prototype object for Square constructor. Square instances inherit methods from Figure.prototype
  • Square.prototype.constructor = Square fixes the prototype to have the correct constructor property

If you get payed depending on how much lines of code you provide in 8 hours, this option may work. But normally this is too verbose.

ECMAScript 2015 class makes the class inheritance usage much easier, providing a syntactic sugar over JavaScript's prototypal inheritance. Nice!

Use ES2015 class synstax instead of manually setup the prototypes

Let's improve the above geometrical class hierarchy using ES2015 class syntax:


class Figure {

constructor(notation) {

this.notation = notation;

}

getNotation() {

return 'Figure notation: ' + this.notation;

}

}

class Square extends Figure {

constructor(notation, sideLength) {

super(notation);

this.sideLength = sideLength;

}

getArea() {

return Math.pow(this.sideLength, 2);

}

}

var mySquare = new Square('ABCD', 4);

mySquare.getNotation(); // => 'Figure notation: ABCD'

mySquare.getArea(); // => 16


Indeed class syntax makes the code more convenient:

  • Use class <ClassName> to defined a class and class <Class> extends <SuperClassName> to define a subclass that extends a super class
  • A special method constructor() {...} is used as constructor to initialize the newly created instance with properties
  • In the constructor function super() calls the parent class constructor. Just be sure to execute super() before using this

If you're using React's React.createClass({...}), it is the right time to migrate to ES2015 classes React.Component.

class syntax availability:

5. Transform array-like object to array

Array-like objects... What could be more hacky?

I consider array-like objects a weird constructor. In the end, why not simply create arrays? Probably this is the answer for arguments object. And because many DOM methods return an HTMLCollection, which is a live array-like object that cannot be mutated (live means it's auto-updated when DOM changes).

Nevertheless exist situations when the array-like object needs to be transformed into a regular array, to take advantage of the array methods like .forEach(), .map(), etc.

Let's see how to transform an array-like object to an array using ES5 possibilities:


function argsToArray() {

return Array.prototype.slice.call(arguments);

}

argsToArray(5, 6, 8) // => [5, 6, 8]


argsToArray() returns an array that contains the arguments passed to function on invocation. To transform the array-like object arguments into a real array, an indirect call to .slice() method is applied: Array.prototype.slice.call(arguments).

Array.from(arrayLike, [mapFunction]) function transform arrayLike parameter into an array and returns it.

Use Array.from(arrayLike) to transform an array-like object into an array

Let's see Array.from() in action:


function argsToArray() {

return Array.from(arguments);

}

argsToArray(5, 6, 8) // => [5, 6, 8]


Array.from(arguments) transforms arguments array-like object into an array. This approach is shorter than the one presented above with an indirect call to .slice() array method.

If you don't want to deal at all with arguments object (and I recommend you to do that), define a rest parameter:


function argsToArray(...args) {

return args;

}

argsToArray(5, 6, 8) // => [5, 6, 8]


Array.from() availability:

6. Don't stop me now

The article covered another list of hacks that should be migrated when possible. Thanks to ECMAScript 2015/2016 the migration adds big benefits.

Surely string.indexOf(substring) !== -1 should disappear from now on in favor of the correct string.includes(substring).

The template literal improves the code readability when variables should embed into a string. You could also insert newlines into template literals, which is a nice bonus.

To reduce the redundant code that mimics class inheritance over JavaScript's prototypal inheritance, it is recommended to use class syntax. It is light and short.
However think twice before creating hierarchies of classes in JavaScript. Composition has a lot of flexibility, and consider it first as an option before diving into class inheritance mechanism.

Such a beautiful day to make your JavaScript code shine!