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: