Edit in JSFiddle

_.mixin({
  /**
   * Fill a destination array with values from source.
   *
   * Useful when you need to mutate an existing array, i.e. without using
   * `.concat`.
   *
   * @param {Array} destination The array to copy values to.
   * @param {Array} source The array to copy values from.
   * @example
   *
   * _.fill([1], [2, 3]);
   * // => [1, 2, 3]
   */
  fill: function(destination, source) {
    _.each(source, function(value) {
      destination.push(value);
    });

    return destination;
  },

  /**
   * Batches an array into `n` sized chunks.
   *
   * @param {Array} items The values to batch.
   * @param {number} batchSize The size of chunks to return.
   * @example
   *
   * _.batch([1, 2, 3, 4, 5, 6, 7, 8], 3);
   * // => [[1, 2, 3], [4, 5, 6], [7, 8]]
   */
  batch: function(items, batchSize) {
    if (batchSize <= 0) {
      throw new Exception('batch must be > 0 in _.batch');
    }
    var batched = [];

    while (items.length) {
      batched.push(items.splice(0, Math.ceil(batchSize)));
    }

    return batched;
  },

  /**
   * Logs its arguments and returns it's first argument.
   */
  log: function (returnValue) {
    var console = window.console;
    console.log.apply(console, arguments);
    return returnValue;
  },

  /**
   * Returns a function that negates the function passed.
   *
   * @example
   *
   * var even = function(n) { return n % 2 == 0; };
   * var odd = _.not(even);
   * odd(2) // => false;
   * _.filter([{a: 1}, {a:1, b:2}, {b:2}], _.not('a')); // => {b:2}
   */
  not: function() {
    var cb = _.createCallback.apply(_, arguments);
    return function() {
      return !cb.apply(null, arguments);
    };
  },

  /**
   * Returns a number clamped between a minimum and maximum value.
   * If the maximum isn't provided, only clamps from the bottom.
   *
   * @param {number} value The value to clamp.
   * @param {number} minimum The minimum value.
   * @param {number?} maximum The maximum value.
   * @returns {number} A value between minimum and maximum.
   *
   * @example
   *
   * _.clamp(4, 2, 6); // => 4
   * _.clamp(2, 3, 5); // => 3
   * _.clamp(7, 2); // => 7
   * _.clamp(7, 2, 5); // => 5
   */
  clamp: function(value, minimum, maximum) {
    if (maximum == null) {
      maximum = Number.MAX_VALUE;
    }
    if (maximum < minimum) {
      var swap = maximum;
      maximum = minimum;
      minimum = swap;
    }
    return Math.max(minimum, Math.min(value, maximum));
  }
});

describe('_.fill', function() {
   it('fills the destination array', function () {
      var destination = [1];
      var source = [2, 3];
      var returnValue = _.fill(destination, source);
      expect(source).toEqual([2, 3]);
      expect(destination).toEqual([1, 2, 3]);
      expect(returnValue).toBe(destination);
   }); 
   it('works the same as concat', function() {
     expect(function(destination, source) {
       var expected = destination.concat(source);
         _.fill(destination, source);
           return _.isEqual(destination, expected);
     }).forAll(qc.array, qc.array);
   });
});
describe('_.batch', function () {
  it('chunks are never greater than the limit', function () {
    expect(function(list, limit) {
      if (limit > 0) {
        var batched = _.batch(list, limit);
        return _.all(batched, function(batch) {
          return batch.length <= limit;
        });
      }
    }).forAll(qc.array, qc.ureal.large);
  });
});
describe('_.log', function () {
  beforeEach(function () {
    spyOn(console, 'log');
  });

  it('logs all it\'s arguments', function () {
    _.log(1, 2, 3);
    expect(console.log).toHaveBeenCalledWith(1, 2, 3);
  });

  it('returns it\'s first argument', function() {
    var returnValue = _.log(1, 2, 3);
    expect(returnValue).toBe(1);
  });

  it('is chainable', function () {
    _([1, 2, 3]).map(function(n) { return n * 2; }).log();
    expect(console.log).toHaveBeenCalledWith([2, 4, 6]);
  });
});
describe('_.not', function () {
  it('negates a function', function () {
    var even = function(n) { return n % 2 === 0; };
    var odd = _.not(even);
    expect(odd(3)).toBe(true);
  });

  it('can find falsey attributes', function () {
    var obj = {a: 3, b: false};
    expect(_.not('a')(obj)).toBe(false);
    expect(_.not('b')(obj)).toBe(true);
    expect(_.not('c')(obj)).toBe(true);
  });

  it('can select negative conditions on objects', function () {
    var obj = {a: 3, b: 6};
    expect(_.not({a: 3})(obj)).toBe(false);
    expect(_.not({a: 4})(obj)).toBe(true);
    expect(_.not({a: 4, b: 6})(obj)).toBe(true);
  });

  it('negates itself', function() {
    expect(function(fn, value) {
      return fn(value) === _.not(_.not(fn))(value);
    }).forAll(qc.function(qc.bool), qc.any);
  });
});
describe('_.clamp', function() {
  it('clamps values', function() {
    expect(function(a,b,c) {
      var v = _.clamp(a, b, c);
      return b <= c && v >= b && v <= c || b > c && v <= b && v >= c;
    }).forAll(qc.real, qc.real, qc.real);
  });
  it('doesn\'t clamp if c is undefined', function() {
    expect(function(a,b,c) {
      return _.clamp(a, b, c) === a || _.clamp(a, b, c) === b;
    }).forAll(qc.real, qc.real, _.constant(undefined));
  });
});