Install
openclaw skills install javascript-skillsA comprehensive JavaScript style guide skill. When activated, it provides best-practice JavaScript coding conventions and generates code that strictly follows the style guide, covering variables, functions, objects, arrays, classes, modules, async patterns, error handling, naming conventions, and more.
openclaw skills install javascript-skillsThis skill activates when the user mentions or implies JavaScript in their request. Once activated, it:
const for all references; avoid var.let instead of var.const and let are block-scoped, whereas var is function-scoped.// bad
var count = 1;
// good
const count = 1;
let mutableValue = 1;
mutableValue += 1;
...) over Object.assign to shallow-copy objects.// bad
const item = new Object();
// good
const item = {};
// computed property names
const key = 'name';
const obj = { [key]: 'value' };
// method & property shorthand
const name = 'Alice';
const atom = {
name,
value: 1,
addValue(val) {
return atom.value + val;
},
};
// shallow copy
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 };
Array.from or the spread operator to convert array-like objects.return statements in array method callbacks.// bad
const items = new Array();
// good
const items = [];
// convert iterable
const nodes = Array.from(document.querySelectorAll('.item'));
const uniqueValues = [...new Set(arr)];
// array methods
[1, 2, 3].map((x) => x + 1);
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
// array destructuring
const [first, , third] = [1, 2, 3];
// multiple return values — use object destructuring
function processInput(input) {
return { left, right, top, bottom };
}
const { left, top } = processInput(input);
'' for strings.eval() on a string.// bad
const name = "Alice";
const greeting = 'Hello, ' + name + '!';
// good
const name = 'Alice';
const greeting = `Hello, ${name}!`;
if, while, etc.).arguments.Function constructor to create a new function.... to call variadic functions....args) instead of arguments.// named function expression
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
// default parameters last
function handleThings(name, opts = {}) {
// ...
}
// rest parameters
function concatenateAll(...args) {
return args.join('');
}
// spread to call
const values = [1, 2, 3];
console.log(Math.max(...values));
// bad
[1, 2, 3].map(function (x) {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// implicit return
[1, 2, 3].map((x) => x * 2);
// multiline implicit return
[1, 2, 3].map((number) => (
`A long string with the ${number}. It's so long that we don't want it to take up space on the .map line!`
));
class; avoid manipulating prototype directly.extends for inheritance.this to enable method chaining.// bad
function Queue(contents = []) {
this.queue = [...contents];
}
Queue.prototype.pop = function () {
return this.queue.pop();
};
// good
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
return this.queue.pop();
}
}
// inheritance
class PeekableQueue extends Queue {
peek() {
return this.queue[0];
}
}
import/export) over a non-standard module system.imports above non-import statements.// bad
const utils = require('./utils');
module.exports = utils.fetchData;
// good
import { fetchData } from './utils';
export default fetchData;
// single place import
import { named1, named2 } from 'module';
// multiline
import {
longNameA,
longNameB,
longNameC,
} from 'path/to/module';
for-in or for-of loops.map, filter, reduce, find, findIndex, every, some, etc.const numbers = [1, 2, 3, 4, 5];
// bad
let sum = 0;
for (const num of numbers) {
sum += num;
}
// good
const sum = numbers.reduce((total, num) => total + num, 0);
// filtering
const evens = numbers.filter((num) => num % 2 === 0);
[] when accessing properties with a variable.** instead of Math.pow.const luke = { jedi: true, age: 28 };
// dot notation
const isJedi = luke.jedi;
// bracket notation
const prop = 'jedi';
const isJedi = luke[prop];
// exponentiation
const result = 2 ** 10;
const or let; never use var.const or let declaration per variable or assignment.consts and then group all lets.++, --); use += 1 / -= 1 instead.= in an assignment.// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
// group const then let
const a = 1;
const b = 2;
let c = 3;
let d = 4;
// avoid unary
let count = 0;
count += 1;
var declarations get hoisted; const and let are in Temporal Dead Zone.=== and !== over == and !=.case and default clauses that contain lexical declarations.**, +, -).// bad
if (isValid == true) { /* ... */ }
if (name != '') { /* ... */ }
// good
if (isValid) { /* ... */ }
if (name !== '') { /* ... */ }
if (collection.length > 0) { /* ... */ }
// no nested ternaries
const thing = foo === 123 ? bar : foobar;
else on the same line as the if block's closing brace.if block always executes a return, the subsequent else block is unnecessary.// bad
if (test)
return false;
// good
if (test) return false;
if (test) {
return false;
}
// if/else
if (test) {
thing1();
} else {
thing2();
}
if, while, etc.) gets too long, each grouped condition should be on a new line, with the logical operator at the beginning of the line.if (
foo === 123
&& bar === 'abc'
) {
thing1();
}
/** ... */ for multiline comments.// for single-line comments. Place them on a new line above the subject.FIXME: or TODO: to annotate problems or suggest actions.// good single line
// This is a comment
const active = true;
/**
* Multiline comment explaining the function.
* @param {string} tag - The tag name.
* @returns {Element} The created element.
*/
function make(tag) {
return document.createElement(tag);
}
// TODO: implement caching
// FIXME: should not use global state
// bad
function foo(){
const name='Alice';
}
// good
function foo() {
const name = 'Alice';
}
// method chaining
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
// bad — leading commas
const hero = {
firstName: 'Ada'
, lastName: 'Lovelace'
};
// good — trailing commas
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
};
const heroes = [
'Batman',
'Superman',
];
// bad
const name = 'Alice'
// good
const name = 'Alice';
String() for strings, Number() for numbers, Boolean() or !! for booleans.parseInt always with a radix.const val = '4';
// bad
const totalScore = val + 0;
// good
const totalScore = Number(val);
const inputValue = String(someNum);
const hasAge = Boolean(age);
const hasName = !!name;
const count = parseInt(inputValue, 10);
// bad
const OBJEcttsssss = {};
function c() {}
const u = new user();
// good
const thisIsMyObject = {};
function calculateTotal() {}
const user = new User();
// constants
export const API_BASE_URL = 'https://api.example.com';
export const MAX_RETRY_COUNT = 3;
// filename examples
// file: makeStyleGuide.js → export default function makeStyleGuide() {}
// file: User.js → export default class User {}
getVal() and setVal('value').isVal() or hasVal().// bad
dragon.age();
// good
dragon.getAge();
dragon.setAge(25);
dragon.isAlive();
dragon.hasWings();
// bad
emitter.emit('itemUpdate', item.id);
// good
emitter.emit('itemUpdate', { itemId: item.id });
async/await over chaining .then() and .catch().try...catch in async functions.Promise.all for concurrent independent async operations.Promise.allSettled when you need results of all promises regardless of rejection.// bad
function fetchData() {
return getData()
.then((data) => parseData(data))
.then((parsed) => validate(parsed))
.catch((err) => console.error(err));
}
// good
async function fetchData() {
try {
const data = await getData();
const parsed = await parseData(data);
return validate(parsed);
} catch (err) {
console.error(err);
throw err;
}
}
// concurrent operations
async function loadDashboard() {
const [user, posts, notifications] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchNotifications(),
]);
return { user, posts, notifications };
}
Error objects (or subclasses of Error).// bad
throw 'Something went wrong';
throw { message: 'error' };
// good
throw new Error('Something went wrong');
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
throw new ValidationError('Invalid email', 'email');
?.) to access nested properties that may not exist.??) instead of || when you want to allow falsy values like 0 or ''.// bad
const city = user && user.address && user.address.city;
const port = config.port || 3000; // breaks if port is 0
// good
const city = user?.address?.city;
const port = config.port ?? 3000;
Number.isNaN instead of global isNaN.Number.isFinite instead of global isFinite.// bad
isNaN('1.2');
isFinite('2e3');
// good
Number.isNaN('1.2');
Number.isFinite('2e3');
This is a Prompt-based Skill (natural language activation).
Automatic Activation: Simply mention "JavaScript" in your request. The skill will be activated automatically.
Ask for Style Guidance:
"How should I write a JavaScript function that fetches user data?"
Request Code Review:
"Review this JavaScript code for style issues: [paste code]"
Generate Compliant Code:
"Write a JavaScript module to handle form validation"
Ask About Specific Rules:
"What's the correct way to declare variables in JavaScript?"
"How should I handle async errors in JavaScript?"
User: Write a JavaScript utility function to deep clone an object.
Skill Response: Generates clean, style-guide-compliant code:
/**
* Deep clones a plain object or array using structured cloning.
* Falls back to JSON serialization for environments without structuredClone.
*
* @param {Object|Array} source - The value to clone.
* @returns {Object|Array} A deep copy of the source.
*/
function deepClone(source) {
if (typeof structuredClone === 'function') {
return structuredClone(source);
}
return JSON.parse(JSON.stringify(source));
}
export default deepClone;