Truly private JavaScript variables
As for the code:


The property getter and setter should probably throw an exception if the check against the caller failed.  If you attempt to set it from any function besides one defined on the prototype, you don't want it to silently fail -- if it does, you'll be wasting lots of time trying to figure out why your changes aren't sticking.  (Particularly in light of other issues below.)
If you're going to assign a different key to each member function, you might consider making validFns[key] be the function with that key, rather than just '1', so you can check it against the caller.  (Ensures the key doesn't ever accidentally match.)
You're setting keys on each thing you copy from the prototype, regardless of whether it's a function.  Only functions need keys; other stuff (well, anything really, but particularly non-function members) might have its own purpose for key.
The way the access checks are now, only member functions can use them.  Though that may be the intent, it is way more crippling than it sounds.


Observe:

foo.prototype.doStuff = function() {
    var self = this;
    $('input.some_selector').each(function() {
        self.foo += parseInt(this.value);
    });
};


We're in a private function, which should have access to foo.  But since the actual access is within an anonymous function, and not doStuff, it's not allowed.  In order to fix this (and fix it you should), your access check would need to go down the call stack (check the caller, then the caller's caller, and so on) til it finds a function with a key.  Recursive functions seem to cause such a search massive amounts of trouble, though; a depth limit might help with that.


definePrivateProperties can be run a second time.  And a third, fourth, etc.  And each time you call it, it obliterates the previous property's value in order to add the new one.  Read: external code can mess up your private variables.


Say:

foo.prototype.mangleFoo = function() { this.foo = 100; };
x.definePrivateProperties('foo');
delete foo.prototype.mangleFoo;
x.mangleFoo();


(BTW, see what we just did there?  We can add a function and give it access to the "private" properties pretty much at will.)  In order to prevent that...i dunno.  redefine definePrivateProperties after it's run once?

In contrast, the standard method (hiding the variable away in a closure) is not prone to such hackery.