Post

JavaScript Tips & Tricks 2

In this post we’ll look at some more useful tips and tricks when using JavaScript.

  1. Copying to Clipboard in the Browser
  2. Object Functions
  3. Numeric Separators
  4. Displaying Units
  5. Quoteless XSS Payloads
  6. Object Freezing
  7. Binding

Copying to Clipboard in the Browser

1
navigator.clipboard.writeText('Value To Copy to Clipboard');
  • An error can occur Uncaught (in promise) DOMException: Clipboard write was blocked due to lack of user activation. if the user is not focused on the browser tab when this command is run.

Object Functions

1
2
3
4
5
let testObject = {name: "Alex", city: "Melbourne", age: 200};
  
console.log(Object.keys(testObject)); // ["name", "city", "age"]
console.log(Object.values(testObject)); // ["Alex", "Melbourne", 200]
console.log(Object.entries(testObject)); // [["name", "Alex"], ["city", "Melbourne"], ["age", 2000]]
  • Object.key(inputObject) returns a Array of the keys in the Object.
  • Object.values(inputObject) returns a Array of the values in the Object.
  • Object.entries(inputObject) returns a Array of Arrays, where each inner Array contains two elements (the key and the value).


Numeric Separators

1
2
let numberBad = 10000000000; // HARD to read
let numberGood = 10_000_000_000; // EASY to read
1
let floatValue = 25_832_834.451;
1
2
let hexValue = 0xff_ff_ff; // 16777215 in Base10
let binaryValue = 0b1000_0001; // 129 in Base10
  • This is great for static values in your code as it can greatly improve readability.


Displaying Units

A situation that most front-end developers will face is formatting numbers. Say we have a variable containing the value 124685.019 and want it formatted nicely with a separator as 124,685.019, or maybe as Australian dollars, or as Spanish Euros. Normally this would mean looking for an NPM module that does this. Fortunately, JavaScript has built in methods to do such operations:

Formatting Numbers

1
2
3
4
5
6
7
let number = 124685.019;

let prettyNumberAustralia = new Intl.NumberFormat('en-AU').format(number);
let prettyNumberIndia = new Intl.NumberFormat('en-IN').format(number);

console.log(prettyNumberAustralia); // 124,685.019
console.log(prettyNumberIndia); // 1,24,685.019

Formatting Currencies

1
2
3
4
5
6
7
let amount = 124685.019;

let prettyAmountAustralianDollars = new Intl.NumberFormat('en-AU', { style: 'currency', currency: 'AUD' }).format(amount);
let prettyAmountSpanishEuros = new Intl.NumberFormat('es-ES', { style: 'currency', currency: 'EUR' }).format(amount);

console.log(prettyAmountAustralianDollars); // $124,685.02
console.log(prettyAmountSpanishEuros); // 124.685,02 €

Formatting Units

1
2
3
4
5
6
7
8
9
10
11
let value = 109.3;

let prettyUnitCelsius = new Intl.NumberFormat('en-US', { style: 'unit', unit: 'celsius' }).format(value);
let prettyUnitMPH = new Intl.NumberFormat('en-US', { style: 'unit', unit: 'mile-per-hour' }).format(value);
let prettyUnitKMHShort = new Intl.NumberFormat('en-AU', { style: 'unit', unit: 'kilometer-per-hour' }).format(value);
let prettyUnitKMHLong = new Intl.NumberFormat('en-AU', { style: 'unit', unit: 'kilometer-per-hour', unitDisplay: 'long' }).format(value);

console.log(prettyUnitCelsius); // 109.3°C
console.log(prettyUnitMPH); // 109.3 mph
console.log(prettyUnitKMHShort); // 109.3 km/h
console.log(prettyUnitKMHLong); // 109.3 kilometres per hour


Quoteless XSS Payloads

Compact Version:

1
2
3
4
5
6
7
8
9
10
let payload = 'alert(1)';
let evalStatement;

