In this post we’ll look at some more useful tips and tricks when using JavaScript.
- Copying to Clipboard in the Browser
- Object Functions
- Numeric Separators
- Displaying Units
- Quoteless XSS Payloads
- Object Freezing
- 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:
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
|
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 €
|
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: