Skip to content

Commit a6391d4

Browse files
authored
Use string concatenation to increase performance (#336)
1 parent 6d4d634 commit a6391d4

File tree

5 files changed

+120
-63
lines changed

5 files changed

+120
-63
lines changed

benchmarks/benchmarks.js

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { Bench } from 'tinybench';
2+
import { markdownTable } from 'markdown-table';
23

34
import local from 'classnames-local';
5+
import bind from 'classnames-local/bind.js';
46
import dedupe from 'classnames-local/dedupe.js';
57
import localPackage from 'classnames-local/package.json' with { type: 'json' };
68

79
import npm from 'classnames-npm';
810
import npmDedupe from 'classnames-npm/dedupe.js';
11+
import npmBind from 'classnames-npm/bind.js';
912
import npmPackage from 'classnames-npm/package.json' with { type: 'json' };
1013

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

4144
export async function runBenchmarks () {
4245
for (const benchmark of benchmarks) {
43-
console.log(`Benchmarking '${benchmark.description}'.`);
46+
console.log(`Benchmarking '${benchmark.description}'.\n`);
4447
const bench = await runBenchmark(benchmark);
45-
console.table(bench.table());
48+
printTable(bench);
4649
}
4750

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

54-
bench.add(`local#${benchmark.description}`, () => {
55-
local(...benchmark.args);
56-
});
57+
bench.add('default/local', () => local(...benchmark.args));
58+
bench.add('default/npm', () => npm(...benchmark.args));
5759

58-
bench.add(`npm#${benchmark.description}`, () => {
59-
npm(...benchmark.args);
60-
});
60+
bench.add('bind/local', () => bind(...benchmark.args));
61+
bench.add('bind/npm', () => npmBind(...benchmark.args));
6162

62-
bench.add(`local/dedupe#${benchmark.description}`, () => {
63-
dedupe(...benchmark.args);
64-
});
65-
66-
bench.add(`npm/dedupe#${benchmark.description}`, () => {
67-
npmDedupe(...benchmark.args);
68-
});
63+
bench.add('dedupe/local', () => dedupe(...benchmark.args));
64+
bench.add('dedupe/npm', () => npmDedupe(...benchmark.args));
6965

7066
await bench.run();
7167
return bench;
7268
}
69+
70+
function printTable(bench) {
71+
const table = bench.table();
72+
const headers = Object.keys(table[0]);
73+
const data = table.map((entry) => headers.map((header) => entry[header]));
74+
75+
console.log(markdownTable([headers, ...data]) + '\n');
76+
}

benchmarks/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"classnames-local": "file:../",
1313
"classnames-npm": "npm:classnames@*",
1414
"http-server": "^14.1.1",
15+
"markdown-table": "^3.0.3",
1516
"rollup": "^4.9.1",
1617
"tinybench": "^2.5.1"
1718
}

bind.js

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,56 @@
1111
var hasOwn = {}.hasOwnProperty;
1212

1313
function classNames () {
14-
var classes = [];
14+
var classes = '';
1515

1616
for (var i = 0; i < arguments.length; i++) {
1717
var arg = arguments[i];
18-
if (!arg) continue;
19-
20-
var argType = typeof arg;
21-
22-
if (argType === 'string' || argType === 'number') {
23-
classes.push(this && this[arg] || arg);
24-
} else if (Array.isArray(arg)) {
25-
classes.push(classNames.apply(this, arg));
26-
} else if (argType === 'object') {
27-
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
28-
classes.push(arg.toString());
29-
continue;
30-
}
31-
32-
for (var key in arg) {
33-
if (hasOwn.call(arg, key) && arg[key]) {
34-
classes.push(this && this[key] || key);
35-
}
36-
}
18+
if (arg) {
19+
classes = appendClass(classes, parseValue.call(this, arg));
3720
}
3821
}
3922

40-
return classes.join(' ');
23+
return classes;
24+
}
25+
26+
function parseValue (arg) {
27+
if (typeof arg === 'string' || typeof arg === 'number') {
28+
return this && this[arg] || arg;
29+
}
30+
31+
if (typeof arg !== 'object') {
32+
return '';
33+
}
34+
35+
if (Array.isArray(arg)) {
36+
return classNames.apply(this, arg);
37+
}
38+
39+
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
40+
return arg.toString();
41+
}
42+
43+
var classes = '';
44+
45+
for (var key in arg) {
46+
if (hasOwn.call(arg, key) && arg[key]) {
47+
classes = appendClass(classes, this && this[key] || key);
48+
}
49+
}
50+
51+
return classes;
52+
}
53+
54+
function appendClass (value, newClass) {
55+
if (!newClass) {
56+
return value;
57+
}
58+
59+
if (value) {
60+
return value + ' ' + newClass;
61+
}
62+
63+
return value + newClass;
4164
}
4265

4366
if (typeof module !== 'undefined' && module.exports) {

index.js

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,57 @@
1010

1111
var hasOwn = {}.hasOwnProperty;
1212

13-
function classNames() {
14-
var classes = [];
13+
function classNames () {
14+
var classes = '';
1515

1616
for (var i = 0; i < arguments.length; i++) {
1717
var arg = arguments[i];
18-
if (!arg) continue;
19-
20-
var argType = typeof arg;
21-
22-
if (argType === 'string' || argType === 'number') {
23-
classes.push(arg);
24-
} else if (Array.isArray(arg)) {
25-
if (arg.length) {
26-
var inner = classNames.apply(null, arg);
27-
if (inner) {
28-
classes.push(inner);
29-
}
30-
}
31-
} else if (argType === 'object') {
32-
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
33-
classes.push(arg.toString());
34-
continue;
35-
}
36-
37-
for (var key in arg) {
38-
if (hasOwn.call(arg, key) && arg[key]) {
39-
classes.push(key);
40-
}
41-
}
18+
if (arg) {
19+
classes = appendClass(classes, parseValue(arg));
4220
}
4321
}
4422

45-
return classes.join(' ');
23+
return classes;
24+
}
25+
26+
function parseValue (arg) {
27+
if (typeof arg === 'string' || typeof arg === 'number') {
28+
return arg;
29+
}
30+
31+
if (typeof arg !== 'object') {
32+
return '';
33+
}
34+
35+
if (Array.isArray(arg)) {
36+
return classNames.apply(null, arg);
37+
}
38+
39+
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
40+
return arg.toString();
41+
}
42+
43+
var classes = '';
44+
45+
for (var key in arg) {
46+
if (hasOwn.call(arg, key) && arg[key]) {
47+
classes = appendClass(classes, key);
48+
}
49+
}
50+
51+
return classes;
52+
}
53+
54+
function appendClass (value, newClass) {
55+
if (!newClass) {
56+
return value;
57+
}
58+
59+
if (value) {
60+
return value + ' ' + newClass;
61+
}
62+
63+
return value + newClass;
4664
}
4765

4866
if (typeof module !== 'undefined' && module.exports) {

package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)