How do I override javascript's cloneNode? - javascript

This is what I have so far: http://jsfiddle.net/beCVL/5/
This is how I'm trying to override the default cloneNode:
Object.defineProperty(Object.prototype, "cloneNode", {
get: cloneNode2,
set: cloneNode2
});
but it's not working, so, I think Object.prototype doesn't have cloneNode, but Element.prototype doesn't either.
So, which class do I need to use to override cloneNode, assuming my method is correct?

It's a property of Node.prototype https://developer.mozilla.org/En/DOM/Node.cloneNode
Node.prototype.cloneNode = function() {}
However, modifying built in objects may give you grief in the future. If possible, you should create a different function and use that instead, that way, existing code that uses cloneNode won't break.

Try using:
Node.prototype.cloneNode = cloneNode2;
Object.defineProperty is not used for this purpose. Here's an example of a use for it:
var o = {};
Object.defineProperty(o, 'blah', {
'get': function () { return 'asdf'; },
'set': function (x) { alert(x); }
});
alert(o.blah); // alerts 'asdf'
o.blah = 'fdsa'; // alerts 'fdsa'
Apparently, this only works in Chrome.
To solve the actual problem, it should be simple enough to just replace the RGB codes with its equivalent hex code.
function decToHex(a) {
return ('00' + (+a).toString(16)).substr(-2);
}
function replaceRGB(str) {
return str.replace(/rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)/, function (_, r, g, b) {
return "#" + decToHex(r) + decToHex(g) + decToHex(b);
});
}
replaceRGB("color: rgb(255, 0, 0)") // "color: #ff0000"

This series of fiddles is a work in progress, but it reimplements cloneNode's functionality. http://jsfiddle.net/beCVL/19/
as of April 4th, 10:53am EST, it needs to work with IE, as IE doesn't have a Node object.
Note, that in IE, prototype functions can't be overridden. So, all instances of cloneNode have to be replaced by a function that determines which version of cloneNode to use.

Related

Bound function instead of closure to inject extra arguments

It seems that instead of
{ onClick: function(event){ someHandler('clicked', event); } }
you can write
{ onClick: someHandler.bind(null, 'clicked'); }
which looks more concise and feels more "functional".
Are there any drawbacks to this? Is there a performance gain by not having created a closure? Is there a way to keep the this that the closure would have received?
It's not a "closure", it's just an anonymous function.
Personally I prefer the bind version because as you say, it's more concise. However, according to this jsperf (http://jsperf.com/anonymous-function-vs-bind), it's ten times slower, which surprises me greatly, especially since the bind used here seems to be the native one. One hypothesis is that bind, or rather the function it generates, needs to do work around looking at the arguments passed in and constructing an argument list to pass along to the function being called.
To maintain this, you need a variant of bind such as Underscore's _.partial, or you could write one yourself:
function partial(fn) {
var slice = Array.prototype.slice,
args = slice.call(arguments, 1);
return function() {
return fn.apply(this, args.concat(slice.call(arguments, 1)));
};
}
Unfortunately, the variation using partial ({ onClick: partial(someHandler, 'clicked'); }) is still ten times slower than the anonymous function.
The hypothesis that argument list handling is causing the slowdown is supported by another test case in the jsperf, which defines a partial1 which predefines just the first of exactly two arguments to the underlying function:
function partial1(fn, a) {
return function(b) {
return fn.call(this, a, b);
};
}
Using that one, which doesn't have to create and merge argument lists, results in a slowdown of only 25-35%, instead of 90%.
If we don't care about passing through this, which allows us to avoid using Function#call:
function partial2(fn, a) {
return function(b) {
return fn(a, b);
};
}
Then the slowdown is just 10%.
However, if we really want to pass through this, then we need to write the anonymous function version as
{ onClick: function(event) { someHandler.call(this, 'clicked', event); } }
which also results in a 20-25% slowdown from the original version, presumably due to the cost of invoking Function#call. So in that sense, asusming you do want to pass through this, the performance of the anonymous function and our home-grown partial1, customized for number of arguments, is roughly equivalent, which is not surprising since they're essentially doing identical work.
Are there any drawbacks to this? Is there a performance gain by not
having created a closure?
Yes, bind has a performance drawback you can find more details here
Is there a way to keep the this that the closure would have received?
This depends on how you define this
It works well if you pass an object like my_klass in the example
function some(a,b){
console.log("a is: " + a);
console.log("b is: " + b);
console.log("this.val: " + this.val)
}
function klass(val){
this.val = val;
}
my_klass = new klass("test val");
var ab = {
click: function(a){ some('clicked',a);},
bclick: some.bind(my_klass,'clicked')
}
ab.bclick("param");
Output:
a is: clicked
b is: param
this.val: test val
It wont work if you do like
function some(a,b){
console.log("a is: " + a);
console.log("b is: " + b);
console.log("this.val: " + this.val)
}
var ab = {
val: 99,
click: function(a){ some('clicked',a);},
bclick: some.bind(this,'clicked')
}
ab.bclick("param"); // Expected to print 99
Output:
a is: clicked
b is: param
this.val: undefined

