Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending an object by reference

Tags:

javascript

I have an extend method that extends objects like you would expect it would in JavaScript:

var objA = {a: true};
var objB = extend({b:false}, objA);
console.log(objB); // {a: true, b: false}

However, I would like to extend the source's (objA) properties onto the target (objB) object by reference so that any changes made to the source are reflected in the target after the fact, like so:

var objA = {a: true};
var objB = extend({b: false}, objA);
console.log(objB); // {a: true, b:false}
objA.a = false;
console.log(objB); // {a: false, b:false}

For instance, when you modify an object (which is always assigned by reference) things are working the way I would like them to:

var objA = {A:{a: true}};
var objB = _.extend({b: false}, objA);
console.log(objB) // {A:{a:true}, b:false};
objA.A.a = false; 
console.log(objB); // {A:{a:false}, b:false};

So in other words, when changes are made to objA's non-object literal properties (or any value that isn't assigned by reference) I would like those changes to be reflected in objB.

I'm fairly sure this isn't exactly possible without an additional helper method that would depend on some sort of object watch method that is triggered whenever an object changes.

Thoughts?

Code from my extend method:

(l).extend = (l).merge = function () {
    var args = (l).fn.__args(arguments, [{
            deep: 'bool'
        }, {
            '*': 'obj:object'
        }
    ]),
        target = (l)._object || args.obj,
        keys = [],
        obj, objs, copy, key, i, o;

    // Collect potential objects to merge
    objs = (l).filter(args, function (value, index) {
        if (index !== "obj" &&
            (l).isPlainObject(value) && !((l).isEqual(target, value))) {
            return value;
        }
    });

    // When target object is not selected globally
    if (!args.obj) {
        target = objs.shift();
    }

    // When target object is not selected globally and only a single object
    // is passed extend the library itself
    if (!(l)._global && !objs.length) {
        target = this;
        objs[0] = args.obj;
    }

    // When a boolean is passed go deep
    if (args.deep) {

        // Build property reference used to prevent never ending loops
        (l).each(objs, function (index, value) {
            keys.push((l).keys(value));
            keys = (l).flatten(keys);
        });

        // Add properties to all nested objects
        (l).deep(target, function (depth, index, obj, ref) {
            if ((l).indexOf(keys, index) === -1) {
                for (i = 0; i < objs.length; i++) {
                    for (key in objs[i]) {
                        if ((l).isPlainObject(obj)) {
                            copy = objs[i][key];
                            obj[key] = copy;
                        }
                    }
                }
            }
        }, "*");
    }

    // Merge first level properties after going deep
    for (i = 0; i < objs.length; i++) {
        if ((obj = objs[i]) !== null) {
            for (key in obj) {
                copy = obj[key];
                if (target === copy) {
                    continue;
                }
                target[key] = copy;
            }
        }
    }
    return (l).fn.__chain(target);
};
like image 873
Xaxis Avatar asked Apr 14 '26 22:04

Xaxis


2 Answers

What are you mentioned here is exactly the prototypal inheritance of JavaScript:

var objA = {a: false};
var objB = Object.create(objA);
objB.b = false;

console.log(objB.a, objB.b); // true, 
objA.a = false;
console.log(objB.a, objB.b); // false, false

Of course if objB overrides the a property, you will lose that link, because JavaScript will found the property a in the object objB, therefore won't look up in the prototype's chain.

Using the ECMAScript 5 methods, you could have your extend function like that:

function extend(properties, proto) {
  var object = Object.create(proto);
  var descriptor = {};

  Object.getOwnPropertyNames(properties).forEach(function(name) {
    descriptor[name] = Object.getOwnPropertyDescriptor(properties, name);
  });

  return Object.defineProperties(object, descriptor);
}


var objA = {a: true};
var objB = extend({b: false}, objA);
console.log(objB.a, objB.b); // true, false
objA.a = false;
console.log(objB.a, objB.b); // false, false
like image 196
ZER0 Avatar answered Apr 16 '26 12:04

ZER0


Object.extend= function (properties,obj) {
            function F() { };
            F.prototype = obj;
            var newObj = new F();
            for (var prop in properties){
                 newObj[prop] = properties[prop];
            }
            return newObj;
        }
like image 23
Khanh TO Avatar answered Apr 16 '26 10:04

Khanh TO



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!