What is the purpose of `Object(target)` in the `Object.assign()` polyfill - javascript

In the MDN page forObject.assign() the example polyfill first wraps all sources and the target parameters in Object() before iterating over the properties.
(i.e. Object(target), Object(source1), Object(source2)...).
The text also mentions that the additional properties are added directly to the target before returning the target. However, wrapping the target in Object() results in an object that is different than simply augmenting properties. (i.e. Object(target).newProp !== target.newProp).
All the examples given have objects as parameters to Object.assign(). The use-case for non-object source or target parameters is therefore not clear.
A) What is the purpose of wrapping parameters in Object()? (I am under the impression that Object.keys(x) is the same as Object.keys(Object(x))).
B) What are possible use cases for using Object.assign() with non-objects, is any? (for example something like: Object.assign(1, 'b', [3], true, function(){}) )

Let's break it down:
Test if the object exists, if not make it:
if (!Object.assign) {
Make the method via Object.defineProperty and add it to Object
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
Here the actual function gets set. One needs to supply a target and one source minimum.
value: function(target, firstSource) {
'use strict';
If the target is not defined throw an error.
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
Cast the target to Object format. (E.g. String 1234 to [object String]{0: "1", 1: "2", 2: "3", 3: "4", length: 4}.
var to = Object(target);
Now loop through all sources using the arguments object of the function. Start with 1, since 0 is the target.
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i]; //store the argument in a variable.
if (nextSource === undefined || nextSource === null) {
continue; //if the source is undefined continue.
}
Then we need all (not only the exposed) the enumerable properties from the source object, use Object.keys in combination with Object(source).
var keysArray = Object.keys(Object(nextSource));
Iterate over the keys:
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex]; //select the key from the index.
getOwnPropertyDescriptor gives us information about the property in the form of an object.
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
If the property is not undefined and is enumerable then set this property as a property to to.
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
Finally return to with the newly added (cloned) properties.

Related

Adding a function to Array.prototype in IE results in it being pushed in to every array as an element

I have added the following polyfill to Array in the beginning of my project:
if (!Array.prototype.find) {
Array.prototype.find = function(predicate) {
if (this === null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
This works perfectly fine in Chrome and Firefox, but on Internet Explorer 11, this function is actually being pushed in every Array as an element of it, and I can even access it like:
var a = [];
a[0]();
This is throwing all sorts of exceptions in IE with functions like .forEach where I am expecting some data and this function is found.
Here's a screenshot from IE's developer tools, in this case, this array should have just 2 elements, instead of 3.
And this is how it should be, from Chrome. In fact, I believe even the actual content is wrong, but i didn't get there yet (it should be an array containing arrays of length 2).
How can JavaScript still behave so wrong in IE11, and how can I correctly add this function to the prototype instead of in every Array instance?
It's not being "pushed" into every array; you added a property to the prototype object, so it's visible and enumerable in every array instance. That's how prototype properties are supposed to work.
It works in Chrome and Firefox because .find() on the prototype in those environments is defined in such a way as to be visible but not enumerable. You can do that in IE by using Object.defineProperty():
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, "find", {
value: function(predicate) {
if (this === null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
}
});
}
In addition to property "value", which is clearly the value of the new property, the properties "enumerable" and "configurable" default to false. That means that "find" won't show up in any situation that involves iterating through object properties.

Object.create erasing properties of object

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;
};
})();
}

How can I get the key of my object?