Placeholders for anonymous functions in JavaScript

I've been writing d3 code that ends up having lots of functions like this:
selection.attr('x', function(d) { return d.layout.x; });
Is there any way to simulate Scala's placeholder syntax, which would allow me to write this:
selection.attr('x', _.layout.x);
Obviously getters need to be told a specific parameter name to apply to, or one could define a sort of 'meta-getter' that responds with an anonymous function that gets the desired named attribute.
I'd be interested to know if anything like this exists in, for example, CoffeeScript. ES6 lambda functions are closer, but still not as semantic and clear as placeholder syntax.
You don't say what environment you want this to run in, so, assuming that the bleeding edge is okay, let's use Proxy:
var _ = new Proxy({}, {
get: function(target, name) {
return createProxyForPath(name, []);
}
});
function createProxyForPath(name, path) {
var newPath = path.slice();
newPath.push(name);
return new Proxy({}, {
get: function(target, name) {
if (name !== "$") return createProxyForPath(name, newPath);
return function getter(obj) {
return newPath.reduce(function(prev, curr) {
return prev[curr];
}, obj);
};
},
apply: function(target, context, args) {
// TODO: Preserve function calls and args here
}
});
}
You would use it like this:
> [{x: 1}, {x: 2}, {x: 3}].map(_.x.$)
[1, 2, 3]
It's not a complete replacement for Scala's magic underscore (it doesn't trap method calls right now, for example, so you can't do _.x.toString().slice(0, 3) to take one example). Also, it requires an explicit $ to signal the end of the chain. But for simple getters it works pretty well.
Alternately, if you need to support browsers that aren't Firefox right now you could write a sweet.js macro to generate the getter instead:
// Via Daniel
macro _ {
rule { . $m ... } => { function (value) { return value.$m ... } }
}
selection.attr('x', _.layout.x + 1);
Will expand to:
selection.attr('x', function(value) {
return value.layout.x + 1;
});
(If you use value yourself in the function sweet.js will do the right thing and rename the argument to value$some-integer to avoid any name conflicts inside the anonymous function.)
It does handle method calls, but of course none of these approaches handle using the placeholder as, for example, a function argument:
selection.attr('x', someFunction(_));
This could be emulated with a function instead of an object:
var getter = function(properties) {
props = properties.split('.');
return function(d) {
return props.reduce(function(prev, curr) {
return prev[curr];
}, d);
};
};
selection.attr('x', getter('layout.x'));
Which is... okay, but I wonder if JS can do better.

jQuery Extension accepting a String not just an Object

This might be a silly question, but I can't seem to find a solution...
I just wanted to make a isNullOrWhiteSpace extension (same name as the .NET one), to determine if a string is '', '0', 0, undefined, null. Nothing crazy.
Now doing it with a typical jQuery extension, it seems it is always looking for a jQuery Object to be passed in. But for in my extension, I need it to work with a simple string, but it doesn't work at all when I do.
$.fn.isNullOrWhiteSpace = function () {
if (['', '0', 0, undefined, null].indexOf($.trim(this)) > -1) {
return false;
}
return true;
};
'testing'.isNullOrWhiteSpace(); // doesn't work
// Uncaught TypeError: Object has no method 'isNullOrWhiteSpace'
What am I missing here??
-- from answers below, turns out it should be simply:
$.isNullOrWhiteSpace, the $.fn. part makes it a jQuery-Object extension as opposed to just a regular extension (like $.isArray(), $.trim() (which I use in my own question... sigh))
If you must hook this to jQuery — and there's really no reason to beyond namespace economy — you would do this:
$.nullOrWhitespace = function(s) {
return !s || !(s.replace(/\s*/, '')); // or something like this; just an example
};
Then call it with
if ( $.nullOrWhitespace( yourString ) ) {
... whatever ...
}
Try String.prototype.isNullOrWhiteSpace = function() {...
$.fn by default sets the context of this to an array of matched elements(when used with a selector). Which you clearly don't need in this case.
$.isNullOrWhiteSpace = function (str) {
return $.inArray($.trim(str), ['', '0', 0, undefined, null]) > -1;
};
This is what most other utility methods do eg: $.inArray, $.trim like we have already used! :)
As far as I know, $.fn.isNullOrWhiteSpace extends jQuery.
If you are extending jQuery you propably need to call the method on a jQuery object.
'testing' is a string but $('testing') would be a jQuery object.
This seems to work:
$.fn.isNullOrWhiteSpace = function () {
if (['', '0', 0, undefined, null].indexOf($.trim(this.selector)) > -1) {
return false;
}
return true;
};
alert($('').isNullOrWhiteSpace());
alert($('testing').isNullOrWhiteSpace());
DEMO

