Skip to content
Snippets Groups Projects
implementation.js 2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jay's avatar
    Jay committed
    'use strict';
    
    /* eslint no-invalid-this: 1 */
    
    var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
    var toStr = Object.prototype.toString;
    var max = Math.max;
    var funcType = '[object Function]';
    
    var concatty = function concatty(a, b) {
        var arr = [];
    
        for (var i = 0; i < a.length; i += 1) {
            arr[i] = a[i];
        }
        for (var j = 0; j < b.length; j += 1) {
            arr[j + a.length] = b[j];
        }
    
        return arr;
    };
    
    var slicy = function slicy(arrLike, offset) {
        var arr = [];
        for (var i = offset || 0, j = 0; i < arrLike.length; i += 1, j += 1) {
            arr[j] = arrLike[i];
        }
        return arr;
    };
    
    var joiny = function (arr, joiner) {
        var str = '';
        for (var i = 0; i < arr.length; i += 1) {
            str += arr[i];
            if (i + 1 < arr.length) {
                str += joiner;
            }
        }
        return str;
    };
    
    module.exports = function bind(that) {
        var target = this;
        if (typeof target !== 'function' || toStr.apply(target) !== funcType) {
            throw new TypeError(ERROR_MESSAGE + target);
        }
        var args = slicy(arguments, 1);
    
        var bound;
        var binder = function () {
            if (this instanceof bound) {
                var result = target.apply(
                    this,
                    concatty(args, arguments)
                );
                if (Object(result) === result) {
                    return result;
                }
                return this;
            }
            return target.apply(
                that,
                concatty(args, arguments)
            );
    
        };
    
        var boundLength = max(0, target.length - args.length);
        var boundArgs = [];
        for (var i = 0; i < boundLength; i++) {
            boundArgs[i] = '$' + i;
        }
    
        bound = Function('binder', 'return function (' + joiny(boundArgs, ',') + '){ return binder.apply(this,arguments); }')(binder);
    
        if (target.prototype) {
            var Empty = function Empty() {};
            Empty.prototype = target.prototype;
            bound.prototype = new Empty();
            Empty.prototype = null;
        }
    
        return bound;
    };