I have a question about the following canonical object.create method:
Object.create = function(o, props) {
function F() {}
F.prototype = o;
if (typeof(props) === "object") {
for (prop in props) {
if (props.hasOwnProperty((prop))) {
F[prop] = props[prop];
}
}
}
return new F();
};
On line 3 of the above code we set the prototype property of the F object to the o argument's prototype.
I would have thought this meant that both o and F point to the same prototype and therefore point to the same set of members.
But the code then goes onto copy all the members in the prop in props loop.
What is the point of setting the prototype in line 3 if we then go onto copy all the members manually?
There's a mistake in the version of Object.create in your question: The loop attaches the properties to the constructor function F (not to the returned object, or its prototype) which means they're not accessible in the created object.
The properties of the second parameter to Object.create are supposed to be copied to the newly created object. The Mozilla documentation for Object.create puts it like this:
If specified and not undefined, an object whose enumerable own properties (that is, those properties defined upon itself and not enumerable properties along its prototype chain) specify property descriptors to be added to the newly-created object, with the corresponding property names.
Try running the following code with the version of Object.create in the question:
o = Object.create(
{a: "prototype's a", b: "prototype's b"},
{a: "object's a"}
);
You'll find that o.a == "prototype's a" and o.b == "prototype's b", "object's a" is lost.
The following version of the function would probably be more useful:
Object.create = function(o, props) {
var newObj;
// Create a constructor function, using o as the prototype
function F() {}
F.prototype = o;
// Create a new object using F as the constructor function
newObj = new F();
// Attach the properties of props to the new object
if (typeof(props) === "object") {
for (prop in props) {
if (props.hasOwnProperty((prop))) {
newObj[prop] = props[prop];
}
}
}
return newObj;
};
Let's try it out with the same example:
o = Object.create(
{a: "prototype's a", b: "prototype's b"},
{a: "object's a"}
);
The new object o is created with a prototype that has properties a and b and it's own property a.
Let's look at o.b first: o.hasOwnProperty("b") will return false, because o does not have a property called b. That's where the prototype comes in; because there is no property b it is looked up on the prototype, and therefore o.b === "prototype's b".
On the other hand, o.hasOwnProperty("a") will return true, because o does have an a property. o.a == "object's a" and nothing is looked up from the prototype.
As pointed out in #chuckj's answer, the correct implementation of Object.create is more complicated than this. For more details, see John Resig's post on ECMAScript 5 Objects and Properties.
The code you present is not equivalent to Object.create() as defined by the ECMA standard. The members of the second parameter, here called prop, is supposed to be a set of descriptors to be defined on the resulting object, not copied to the constructor function. This is more accurate (but not exacly right either),
Object.create = function(o, props) {
function F() {}
F.prototype = o;
var result = new F();
if (typeof(props) === "object")
for (prop in props)
if (props.hasOwnProperty(prop) && typeof(props[prop].value) != "undefined")
result[prop] = props[prop].value;
return result;
};
The props parameter contains the property descriptors for the members of the new object you want to be different than specified in the prototype, o. The values are not supposed to be copied literally into the object rather they are supposed to be processed as if you called Object.defineProperties(), above simulated by assigning the value part of the descriptor to the new object. Object.create() cannot be accurately polyfilled to ECMA 3 since it can be used to specifiy properties with accessor functions, though the above will work for props which use value instead of get or set.
If you are not interested in a polyfill version, the following is an accurate description of what Object.create() does, assuming the existence of both __proto__ and Object.defineProperties().
Object.create = function (o, props) {
if (typeof(o) !== "object" && o != null) throw new TypeError();
var result = new Object();
if (o != null)
result.__proto__ = o;
if (typeof(props) !== "undefined")
Object.defineProperties(result, props);
return result;
};
Think of F as a function that returns a new instance of an object and F.prototype a reference to the object that the new instance is borrowing properties from.
Instances created with F have their own property set independant of F.prototype.
When a property cannot be found in an incance of F then the runtime will step up the prototype chain and peek to see if the property exists in F.prototype.
This is why you would want to copy some and inherit others.
The point of the Prototype is to provide class-like code reuse/property storage.
Think of F.prototype as the class instances of F as the object.
Related
var obj={name:'John'}
var obj1=Object.create(obj);
This line will add the properties on the prototype of the obj1.
so if i will use console.log(obj1.name) , as property is not existing on current object it will go up in prototype chain and get name property and its value too.
Now consider
var obj2={lname:'Mart'}
obj2=Object.create(obj);
Now it will add name property to its prototype which is fine.
But all properties are gone of obj2 itself. (Reason for it i can think of from crokford's shim implementation where new object is created,properties added to its prototype and then returning them)
I can do so by obj2._ _proto_ _=obj; (Not browser friendly for all browsers)
Now my question is if good way of object.create is erasing properties, how should I add properties to my prototype so that own properties of obj2 do not get erased.
I don't want to make use of constructors. Is there any another way to add properties to prototype apart from above two. Please put light.
What you want is Object.assign. From the MDN:
The Object.assign() method is used to copy the values of all
enumerable own properties from one or more source objects to a target
object. It will return the target object.
Example
var obj1 = { a: "A", b: "B" };
var obj2 = { b: "XXX", c: "C" };
Object.assign(obj1, obj2);
// target ^ ^ source
console.log(obj1);
//=> { a: "A", b: "XXX", c: "C" }
// ^ the property from `obj2` overwrote the existing property
If you want to extend a prototype, just assign to the prototype instead of the object itself, e.g.:
Object.assign(Vector.prototype, Paths.prototype);
// Vector objects now include the methods from Paths
Compatibility
Be aware though that this is an ES2015 method. So when you deploy you will need a polyfill to support older browsers, which is provided on the MDN page:
if (typeof Object.assign != 'function') {
(function () {
Object.assign = function (target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
})();
}
It appears that ECMAScript 5 property attributes are inherited while others are not. Based on a simple experiment in Google Chrome, Safari, and Firefox, it seems that enumerable and writable are inherited from prototypes, but configurable is not. Consider when a is the prototype of b, and a defines property x (but b does not). If x is not writable, then b cannot override the value of x with =, which would, if allowed, only change b's x, and not a's (which is the x marked as non-writable). However, even if x is neither configurable nor writable, b may override the value of x with Object.defineProperty(b, 'x', ...) (which would fail for a: Object.defineProperty(a, 'x', ...), because a's `x' is not configurable).
I don't see anything in the standard that explains this (perhaps it's there, but I can't find it). Is this inconsistent behaviour intended?
Test output (answers "do a's and b's property attribute behave the same way?):
Object {enumerable: true, configurable: false, writable: true}
Code used to test:
function isEnumerable(p, o) {
for (var key in o) {
if (key === p) {
return true;
}
return false;
}
}
function isConfigurable(p, d, o) {
try {
Object.defineProperty(o, p, d);
return true;
} catch (e) {
return false;
}
}
function isWritable(p, v, o) {
if (o[p] === v) {
throw 'Error: isWritable will not work with identical value';
}
o[p] = v;
return o[p] === v;
}
function inheritedAttributes(
parentGenerator,
propertyName,
differentDescriptor,
differentValue) {
var parent, child, rtn = {};
var fns = {
'enumerable': isEnumerable.bind(this, propertyName),
'configurable': isConfigurable.bind(
this,
propertyName,
differentDescriptor),
'writable': isWritable.bind(this, propertyName, differentValue)
};
for (var key in fns) {
parent = parentGenerator();
child = Object.create(parent);
rtn[key] = fns[key](parent) === fns[key](child);
}
return rtn;
}
var propertyName = 'x';
var starterProperties = {};
starterProperties[propertyName] = {
'writable': false,
'configurable': false,
'enumerable': false,
'value': 'foo'
};
var differentDescriptor = {
'writable': false,
'configurable': false,
'enumerable': false,
'value': 'bar'
};
var differentValue = 'baz';
var parentGenerator = function() {
return Object.create(Object.prototype, starterProperties);
};
window.console.log(inheritedAttributes(
parentGenerator,
propertyName,
differentDescriptor,
differentValue
));
This doesn't answer "why". I suspect the answer to "why" is, "because that's what the standard says". Nevertheless, here are some relevant links, excerpts, and paraphrases from the standard. Thanks to basilikum for pointing me in the right direction.
Enumerable
for (var key in obj) { ... }
The for-in statement documentation states (emphasis added):
Enumerating the properties of an object includes enumerating
properties of its prototype, and the prototype of the prototype, and
so on, recursively; but a property of a prototype is not enumerated if
it is “shadowed” because some previous object in the prototype chain
has a property with the same name. The values of [[Enumerable]]
attributes are not considered when determining if a property of a
prototype object is shadowed by a previous object on the prototype
chain.
Hence, enumerability is inherited.
Writable
obj.x = a;
Property assignment invokes the internal method [[Put]], which starts by checking the return value of [[CanPut]]. When the property is a value, not a getter+setter, [[CanPut]] will check property attributes in the following order:
obj's x property's writable attribute
obj's extensible attribute
For each prototype from obj's prototype down to (but not including) null:
prototype's extensible attribute
prototype's x property's writable attribute
If any of these attributes is defined, then its value is returned. Hence, even when other attributes are missing, a non-writable property somewhere down obj's prototype chain causes assignment to fail on obj; i.e., writability is inherited.
Configurable
Object.defineProperty(obj, 'x', { 'value': a, ... });
Object.defineProperty invokes the internal method [[DefineOwnProperty]], which is rather convoluted. In general, this method will concern itself only with the extensible attribute of obj, and the configurable attribute of obj's current own x property, if it exists. As such, a non-configurable property, x, somewhere down obj's prototype chain does not influence this procedure; i.e., configurability is not inherited.
In Stoyan Stefanov's book Object-Oriented javascript, on page 103 he has the following. However when I try this, I get a different result with h instanceof Object. Am I missing something, has something in JS changed since or is this an error in the book.
>>> function Hero(){}
>>> var h = new Hero();
>>> var o = {};
>>> h instanceof Hero;
true
>>> h instanceof Object;
false //true in Google Chrome
>>> o instanceof Object;
true
If that's what the book says, then the book
is incorrect. (And searching the book content in Amazon.com confirms the error.)
Your true result that you get in Google Chrome is the correct result.
While the h object inherits from the .prototype on the Hero function, that .prototype inherits from the .prototype on the Object function. This means that h inherits both from Hero.prototype and Object.prototype, and is considered an instance of both constructors.
The only way it wouldn't be would be if Hero.prototype was an object that did not inherit from Object.prototype. But in that example, it uses the default object, so it does indeed inherit.
Using the instanceof operator you don't test whether something was created by a certain constructor, but whether something inherits from a certain object (whether a certain object is in the prototype chain of something). foo instanceof F has exactly the same result as a recursive* Object.getPrototypeOf(foo) === F.prototype
var F = function() {};
var foo = new F();
foo instanceof F // true
Object.getPrototypeOf(foo) === F.prototype // true
F.prototype = {}; // changed the prototype of F
foo instanceof F // false
Object.getPrototypeOf(foo) === F.prototype // false
foo instanceof Object // true
Object.getPrototypeOf(Object.getPrototypeOf(foo)) === Object.prototype // true
With that in mind, it is pretty obvious that if you don't change the prototype of a function to an object which doesn't inherit from Object.prototype, all the instances constructed with that function as constructor will inherit from Object.prototype, so they will be instances of Object.
F.prototype = Object.create(null);
var bar = new F();
bar instanceof F // true
bar instanceof Object // false
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
instanceof shim (just theoretical purposes, there is no practical use for it):
function instanceOf(object, constructor) {
if (!object) return false;
var object = Object.getPrototypeOf(object);
if (object === constructor.prototype) return true;
return instanceOf(object, constructor);
}
Why do the following two lines return different results?
("test" instanceof String) // returns false
("test".constructor == String) // returns true
Tested in the console of chrome version 28.0.1500.95 m
Does it work slightly differently for native types?
constructor is just a property of the internal [[prototype]] property, that can easily be manipulated:
function A(){}
function B(){}
A.prototype.constructor = B;
var a = new A();
console.log(a.constructor); //B
The instanceof operator however checks the internal prototype chain and is not so easily to be fooled, even if you change the complete prototype property of the constructor function:
function A(){}
function B(){}
A.prototype = B.prototype;
var a = new A();
console.log(a instanceof A); //true
console.log(a instanceof B); //false
So, why is "test" instanceof String === false but ("test".constructor == String) === true?
First of all, "test" is a primitive and primitives are never an instance of anything. What actually happens when you use instanceof is that the internal [[HasInstance]] method of the constructor is called with the possible instance as an argument. So a instanceof A translates roughly to:
`A.[[HasInstance]](a)`
The ECMA Specs have this to say to [[HasInstance]]: http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.3
[[HasInstance]] (V)
Assume F is a Function object.
When the [[HasInstance]] internal method of F is called with value V,
the following steps are taken:
If V is not an object, return false.
....
In other words: If the left hand side of instanceof is not an object, the operator will return false.
("test".constructor == String) === true works for a different reason: If you try to access a property of a primitive, the primitive will be temporarily converted into an object. So "test".constructor is roughly equal to:
(new String("test")).constructor
in which case you are actually creating an object, with a constructor function and requesting the constructor property afterward. So it is no surprise, that it will return String.
The main difference is that instanceof inspects the object's prototype chain whereas checking the constructor only checks to see if it was created from the same constructor.
Example:
function MyObject() {
this.sayHi = function() { alert('Hi!'); }
}
var x = new MyObject();
alert(x.constructor === Object);
alert(x instanceof Object);
I'm messing around with the prototype chain and noticed something I can't explain. I'm still learning all of this, so it's probably a mistake i've made. I'm trying to do some multi-inheritance, like so many others. I noticed the prototype object looks a lot like a hash/dictionary, I thought, why not use something like underscore.extend to merge multiple prototype objects together as one.
function A(){this.value="A";};
A.prototype.funcA = function (){console.log(this.value);}
function B(){this.value="B";};
B.prototype.funcB = function (){console.log(this.value);}
function C(){
// fix constructor
this.constructor = C;
// 'inherit' properties
A.call(this);
B.call(this);
};
// gobble up the prototype chains of A and B
C.prototype = new underscore.extend(A.prototype,B.prototype);
C.prototype.funcC = function (){console.log(this.value);}
var c = new C();
> c instanceof C
true
> c instanceof A
true
> c instanceof B
false
I'm really surprised to get a true at all here. Can anyone explain what's going on here?
UPDATE
I removed underscore's extend method from the code, as suggested, and this works a lot better. thanks!
function extend(destination, source) {
for (var property in source) {
if (source.hasOwnProperty(property)) {
destination[property] = source[property];
}
}
return destination;
};
function A(){this.value="A";};
A.prototype.funcA = function (){console.log(this.value);}
function B(){this.value="B";};
B.prototype.funcB = function (){console.log(this.value);}
function C(){
this.constructor = C;
A.call(this);
B.call(this);
};
var destination = {};
destination = extend(destination,A.prototype);
destination = extend(destination,B.prototype);
C.prototype = destination;
C.prototype.funcC = function (){console.log(this.value);}
var c = new C();
> c
{ constructor: [Function: C], value: 'B' }
> c instanceof A
false
> c instanceof B
false
> c instanceof C
true
There is no multiple inheritance in JavaScript, because one object can have only one prototype. To prove it is enough to see ECMAScript 5 Object.getPrototypeOf method which of course returns only one value. For older interpreters you could try __proto__ property (non-standard) of simply obj.constructor.prototype.
The example you've made give you a possibility to have all features from two different prototypes, however it brakes the prototype chain - this is why instanceof operator returns false for A and B. In fact prototypes of A or B are not prototypes of your object, but the mixin of them which you have made using the extend function. The function name is very misleading (however such name is used by some of frameworks and libraries) - because we don't extend any object (in object-oriented programming meaning) - we build a mixin of two object - which is completely different design pattern.
Bit out off topic - if you're experimenting with objects and prototypal inheritance - try to play with Object.create method (of ECMAScript 5). It's very useful in this case.