• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3/* eslint no-invalid-this: 1 */
4
5var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
6var toStr = Object.prototype.toString;
7var max = Math.max;
8var funcType = '[object Function]';
9
10var concatty = function concatty(a, b) {
11    var arr = [];
12
13    for (var i = 0; i < a.length; i += 1) {
14        arr[i] = a[i];
15    }
16    for (var j = 0; j < b.length; j += 1) {
17        arr[j + a.length] = b[j];
18    }
19
20    return arr;
21};
22
23var slicy = function slicy(arrLike, offset) {
24    var arr = [];
25    for (var i = offset || 0, j = 0; i < arrLike.length; i += 1, j += 1) {
26        arr[j] = arrLike[i];
27    }
28    return arr;
29};
30
31var joiny = function (arr, joiner) {
32    var str = '';
33    for (var i = 0; i < arr.length; i += 1) {
34        str += arr[i];
35        if (i + 1 < arr.length) {
36            str += joiner;
37        }
38    }
39    return str;
40};
41
42module.exports = function bind(that) {
43    var target = this;
44    if (typeof target !== 'function' || toStr.apply(target) !== funcType) {
45        throw new TypeError(ERROR_MESSAGE + target);
46    }
47    var args = slicy(arguments, 1);
48
49    var bound;
50    var binder = function () {
51        if (this instanceof bound) {
52            var result = target.apply(
53                this,
54                concatty(args, arguments)
55            );
56            if (Object(result) === result) {
57                return result;
58            }
59            return this;
60        }
61        return target.apply(
62            that,
63            concatty(args, arguments)
64        );
65
66    };
67
68    var boundLength = max(0, target.length - args.length);
69    var boundArgs = [];
70    for (var i = 0; i < boundLength; i++) {
71        boundArgs[i] = '$' + i;
72    }
73
74    bound = Function('binder', 'return function (' + joiny(boundArgs, ',') + '){ return binder.apply(this,arguments); }')(binder);
75
76    if (target.prototype) {
77        var Empty = function Empty() {};
78        Empty.prototype = target.prototype;
79        bound.prototype = new Empty();
80        Empty.prototype = null;
81    }
82
83    return bound;
84};
85