One way to think about it is that the this keyword in JavaScript is not the reference to the object where it is defined, but a reference to the current invoker – the object that is doing the invoking. Consider your example:
var counter = {counter: 0; increment: function() { this.counter++; } };
If we were to construct another object that has a similar form, we can "borrow" the increment function:
var startAt100 = {counter:100, increment: counter.increment }
startAt100.increment()
// startAt100.counter == 101
Here, because the startAt100 object is doing the invoking, then inside the function this refers to startAt100.
One way to get around this is to use the bind() function. The bind() function takes a function and returns a version of that function where this always points at the same invoker. 
  Note: bind() also has the ability to perform partial applications of function parameters, which can also be useful.
So, we can create a bound function of increment by doing:
var boundIncrement = counter.increment.bind(counter);
Now, when we invoke boundIncrement() – no matter whom the actual invoker is – this inside the function will point at the counter object.
boundIncrement() // counter.counter == 1
call(boundIncrement) // counter.counter == 2
Understanding bind()
To understand bind() in a little more detail, we can implement a bind function purely in javascript. It would look something like this:
function bind(fn, invoker) {
  return function() {
    return fn.apply(invoker, arguments);
  }
}
Binding returns a function that will invoke the supplied fn in a specific context, and always in that context.
  Note: once a function has been bound to a context/invoker, you cannot "undo" that binding. Using .call() or .apply() on a bound function will not change this.
Another approach: Object construction
Another approach would be to change the way that you are constructing your counter object. We can use an anonymous function to create a stable scope, then create a closure that references a variable in that scope. By doing this, rather than using this, you can create a function that has a stable environment to execute in. Consider the following code:
var counter = (function() {
  var state = { counter: 0 }
  state.increment = function() { state.counter += 1; };
  return state;
})();
counter.increment() // counter.counter == 1
call(counter.increment); // counter.counter == 2
Now when you invoke counter.increment(), because state has been when the function was created, will always point at, and mutate, that specific object.
Not that this is better or worse, just a different way to understand the same underlying concept of how functions work in JavaScript.