Friday, June 5, 2020

JSON helps

There are some great uses of JSON, some fair uses of JSON and then some questionable uses of JSON :-)

Replace keys in Object

  1. obj = JSON.parse(JSON.stringify(obj).replace(/"key1":/g, "\"key2\":")));

Parse response headers

  1. r.headers = JSON.parse("{\"" + r.getAllResponseHeaders().trim()
  2. .replace(/: /g, "\":\"").replace(/\r\n/g, "\",\"") + "\"}");
Saturday, March 14, 2020

RegExp in JS

.exec() - Executes a search for a match in its string parameter.

.test(): bool - Tests for a match in its string parameter.

.split() -

Not IE11:

.match() - Performs match to given string and returns match result.

.matchAll() - Returns all matches of the regular expression against a string.

.search() - Searches the match in given string and returns the index the pattern found in the string.

.replace() - Replaces matches in given string with new substring.

Tuesday, January 14, 2020

Layout reflow

In Dev Console I sometimes got a warning, that page reflow took so much ms. And because I'm curious, I looked up what it means.

Reflow is basically recalculation of dimensions and positions for some or all layout elements on the page. Just from this description you may understand it's pretty expensive (performance-wise) task.

Reflow can be triggered by manipulating DOM, changing visuals (computed styles and classes), resizing browser's window and more. Different browser engines have slightly different triggers and time required for the particular reflow type, but the general idea remains.

You can speed up the reflow process by reducing DOM depth, CSS rules and complex selectors; or moving complex rendering (e.g. animations) out of the flow using absolute or fixed position for the element.

Friday, September 13, 2019

Navigator

Browsers and especially Chromium based ones offers a lot of information about the device it's running on in various APIs. They are great if you want to tailor the experience for your users.

Some of such APIs are in Navigator interface, accessible as read-only object from window.navigator property.

navigator.connection: .effectiveType (how fast), .saveData (data saver preference)

navigator.deviceMemory: reducing memory consumption

navigator.hardwareConcurrency: limit CPU intensive logic and effects

Those APIs also could be abused. There's a rumor one Booking site used now deprecated Battery Status API to crank up prices when your Android phone was running low on juice and therefore you probably didn't have much time to think and compare.

Friday, March 1, 2019

JavaScript Framework Mk. II

My next big project is a frontend for enterprise web app. My assignment was to find a JS framework and the original idea was to use Sencha Ext JS, because the other team in our joint-venture uses it, but I also looked into Angular, React and especially Vue.js, which was my personal favorite.

I quickly figured out Sencha is vast, with complete set of widgets, but hard to learn, quite expensive and even one long term user wrote he wouldn't use it for a new project, so in my eyes Sencha was out.

I tried some demos for Vue.js and because it was exactly what I was looking for, I wanted to know how it works inside. I studied some basic Vue internals and virtual DOM and it got me thinking the idea is not that different from how I implemented QetriX Components years ago in PHP.

My big mistake in Mk. I was I relied solely on DOM, so most variables were strings (because input.value returns a string) and I had to convert it to proper data type all the time, which created a messy code. Now all data would be in the model, so variables will always have a correct type.

I already had a JSON definition for layout (nested components) from PHP version of Qedy, so I used it to create a proof-of-concept and it worked quite well. There's no reactive binding magic, all changes are declarative, but this way it should perform better with smaller memory footprint, than real virtual DOM. This is the best excuse I came up with ;-)

DOM is generated by Converters and some parts of it are accessible via internal key-object register called “domLinks”. For example, in a form I can access all the inputs and other fields to get/set their values. I did it this way to simplify object model in a different languages.

Monday, May 28, 2018

JavaScript Array Methods

Arrays in JavaScript are special kind of object, with numeric indexes starting at 0 and going up by 1 to how many items the array has, minus one (because of the zero-based index).

• Mutating / Non-mutating

• Returns Array / returns something else

LIFO (stack)

push(v) – adds an element v at the end of the array (at index length) and returns the new length of the array

pop() – removes the last element and returns the element that was deleted

FIFO (queue)

shift() – returns the first item of the list and deletes the item

push(v) – adds an element v at the end of the array (at index length) and returns the new length of the array

Reverse FIFO

unshift() – adds one or more elements to the beginning of an array and returns the new length of the array

pop() – removes the last element and returns the element that was deleted

Sorting

reverse() - reverses an array: [1,3,2].reverse() => [2,3,1]

sort(f) - returns sorted array according to the function: [1,3,2].sort((a,b) => a-b) => [1,2,3]; [1,3,2].sort((a,b) => b-a) => [3,2,1]

