Skip to content

Instantly share code, notes, and snippets.

@rwaldron
Created August 8, 2012 16:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rwaldron/3296375 to your computer and use it in GitHub Desktop.
Save rwaldron/3296375 to your computer and use it in GitHub Desktop.
Object.extend = function( LeftHandSideExpression, AssignmentExpression ) {
var target, src, needToThrow;
// Let target be ToObject(LeftHandSideExpression).
target = Object( LeftHandSideExpression );
// Let src be ToObject(AssignmentExpression).
src = Object( AssignmentExpression );
// Let needToThrow be false.
needToThrow = false;
// For each own property of src, let key be the property key
// and desc be the property descriptor of the property.
Object.getOwnPropertyNames( src ).forEach(function( key ) {
var desc, f, status;
desc = Object.getOwnPropertyDescriptor( src, key );
// If desc describe a data property , then
// Let f be desc.[[Value]].
// If f is a function with a super binding that is bound to src, then,
// Let desc.[[Value]] be a new function will all the same
// internal properties as f except that its super binding is set to target.
if ( desc.value !== undefined ) {
f = desc.value;
if ( typeof f === "function" ) {
desc.value = f.bind( target );
}
}
// If desc describe an accessor property and has a [[Get]] attribute, then
// Let f be desc.[[Get]].
// If f is a function with a super binding that is bound to src, then
// Let desc.[[Get]] be a new function will all the same
// internal properties as f except that its super binding is set to target.
if ( desc.get !== undefined ) {
f = desc.get;
desc.get = f.bind( target );
}
// If desc describe an accessor property and has a [[Set]] attribute, then
// Let f be desc.[[Set]].
// If f is a function with a super binding that is bound to src, then
// Let desc.[[Set]] be a new function will all the same
// internal properties as f except that its super binding is set to target.
if ( desc.set !== undefined ) {
f = desc.set;
desc.set = f.bind( target );
}
// Let status be the result of calling the [[DefineOwnProperty]]
// internal method of target with arguments key, desc, and true.
status = Object.defineProperty( target, key, desc );
// If status is an abrupt completion, set needToThrow to true;
// ...
// omitted
// ...
// console.log( desc );
});
if ( needToThrow ) {
throw new TypeError("Something bad happened");
}
return target;
};
// Basic tests...
//
var original, ref, arr, result, div;
original = {
get one() {
return 1;
},
two: 2,
// this will be paved over
// and no longer update this.two
set val( value ) {
this.two = value;
}
};
ref = { b: "baz" };
arr = [ ref, 1, 2 ];
result = Object.extend( original, {
val: 10,
get f() {
return "foo";
},
boundOp: function( x ) {
return this.val * x;
},
ref: ref,
arr: arr
});
result.val = 4;
console.log( result.one /* 1, Nothing happened here */ );
console.log( result.two /* 2, set val(){} was paved; no longer sets "two" */ );
console.log( result.boundOp(2) /* 8, this.val */ );
// add a new prop to the reference object
ref.a = "alpha";
console.log( result.ref.a /* "alpha" */ );
// Of course it will appear in the resulting extended object
// Is this ideal?
// change an element in the reference array
arr[0] = 0;
console.log( result.arr[0] /* 0 */ );
// Of course it will appear in the resulting extended object
// Is this ideal?
//
//
// DOM example...
//
if ( document ) {
div = document.createElement("div");
Object.extend( div, {
// Devs will expect this to "just work"
innerHTML: "<p>P child element that contains a Text node</p>",
id: "my-div",
hidden: true
});
document.body.appendChild( div );
console.log( div.innerHTML /* "" */ );
// This is dissappointing.
// in JS today, devs will expect this
// to work (as it does in major libraries)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment