简体   繁体   中英

JavaScript: Extending Element prototype

I have seen a lot of discussion regarding extending Element . As far as I can tell, these are the main issues:

  • It may conflict with other libraries,
  • It adds undocumented features to DOM routines,
  • It doesn't work with legacy IE, and
  • It may conflict with future changes.

Given a project which references no other libraries, documents changes, and doesn't give a damn for historical browsers:

Is there any technical reason not to extend the Element prototype. Here is an example of how this is useful:

Element.prototype.toggleAttribute=function(attribute,value) {
    if(value===undefined) value=true;
    if(this.hasAttribute(attribute)) this.removeAttribute(attribute);
    else this.addAttribute(attribute,value);
};

I've seen too many comments about the evils of extending prototypes without offering a reasonable explanation.

Note 1: The above example is possibly too obvious, as toggleAttribute is the sort of method which might be added in the future. For discussion, imagine that it's called manngoToggleAttribute .

Note 2: I have removed a test for whether the method already exists. Even if such a method already exists, it is more predictable to override it. In any case, I am assuming that the point here is that the method has not yet been defined, let alone implemented . That is the point here.

Note 3: I see that there is now a standard method called toggleAttribute which doesn't behave exactly the same. With modification, the above would be a simple polyfill. This doesn't change the point of the question.

Is it ok? Technically yes. Should you extend native APIs? As a rule of thumb no. Unfortunately the answer is more complex. If you are writing a large framework like Ember or Angular it may be a good idea to do so because your consumers will have Benifits if better API convenience. But if you're only doing this for yourself then the rule of thumb is no.

The reasoning is that doing so destabilizes the trust of that object. In other words by adding, changing, modifying a native object it no longer follows the well understood and documented behavior that anyone else (including your future self) will expect.

This style hides implementation that can go unnoticed. What is this new method? , Is it a secret browser thing? , what does it do? , Found a bug do I report this to Google or Microsoft now? . A bit exaggerated but the point is that the truth of an API has now changed and it is unexpected in this one off case. It makes maintainability need extra thought and understanding that would not be so if you just used your own function or wrapper object. It also makes changes harder.

Relevant post: Extending builtin natives. Evil or not?

Instead of trying to muck someone else's (or standard) code just use your own.

function toggleAttribute(el, attribute, value) {
  var _value = (value == null ? true : value;
  if (el.hasAttribute(attribute)) {
    el.removeAttribute(attribute);
  } else {
    el.addAttribute(attribute, _value);
  }
};

Now it is safe, composible, portable, and maintainable. Plus other developers (including your future self) won't scratch their heads confused where this magical method that is not documented in any standard or JS API came from.

Do not modify objects you don't own.

Imagine a future standard defines Element.prototype.toggleAttribute . Your code checks if it has a truthy value before assigning your function. So you could end up with the future native function, which may behave differently than what you expected.

Even more, just reading Element.prototype.toggleAttribute might call a getter, which could run some code with undesired sideways effects. For example, see what happens when you get Element.prototype.id .

You could skip the check and assign your function directly. But that could run a setter, with some undesired sideways effects, and your function wouldn't be assigned as the property.

You could use a property definition instead of a property assignment. That should be safer... unless Element.prototype has some special [[DefineOwnProperty]] internal method (eg is a proxy).

It might fail in lots of ways. Don't do this.

In my assessment: no

Massive overwriting Element.prototype slow down performance and can conflict with standardization, but a technical reason does not exist.

I'm using several Element.prototype custom methods. so far so good until I observe a weird behaviour.

<!DOCTYPE html >
<html >
<body>
<script>
function doThis( ){
    alert('window');
}
HTMLElement.prototype.doThis = function(  ){
    alert('HTMLElement.prototype');
}
</script>
<button onclick="doThis( )" >Do this</button>
</body>
</html>

when button is clicked, the prototype method is executed instead of the global one. The browser seems to assume this.doThis() which is weird. To overcome, I have to use window.doThis() in the onclick.

It might be better if w3c can come with with diff syntax for calling native/custom methods eg

myElem.toggleAttribute() // call native method
myElem->toggleAttribute() // call custom method

Is there any technical reason not to extend the Element prototype.

Absolutely none!

pardon me: ABSOLUTELY NONE!

In addition the .__proto__ , was practically an a illegal (Mozilla) prototype extension until yesterday. - Today, it's a Standard.


ps: You should avoid the use of if(!Element.prototype.toggleAttribute) syntax by any means, the if("toggleAttribute" in Element.prototype) will do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM