-
-
Save dherman/3194234 to your computer and use it in GitHub Desktop.
Object.beget = (function() { | |
var create = Object.create, | |
defineProperty = Object.defineProperty; | |
// see https://gist.github.com/3194222 | |
var hasOwn = Object.prototype.hasOwnProperty.unselfish(); | |
return function beget(proto, own) { | |
var result = create(proto); | |
var descriptor = { | |
enumerable: true, | |
writable: true, | |
configurable: true | |
}; | |
for (var key in own) { | |
if (!hasOwn(own, key)) | |
continue; | |
descriptor.value = own[key]; | |
defineProperty(result, key, descriptor); | |
} | |
return result; | |
}; | |
})(); | |
var root = { | |
method: function() { return [this.x, this.y] } | |
}; | |
var o = Object.beget(root, { x: 0, y: 22 }); | |
console.log(o.method()); // [0, 22] |
Not sure about hasOwnProperty
, actually. This is one place where the name and argument order may actually affect the intended semantics: a clone
method probably leads people to expect that the object to clone could be anything, which might have enumerable properties anywhere up its prototype inheritance chain. Whereas the original API I described was expected just to be an object literal (and therefore any inherited enumerable properties would be a result of naughty Object.prototype
extensions).
Dave
Take a look at my little protochains lib I was experimenting with. I had the same idea about adding a third param for extra properties. It has several other things I've been playing with too.
https://github.com/polotek/procstreams/blob/master/protochains.js
I don't think people will expect the complicated version of clone. I would dare to say that most of the time, people aren't dealing with complex objects. They are cloning data objects or shallow instance objects. The idea of a "deep clone" suggests more sophistication. It means you know you're dealing with complex objects. You know they may have significant proto chains. And you know whether you want those proto properties or not. And if that's the case, you should put together a sophisticated method that works the way you want, by composing these simpler methods. You should have everything you need with Object.create
, Object.clone
, and perhaps one of the common extend
methods. My feeling is that these are simple to understand individually, but still give you lots of power and flexibility when combined.
When I first started learning about Object.create I actually assumed this was how it worked. I was bewildered when things weren't working as I expected. I do wish this method were simpler.
@polotek: I was actually thinking about the same idea! The reason it's different from
extend
is thatextend
usually takes a target object to modify, rather than returning a new object. But I was thinking aboutclone
too, with the arguments reversed fromObject.create
as you suggest.One downside I see is that it's inconsistent with the argument order of the existing
Object.create
. And that also makes it less convenient for the common case of creating a proto-less dictionary object (Object.create(null)
vsObject.clone({}, null)
). But I guess you could make the case thatObject.create(null)
is a little more idiomatic since you're not really cloning anything.A possible extension, suggested by François Rémy, is a third (and optional) argument containing property descriptors, so that you can provide some properties with more control. Undecided yet about my opinion on that.
Bugfix: I think you want to allow
typeof proto
to be'function'
as well.Dave