// Method 1
evalStatement = "eval(String.fromCharCode("+payload.charCodeAt(0);for (let i=1;i<payload.length;i++) evalStatement += ","+payload.charCodeAt(i);evalStatement+="))";

// Method 2
evalStatement = "eval(String.fromCharCode("+payload.split('').map(x => x.charCodeAt()).join(',')+"))";

// evalStatement = "eval(String.fromCharCode(97,108,101,114,116,40,49,41))"

Expanded Version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let payload = 'alert(1)';

// Method 1
let evalStatement = 'eval(String.fromCharCode(';   
evalStatement += payload.charCodeAt(0);   

for (let i = 1; i < payload.length; i++) {
	evalStatement += ',' + payload.charCodeAt(i);
}  

evalStatement += '))';

// Method 2
evalStatement = 'eval(String.fromCharCode(';
evalStatement += payload.split('').map(char => char.charCodeAt()).join(',');
evalStatement += '))';


Object Freezing

Freezing

If you create a const variable and store an Object in it, the keys and values are still modifiable:

1
2
3
4
5
6
7
8
const dataObject = {
	username: 'alex',
	password: 'SomethingSecure'
};

dataObject.password = '';

console.log(dataObject); // {username: "alex", password: ""}

Using freeze will prevent this from happening. However, freeze won’t stop child-objects being updated:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const dataObject = {
	username: 'alex',
	password: 'SomethingSecure',
	address: {
		country: 'Australia',
		city: 'Melbourne'
	}
};

Object.freeze(dataObject);

// This WON'T work
dataObject.password = '';
dataObject.anotherKey = 'test';

// This WILL work
dataObject.address.city = 'Sydney';

// {username: "alex", password: "SomethingSecure", address: {country: "Australia", city: "Sydney"}}
console.log(dataObject);

Deep Freezing

To fully freeze an Object we need to “Deep Freeze” it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function DeepFreeze(inputObj) {
	Object.values(inputObj).forEach(currentObjValue => {
		if (typeof(currentObjValue) === 'object') DeepFreeze(currentObjValue);
	});

	Object.freeze(inputObj);
}

const dataObject = {
	username: 'alex',
	password: 'SomethingSecure',
	address: {
		country: 'Australia',
		city: 'Melbourne'
	}
};

DeepFreeze(dataObject);

// This WON'T work
dataObject.password = '';
dataObject.anotherKey = 'test';
dataObject.address.city = 'Sydney';

// {username: "alex", password: "SomethingSecure", address: {country: "Australia", city: "Melbourne"}}
console.log(dataObject);


Binding

Binding allows us to set some parameters of a function before calling it, and it allows us to control what the keyword this refers to inside the function we are calling.

Calling the bind function on a function, returns another function.

1
targetFunction.bind(param1, param2, param3, ...);
  • param1 is what we want the this to refer to. This could be a Object or Class (which is just an Object). If this is not required, the parameter should just be null.
  • param2, param3 and on-wards are the parameters we want to pass to the function targetFunction.

Setting parameters before calling the function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function sumValues(a, b) {
    return a + b;
}

// The first parameter of `plusFive` is now bound as 5.
// The function `plusFive` will only require 1 parameter.
let plusFive = sumValues.bind(null, 5);

console.log(sumValues(10, 10)); // 20
console.log(plusFive(15)); // 20

// Check how many parameters the function takes
console.log(sumValues.length); // 2
console.log(plusFive.length); // 1

Controlling what this refers to

1
2
3
4
5
6
7
8
function sumValues() {
	return this.a + this.b;
}

let dataObject = {a: 100, b: 20};
let sumWithDataObject = sumValues.bind(dataObject);

console.log(sumWithDataObject()); // 120

Both

1
2
3
4
5
6
7
8
function sumValues(c, d, e) {
	return this.a + this.b + c + d + e;
}

let dataObject = {a: 100, b: 20};
let sumValuesWithDataObject = sumValues.bind(dataObject, 1, 5);

console.log(sumValuesWithDataObject(10)); // 136


More Info

More information regarding the above tips can be found here:

This post is licensed under CC BY 4.0 by the author.