Manipulation

concat() - returns merged arrays: [1,2].concat([3,4]) => [1,2,3,4]

slice()

splice()

Iteration

every(f) – returns true if function returns true on every item: [1,2,3].every(x => x === 2) => false

fill(val,start,end) - returns an array with replaced values between indexes: [1,2,3,4,5].fill(9,1,3) => [1,9,9,4,5]

filter(f) – returns an array of all items for which the function returns true: [1,2,3].filter(x => x < 3) => [1,2]

find(f) - returns item identified (or undefined) in the function: [{a:1},{a:2},{b:3}].find(x => x.a === 2) => {a:2}

findIndex(f) - returns index (or -1) of an item identified in the function: `[{a:1},{a:2},{b:3}].findIndex(x => x.a === 2) => 1

flat() - returns an array of all items even in nested arrays (single level only) as a flat array: 1,[2,3].flat() => [1,[2],3]

flatMap(f) - returns flatten map (like map+flat) with a bug? in flat: [[1,2],3,[4]].flatMap(x => x * 2) => [NaN,9,16]

forEach(f) – no return value, runs the function on every element in the array: [1,2,3].forEach(x => ...).

includes(val) - returns whether the item exists in the array: [1,2,3].includes(4) => false

map(f) – returns a new list with the result of each item in the array: [1,2,3].map(x => x * 2) => [1,4,9]

some(f) – returns true if the function returns true for at least one of the items: [1,2,3].some(x => x === 2) => true

Reduction

reduce(v, a) - returns single value, computed by function from all of the items (SUMIF, kinda): [1,2,3].reduce((total, val) => total * val) => 6

reduceRight()

Thursday, September 28, 2017

JS Performance Tests

There are many great performance test suites out there, but sometimes they're more clumsy than I wish. And because write such test suite isn't hard at all, I did it.

You basically iterate the code few million times and measure time it took. For this all modern browsers support “performance” class with more precise time measurements.

The most basic test suite may look like this:

  1. let results, repeats = 1000000, data = null; function test(name, testFunc) { let startTime = performance.now(); for (let i = 0; i < repeats; i++) testFunc(i); let totalTime = performance.now() - startTime; results.push({"name": name, "value": testFunc(0), "time": totalTime}); }
  2. // Call the test
  3. test("isNaN", function () { return !isNaN("abc"); });
  4. // Show results in the console
  5. results.sort(function (a, b) { return a.time - b.time; });
  6. console.log(results);

For convenience I added many GUI features, like it creates a list of all tests and outputs the results into a page (DOM), rather than just a console.

Sunday, May 28, 2017

Document fragments

Many JavaScript developers know very well document.createElement, fewer of them know document.createTextNode but AFAIK very few know document.createDocumentFragment. And I'd say it's rather pity, it could be very useful!

Fragments are like lightweight elements, that never goes into the DOM. Because of this they're faster, than working directly with DOM. The magic of fragments is in parentNode.appendChild(fragment), because it doesn't append the fragment, but all it's children. They move into the new parent, so after appendChild the fragment becomes empty.

It's basically a single container for multiple DOM elements, so if you want to output 0-N elements from your function, you simply append them to a fragment and return the fragment instead.

There's is a caveat though. If you want to keep a reference to the fragment, you need to use parent.appendChild(fragment.cloneNode(true)), which keeps all elements in the fragment and it even appends children faster (so called “cached mode”), BUT! if you had some event listeners attached to those nested elements inside your fragment, the cloneNode method won't clone them and therefore they disappear.

There's no good way around it, because with JavaScript you can't extract events from an element. The only way is to attach events after the clone using various approaches, most of which aren't much practical.

Monday, March 9, 2015

JavaScript performance

Do not combine getting and setting visual styles, because it will cause browser to reflow layout many times. You should get all styles first in a batch and then set all styles in a batch.

Do not use for..in, because it's slower, than regular for loop.

Use objects as fast lookup hashtables for either DOM objects, or nested objects.

Take advantage of reference types: arrays, objects, dates. It's faster to pass a DOM reference. Comparing object references is also faster.

Use switch(true) if: if: instead of if-elseif-else statements. Also use === instead of == for faster typed comparisons, also use instanceof for type checking.

Numbers are stored with 8 bytes (double-precision 64-bit binary format IEEE 754 value), strings are 2 bytes per character (set of 16-bit unsinged integer values) a booleans are (surprisingly) stored with 4 bytes, because of C++ implementation using char type for booleans.