I have the following object:
var text = { 'one' : 1, 'two' : 2, 'three' : 3};
I want to write the key 'one' with console.log(). How can I do it?
try:
var text = { 'one' : 1, 'two' : 2, 'three' : 3};
for (var i in text){
console.log(i);
}
You need to use the keys() function, as epoch pointed out in the comment section.
For example :
console.log(Object.keys(text));
Can print one two three; If you want to be certain about your order, you'll have to use an array instead of an object, as properties in objects are not ordered.
Be careful, in older browers you'll have to define your own fucntion of keys, as explained in the doc i linked above, like so :
if (!Object.keys) {
Object.keys = (function() {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function(obj) {
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [], prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
}());
}
Your question is kind of vague.
What exactly is it that you are trying to achieve?
Do you just want to log the first prop in your obj?
If so, that would not be a good idea because the order of properties in an object is not guaranteed. The order might even change over time, depending on what happens to the object.
You could in fact use the Object.keys() method, which would do a for in on all enumerable properties of the object, but this would give you all properties and since you asked for a specific property to be logged to the console, this leads me to the conclusion that you probably are more interested in how to check for the existence of a property.
To make a long story short, if you really want to log a specific property you could just do
(text.hasOwnProperty('one')) ? console.log('one') : console.log('"one" does not exists');

Updating JavaScript object-attributes from another object [duplicate]

This question already has answers here:
How can I merge properties of two JavaScript objects dynamically?
(69 answers)
Closed 6 years ago.
I want to update an object that could look like this:
currentObject = {
someValue : "value",
myObject : {
attribute1 : "foo",
attribute2 : "bar"
}
};
.. with an object that contains some changes i.e.:
updateObject = {
myObject : {
attribute2 : "hello world"
}
};
At the end I would like to have currentObject updated so that:
currentObject.myObject.attribute2 == "hello world"
That should be posible for other objects as well..
As a solution I thought about iterating over the object and somehow take care of the namespace. But I wonder if there is an easy solution for that problem by using a library like jQuery or prototype.
I suggest using underscore.js (or better, lo-dash) extend:
_.extend(destination, *sources)
Copy all of the properties in the source objects over to the
destination object, and return the destination object. It's in-order,
so the last source will override properties of the same name in
previous arguments.
_.extend({name: 'moe'}, {age: 50});
=> {name: 'moe', age: 50}
function update(obj/*, …*/) {
for (var i=1; i<arguments.length; i++) {
for (var prop in arguments[i]) {
var val = arguments[i][prop];
if (typeof val == "object") // this also applies to arrays or null!
update(obj[prop], val);
else
obj[prop] = val;
}
}
return obj;
}
should do the trick: update(currentObject, updateObject). You might want to add some type checks, like Object(obj) === obj to extend only real objects with real objects, use a correct loop for arrays or hasOwnProperty tests.
Here's an Object.keys and recursive example:
// execute object update function
update(currentObject, updateObject)
// instantiate object update function
function update (targetObject, obj) {
Object.keys(obj).forEach(function (key) {
// delete property if set to undefined or null
if ( undefined === obj[key] || null === obj[key] ) {
delete targetObject[key]
}
// property value is object, so recurse
else if (
'object' === typeof obj[key]
&& !Array.isArray(obj[key])
) {
// target property not object, overwrite with empty object
if (
!('object' === typeof targetObject[key]
&& !Array.isArray(targetObject[key]))
) {
targetObject[key] = {}
}
// recurse
update(targetObject[key], obj[key])
}
// set target property to update property
else {
targetObject[key] = obj[key]
}
})
}
JSFiddle demo (open console).
A simple implementation would look like this.
function copyInto(target /*, source1, sourcen */) {
if (!target || typeof target !== "object")
target = {};
if (arguments.length < 2)
return target;
for (var len = arguments.length - 1; len > 0; len--)
cloneObject(arguments[len-1], arguments[len]);
return target;
}
function cloneObject(target, source) {
if (!source || !target || typeof source !== "object" || typeof target !== "object")
throw new TypeError("Invalid argument");
for (var p in source)
if (source.hasOwnProperty(p))
if (source[p] && typeof source[p] === "object")
if (target[p] && typeof target[p] === "object")
cloneObject(target[p], source[p]);
else
target[p] = source[p];
else
target[p] = source[p];
}
This assumes no inherited properties should be cloned. It also does no checks for things like DOM objects, or boxed primitives.
We need to iterate in reverse through the arguments so that the copy is done in a right to left matter.
Then we make a separate cloneObject function to handle the recursive copying of nested objects in a manner that doesn't interfere with the right to left copying of the original object arguments.
It also ensures that the initial target is a plain object.
The cloneObject function will throw an error if a non-object was passed to it.

each implementation in the underscore.js library

Question about the implementation of the "each" function I found in the underscore.js source code (source below).
First, could someone explain what the line "else if (obj.length === +obj.length) " is checking for.
Second, could someone explain why hasOwnProperty.call(obj, key) is used, as opposed to obj.hasOwnProperty? Is it because the passed in obj may not implement hasOwnProperty (which I thought every javascript object did)
any insights appreciated. Thanks.
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
};
This:
+obj.length
...will do a toNumber conversion on the value of length.
It appears as though they're making sure that length references a number by doing the toNumber conversion, and verifying that it's still the same number after the conversion.
If so, they assume that it is an Array, or at least an Array-like object for iteration.
If not, they assume that enumeration of all key value pairs is desired.
var obj = {
length:null,
someprop:'some value'
};
obj.length === +obj.length; // false, so do the enumeration
var obj = {
length: 2,
"0":'some value',
"1":'some other value'
};
obj.length === +obj.length; // true, not an actual Array,
// but iteration is still probably wanted
Of course you could have an object with a length property that is a primitive number, but still intend to enumerate the properties.
var obj = {
length: 2,
"prop1":'some value',
"prop2":'some other value'
};
obj.length === +obj.length; // true, it will iterate, but it would
// seem that enumeration is intended

Categories