Skip to content

Commit

Permalink
[Fix] preserve original function’s length when possible
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Jan 9, 2021
1 parent 72d9f44 commit adbceaa
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@

"rules": {
"func-name-matching": 0,
"id-length": 0,
"new-cap": [2, {
"capIsNewExceptions": [
"GetIntrinsic",
],
}],
"no-magic-numbers": 0,
"operator-linebreak": [2, "before"],
},
}
17 changes: 15 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ var $apply = GetIntrinsic('%Function.prototype.apply%');
var $call = GetIntrinsic('%Function.prototype.call%');
var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply);

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true);
var $defineProperty = GetIntrinsic('%Object.defineProperty%', true);
var $max = GetIntrinsic('%Math.max%');

if ($defineProperty) {
try {
Expand All @@ -18,8 +20,19 @@ if ($defineProperty) {
}
}

module.exports = function callBind() {
return $reflectApply(bind, $call, arguments);
module.exports = function callBind(originalFunction) {
var func = $reflectApply(bind, $call, arguments);
if ($gOPD && $defineProperty) {
var desc = $gOPD(func, 'length');
if (desc.configurable) {
$defineProperty(
func,
'length',
{ value: $max(0, originalFunction.length - (arguments.length - 1)) }
);
}
}
return func;
};

var applyBind = function applyBind() {
Expand Down
9 changes: 9 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,36 @@ var callBind = require('../');

var test = require('tape');

var functionsHaveConfigurableLengths = !!(
Object.getOwnPropertyDescriptor
&& Object.getOwnPropertyDescriptor(function () {}, 'length').configurable
);

test('callBind', function (t) {
var sentinel = { sentinel: true };
var func = function (a, b) {
// eslint-disable-next-line no-invalid-this
return [this, a, b];
};
t.equal(func.length, 2, 'original function length is 2');
t.deepEqual(func(), [undefined, undefined, undefined], 'unbound func with too few args');
t.deepEqual(func(1, 2), [undefined, 1, 2], 'unbound func with right args');
t.deepEqual(func(1, 2, 3), [undefined, 1, 2], 'unbound func with too many args');

var bound = callBind(func);
t.equal(bound.length, func.length, 'function length is preserved', { skip: !functionsHaveConfigurableLengths });
t.deepEqual(bound(), [undefined, undefined, undefined], 'bound func with too few args');
t.deepEqual(bound(1, 2), [1, 2, undefined], 'bound func with right args');
t.deepEqual(bound(1, 2, 3), [1, 2, 3], 'bound func with too many args');

var boundR = callBind(func, sentinel);
t.equal(boundR.length, func.length - 1, 'function length is preserved', { skip: !functionsHaveConfigurableLengths });
t.deepEqual(boundR(), [sentinel, undefined, undefined], 'bound func with receiver, with too few args');
t.deepEqual(boundR(1, 2), [sentinel, 1, 2], 'bound func with receiver, with right args');
t.deepEqual(boundR(1, 2, 3), [sentinel, 1, 2], 'bound func with receiver, with too many args');

var boundArg = callBind(func, sentinel, 1);
t.equal(boundArg.length, func.length - 2, 'function length is preserved', { skip: !functionsHaveConfigurableLengths });
t.deepEqual(boundArg(), [sentinel, 1, undefined], 'bound func with receiver and arg, with too few args');
t.deepEqual(boundArg(2), [sentinel, 1, 2], 'bound func with receiver and arg, with right arg');
t.deepEqual(boundArg(2, 3), [sentinel, 1, 2], 'bound func with receiver and arg, with too many args');
Expand Down

3 comments on commit adbceaa

@ExE-Boss
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguably, the callBinded function should have length === orig.length + 1, since the this parameter was uncurried.

@ljharb
Copy link
Owner Author

@ljharb ljharb commented on adbceaa Jan 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, that's a really good point. i'll fix that.

@ljharb
Copy link
Owner Author

@ljharb ljharb commented on adbceaa Jan 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in dbae7bc and released in v1.0.2, thanks!

Please sign in to comment.