What is happening in Crockford's object creation technique?
There are only 3 lines of code, and yet I'm having trouble fully grasping this:
Object.create = function (o) {
functio开发者_开发问答n F() {}
F.prototype = o;
return new F();
};
newObject = Object.create(oldObject);
(from Prototypal Inheritance)
Object.create()
starts out by creating an empty function calledF
. I'm thinking that a function is a kind of object. Where is thisF
object being stored? Globally I guess.Next our
oldObject
, passed in aso
, becomes the prototype of functionF
. Function (i.e., object)F
now "inherits" from ouroldObject
, in the sense that name resolution will route through it. Good, but I'm curious what the default prototype is for an object, Object? Is that also true for a function-object?Finally,
F
is instantiated and returned, becoming ournewObject
. Is thenew
operation strictly necessary here? Doesn'tF
already provide what we need, or is there a critical difference between function-objects and non-function-objects? Clearly it won't be possible to have a constructor function using this technique.
What happens the next time Object.create()
is called? Is global function F
overwritten? Surely it is not reused, because that would alter previously configured objects. And what happens if multiple threads call Object.create()
, is there any sort of synchronization to prevent race conditions on F
?
1) Object.create() starts out by creating an empty function called F. I'm thinking that a function is a kind of object. Where is this F object being stored? Globally I guess.
No, it's stored on the local scope of the Object.create
function, each time you invoke Object.create
this function F
will be recreated.
You could even create a more memory-efficient implementation, by storing F
on a closure, and reuse it:
if (typeof Object.create !== "function") {
Object.create = (function () {
function F() {} // created only once
return function (o) {
F.prototype = o; // reused on each invocation
return new F();
};
})();
}
2) Next our oldObject, passed in as o, becomes the prototype of function F. Function (i.e., object) F now "inherits" from our oldObject, in the sense that name resolution will route through it. Good, but I'm curious what the default prototype is for an object, Object? Is that also true for a function-object?
All objects have an internal property that builds the prototype chain, this property is known as [[Prototype]]
, it's an internal property, although some implementations let you access to it, like mozilla, with the obj.__proto__
property.
The default [[Prototype]]
when you create a new object, i.e. var obj = {};
is Object.prototype
.
All functions have a prototype
property, this property is used when a function is used as a Constructor, invoked with the new
operator.
A new object instance it's created behind the scenes, and this object [[Prototype]]
is set to its Constructor's prototype
property.
3) Finally, F is instantiated and returned, becoming our newObject. Is the "new" operation strictly necessary here? Doesn't F already provide what we need, or is there a critical difference between function-objects and non-function-objects? Clearly it won't be possible to have a constructor function using this technique.
Yes, the new
operator is essential in this method.
The new
operator is the only standard way to set the [[Prototype]]
internal property of an object, if you are curious about how it works, you can give a look to the [[Construct]]
internal operation.
What happens the next time Object.create() is called? Is global function F overwritten? Surely it is not reused, because that would alter previously configured objects. And what happens if multiple threads call Object.create(), is there any sort of synchronization to prevent race conditions on F?
The next time Object.create
is invoked, a new local F
function is instantiated only within the scope of the method call, you shouldn't worry about race conditions.
Note that this implementation hardly conforms the Object.create
described in the ECMAScript 5th Edition Specification, in that method, you could pass a property descriptor to initialize the object.
All browser vendors are implementing it (already available on Firefox 3.7 alphas, latest Wekit Nightly Builds and Chrome 5 Beta), so I would recommend you at least to check if a native implementation exist before overriding it.
1) A function is indeed a kind of object. A function object with identifier F
is created each time Object.create
is called, and is only accessible with that identifier within that execution of Object.create
. Therefore, each time Object.create
is called, you get a different function object F
. This function object lives on as the constructor
property of the object returned by Object.create
.
2)
F now "inherits" from our oldObject, in the sense that name resolution will route through it
This isn't really correct. Assigning an object someObject
to the prototype
property of a function just means that the prototype of any future object created by calling this function as a constructor will be someObject
.
3) The new
is absolutely vital to this technique. Only by calling a function as a constructor does it produce a new object, and that object's prototype (which is not generally accessible) is set to the constructor function's prototype
property. There is no other (standardised) way to set an object's prototype.
Finally, JavaScript in browsers is single threaded, so race conditions such as you describe are not possible.
Your major misunderstanding here is that F has global scope. It is declared in the body of Object.create and consequently is only in scope within that method block.
> Clearly it won't be possible to have a constructor function using this technique.
The technique is already an object constructor since it returns new F(), but no property values can be set as for say new man('John','Smith'). However, if the Object.create code is modified, instantiation is possible. For example the sarah object below can be constructed and instantiated using Object.creator, and will inherit the getName method.
var girl = {
name: '',
traits: {},
getName: function(){return this.name}
}
var sarah = Object.creator(girl, 'Sarah', {age:29,weight:90})
The sarah object will then consist of own properties { name:'Sarah', traits:{age:9,weight:49} }, and the prototype inherited sarah.getName() will produce 'Sarah'.
The following method relies on own properties enumerating with 'for(prop in o)' in creation order. Although not guaranteed by ECMA specs, this example (and a few more complex) worked for all major browsers (4) tested, providied hasOwnProperty() was used, otherwise not.
Object.creator = function(o) {
var makeArgs = arguments
function F() {
var prop, i=1, arg, val
for(prop in o) {
if(!o.hasOwnProperty(prop)) continue
val = o[prop]
arg = makeArgs[i++]
if(typeof arg === 'undefined') break
this[prop] = arg
}
}
F.prototype = o
return new F()
}
The official ECMA Object.create has an optional 2nd parameter, propertiesObject, that can instantiate property values, but it is an object rather than the usual list, and looks awkward to use. E.g. I believe:-
o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });
is equivalent to the much simpler old way:-
o2 = new function(p) { this.p=p }(42)
and
o2 = Object.creator({p:''}, 42)
精彩评论