Cross-browser Getter and Setter

This works in modern Chrome/Firefox/Opera but fails in IE8. Haven't tried it in IE9. How can I make this cross-browser compatible, including IE7+? (Fiddle here.)
var foo = {
get test(){ return 'Works'; }
};
// foo.test should be 'Works'
I've seen some usage with __defineGetter__ but that threw an 'unrecognized method' error in IE8.
Here is the workaround for IE6/7/8. I performed the test and it works very well!
Update: The link is broken, you can see the code of from my testing here:
// Super amazing, cross browser property function, based on http://thewikies.com/
function addProperty(obj, name, onGet, onSet) {
// wrapper functions
var
oldValue = obj[name],
getFn = function () {
return onGet.apply(obj, [oldValue]);
},
setFn = function (newValue) {
return oldValue = onSet.apply(obj, [newValue]);
};
// Modern browsers, IE9+, and IE8 (must be a DOM object),
if (Object.defineProperty) {
Object.defineProperty(obj, name, {
get: getFn,
set: setFn
});
// Older Mozilla
} else if (obj.__defineGetter__) {
obj.__defineGetter__(name, getFn);
obj.__defineSetter__(name, setFn);
// IE6-7
// must be a real DOM object (to have attachEvent) and must be attached to document (for onpropertychange to fire)
} else {
var onPropertyChange = function (e) {
if (event.propertyName == name) {
// temporarily remove the event so it doesn't fire again and create a loop
obj.detachEvent("onpropertychange", onPropertyChange);
// get the changed value, run it through the set function
var newValue = setFn(obj[name]);
// restore the get function
obj[name] = getFn;
obj[name].toString = getFn;
// restore the event
obj.attachEvent("onpropertychange", onPropertyChange);
}
};
obj[name] = getFn;
obj[name].toString = getFn;
obj.attachEvent("onpropertychange", onPropertyChange);
}
}
I don't believe you can.
In IE8 and lower, property access is mere property access. There's no way to run function code without explicitly invoking the function.
I think in IE8 you may be able to with DOM elements, but I don't believe it works for regular native objects.
There is a "definePropery" method that will essentially allow you to create accessor methods (getters/setters) on Objects without the need to invoke a function call like setProp() / getProp().
The syntax is a little weird but I've been able to get this to work on Firefox, Chrome, Safari and IE9.
Say I have JavaScript Object called "Person".
function Person()
{
// set a default value //
this.__name = 'John';
// add getter & setter methods //
Object.defineProperty(this, "name", {
get: function() {
// additional getter logic
return this.__name;
},
set: function(val) {
this.__name = val;
// additional setter logic
}
});
}
var p = new Person();
console.log(p.name); // 'John'
p.name = 'Stephen';
console.log(p.name); // 'Stephen'
More info on Mozilla's site here.
You cannot, the syntax is not supported in browsers that did not implement it. Its going to be quite a while before you'll be able to use that syntax without having CBC problems. Be grateful IE6 is pretty much dead in North America.

Overriding a JavaScript function while referencing the original

