Skip to content

Commit

Permalink
Use string concatenation to increase performance (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonkoops authored Dec 26, 2023
1 parent 6d4d634 commit a6391d4
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 63 deletions.
34 changes: 19 additions & 15 deletions benchmarks/benchmarks.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Bench } from 'tinybench';
import { markdownTable } from 'markdown-table';

import local from 'classnames-local';
import bind from 'classnames-local/bind.js';
import dedupe from 'classnames-local/dedupe.js';
import localPackage from 'classnames-local/package.json' with { type: 'json' };

import npm from 'classnames-npm';
import npmDedupe from 'classnames-npm/dedupe.js';
import npmBind from 'classnames-npm/bind.js';
import npmPackage from 'classnames-npm/package.json' with { type: 'json' };

if (localPackage.version !== npmPackage.version) {
Expand Down Expand Up @@ -40,9 +43,9 @@ const benchmarks = [

export async function runBenchmarks () {
for (const benchmark of benchmarks) {
console.log(`Benchmarking '${benchmark.description}'.`);
console.log(`Benchmarking '${benchmark.description}'.\n`);
const bench = await runBenchmark(benchmark);
console.table(bench.table());
printTable(bench);
}

console.log('Finished!');
Expand All @@ -51,22 +54,23 @@ export async function runBenchmarks () {
async function runBenchmark (benchmark) {
const bench = new Bench();

bench.add(`local#${benchmark.description}`, () => {
local(...benchmark.args);
});
bench.add('default/local', () => local(...benchmark.args));
bench.add('default/npm', () => npm(...benchmark.args));

bench.add(`npm#${benchmark.description}`, () => {
npm(...benchmark.args);
});
bench.add('bind/local', () => bind(...benchmark.args));
bench.add('bind/npm', () => npmBind(...benchmark.args));

bench.add(`local/dedupe#${benchmark.description}`, () => {
dedupe(...benchmark.args);
});

bench.add(`npm/dedupe#${benchmark.description}`, () => {
npmDedupe(...benchmark.args);
});
bench.add('dedupe/local', () => dedupe(...benchmark.args));
bench.add('dedupe/npm', () => npmDedupe(...benchmark.args));

await bench.run();
return bench;
}

function printTable(bench) {
const table = bench.table();
const headers = Object.keys(table[0]);
const data = table.map((entry) => headers.map((header) => entry[header]));

console.log(markdownTable([headers, ...data]) + '\n');
}
1 change: 1 addition & 0 deletions benchmarks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"classnames-local": "file:../",
"classnames-npm": "npm:classnames@*",
"http-server": "^14.1.1",
"markdown-table": "^3.0.3",
"rollup": "^4.9.1",
"tinybench": "^2.5.1"
}
Expand Down
65 changes: 44 additions & 21 deletions bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,56 @@
var hasOwn = {}.hasOwnProperty;

function classNames () {
var classes = [];
var classes = '';

for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (!arg) continue;

var argType = typeof arg;

if (argType === 'string' || argType === 'number') {
classes.push(this && this[arg] || arg);
} else if (Array.isArray(arg)) {
classes.push(classNames.apply(this, arg));
} else if (argType === 'object') {
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
classes.push(arg.toString());
continue;
}

for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes.push(this && this[key] || key);
}
}
if (arg) {
classes = appendClass(classes, parseValue.call(this, arg));
}
}

return classes.join(' ');
return classes;
}

function parseValue (arg) {
if (typeof arg === 'string' || typeof arg === 'number') {
return this && this[arg] || arg;
}

if (typeof arg !== 'object') {
return '';
}

if (Array.isArray(arg)) {
return classNames.apply(this, arg);
}

if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
return arg.toString();
}

var classes = '';

for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes = appendClass(classes, this && this[key] || key);
}
}

return classes;
}

function appendClass (value, newClass) {
if (!newClass) {
return value;
}

if (value) {
return value + ' ' + newClass;
}

return value + newClass;
}

if (typeof module !== 'undefined' && module.exports) {
Expand Down
72 changes: 45 additions & 27 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,57 @@

var hasOwn = {}.hasOwnProperty;

function classNames() {
var classes = [];
function classNames () {
var classes = '';

for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (!arg) continue;

var argType = typeof arg;

if (argType === 'string' || argType === 'number') {
classes.push(arg);
} else if (Array.isArray(arg)) {
if (arg.length) {
var inner = classNames.apply(null, arg);
if (inner) {
classes.push(inner);
}
}
} else if (argType === 'object') {
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
classes.push(arg.toString());
continue;
}

for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes.push(key);
}
}
if (arg) {
classes = appendClass(classes, parseValue(arg));
}
}

return classes.join(' ');
return classes;
}

function parseValue (arg) {
if (typeof arg === 'string' || typeof arg === 'number') {
return arg;
}

if (typeof arg !== 'object') {
return '';
}

if (Array.isArray(arg)) {
return classNames.apply(null, arg);
}

if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
return arg.toString();
}

var classes = '';

for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes = appendClass(classes, key);
}
}

return classes;
}

function appendClass (value, newClass) {
if (!newClass) {
return value;
}

if (value) {
return value + ' ' + newClass;
}

return value + newClass;
}

if (typeof module !== 'undefined' && module.exports) {
Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a6391d4

Please sign in to comment.