I have a function, a(), that I want to override, but also have the original a() be performed in an order depending on the context. For example, sometimes when I'm generating a page I'll want to override like this:
function a() {
new_code();
original_a();
}
and sometimes like this:
function a() {
original_a();
other_new_code();
}
How do I get that original_a() from within the over-riding a()? Is it even possible?
Please don't suggest alternatives to over-riding in this way, I know of many. I'm asking about this way specifically.
You could do something like this:
var a = (function() {
var original_a = a;
if (condition) {
return function() {
new_code();
original_a();
}
} else {
return function() {
original_a();
other_new_code();
}
}
})();
Declaring original_a inside an anonymous function keeps it from cluttering the global namespace, but it's available in the inner functions.
Like Nerdmaster mentioned in the comments, be sure to include the () at the end. You want to call the outer function and store the result (one of the two inner functions) in a, not store the outer function itself in a.
The Proxy pattern might help you:
(function() {
// log all calls to setArray
var proxied = jQuery.fn.setArray;
jQuery.fn.setArray = function() {
console.log( this, arguments );
return proxied.apply( this, arguments );
};
})();
The above wraps its code in a function to hide the "proxied"-variable. It saves jQuery's setArray-method in a closure and overwrites it. The proxy then logs all calls to the method and delegates the call to the original. Using apply(this, arguments) guarantees that the caller won't be able to notice the difference between the original and the proxied method.
Thanks guys the proxy pattern really helped.....Actually I wanted to call a global function foo..
In certain pages i need do to some checks. So I did the following.
//Saving the original func
var org_foo = window.foo;
//Assigning proxy fucnc
window.foo = function(args){
//Performing checks
if(checkCondition(args)){
//Calling original funcs
org_foo(args);
}
};
Thnx this really helped me out
You can override a function using a construct like:
function override(f, g) {
return function() {
return g(f);
};
}
For example:
a = override(a, function(original_a) {
if (condition) { new_code(); original_a(); }
else { original_a(); other_new_code(); }
});
Edit: Fixed a typo.
Passing arbitrary arguments:
a = override(a, function(original_a) {
if (condition) { new_code(); original_a.apply(this, arguments) ; }
else { original_a.apply(this, arguments); other_new_code(); }
});
The answer that #Matthew Crumley provides is making use of the immediately invoked function expressions, to close the older 'a' function into the execution context of the returned function. I think this was the best answer, but personally, I would prefer passing the function 'a' as an argument to IIFE. I think it is more understandable.
var a = (function(original_a) {
if (condition) {
return function() {
new_code();
original_a();
}
} else {
return function() {
original_a();
other_new_code();
}
}
})(a);
The examples above don't correctly apply this or pass arguments correctly to the function override. Underscore _.wrap() wraps existing functions, applies this and passes arguments correctly. See: http://underscorejs.org/#wrap
In my opinion the top answers are not readable/maintainable, and the other answers do not properly bind context. Here's a readable solution using ES6 syntax to solve both these problems.
const orginial = someObject.foo;
someObject.foo = function() {
if (condition) orginial.bind(this)(...arguments);
};
I had some code written by someone else and wanted to add a line to a function which i could not find in the code. So as a workaround I wanted to override it.
None of the solutions worked for me though.
Here is what worked in my case:
if (typeof originalFunction === "undefined") {
originalFunction = targetFunction;
targetFunction = function(x, y) {
//Your code
originalFunction(a, b);
//Your Code
};
}
I've created a small helper for a similar scenario because I often needed to override functions from several libraries. This helper accepts a "namespace" (the function container), the function name, and the overriding function. It will replace the original function in the referred namespace with the new one.
The new function accepts the original function as the first argument, and the original functions arguments as the rest. It will preserve the context everytime. It supports void and non-void functions as well.
function overrideFunction(namespace, baseFuncName, func) {
var originalFn = namespace[baseFuncName];
namespace[baseFuncName] = function () {
return func.apply(this, [originalFn.bind(this)].concat(Array.prototype.slice.call(arguments, 0)));
};
}
Usage for example with Bootstrap:
overrideFunction($.fn.popover.Constructor.prototype, 'leave', function(baseFn, obj) {
// ... do stuff before base call
baseFn(obj);
// ... do stuff after base call
});
I didn't create any performance tests though. It can possibly add some unwanted overhead which can or cannot be a big deal, depending on scenarios.
So my answer ended up being a solution that allows me to use the _this variable pointing to the original object.
I create a new instance of a "Square" however I hated the way the "Square" generated it's size. I thought it should follow my specific needs. However in order to do so I needed the square to have an updated "GetSize" function with the internals of that function calling other functions already existing in the square such as this.height, this.GetVolume(). But in order to do so I needed to do this without any crazy hacks. So here is my solution.
Some other Object initializer or helper function.
this.viewer = new Autodesk.Viewing.Private.GuiViewer3D(
this.viewerContainer)
var viewer = this.viewer;
viewer.updateToolbarButtons = this.updateToolbarButtons(viewer);
Function in the other object.
updateToolbarButtons = function(viewer) {
var _viewer = viewer;
return function(width, height){
blah blah black sheep I can refer to this.anything();
}
};
Not sure if it'll work in all circumstances, but in our case, we were trying to override the describe function in Jest so that we can parse the name and skip the whole describe block if it met some criteria.
Here's what worked for us:
function describe( name, callback ) {
if ( name.includes( "skip" ) )
return this.describe.skip( name, callback );
else
return this.describe( name, callback );
}
Two things that are critical here:
We don't use an arrow function () =>.
Arrow functions change the reference to this and we need that to be the file's this.
The use of this.describe and this.describe.skip instead of just describe and describe.skip.
Again, not sure it's of value to anybody but we originally tried to get away with Matthew Crumley's excellent answer but needed to make our method a function and accept params in order to parse them in the conditional.

Categories

Resources