Introducing Break the Web: Array extra methods case

Introducing Break the Web: Array extra methods case

Introducing Break the Web: Array extra methods case.

09b5a917b6ea8928e0d5bf8a432412fe?s=128

Yusuke SUZUKI

October 29, 2014
Tweet

Transcript

  1. Introducing Break the Web: Array extra methods case Yusuke Suzuki

    @Constellation utatane.tea@gmail.com 2014/10/29
  2. Background: ES6 Array extensions • Upcoming ES6 adds many useful

    methods to Array.prototype – Array.prototype.values – Array.prototype.keys – Array.prototype.entries – Array.prototype.find – etc.
  3. Problem: Break the Web 1. You already added these methods

    – by extending Array.prototype – Sometimes, your crafted methods don’t have the same semantics to the ES6 standards 2. Too generic name to add it – “values”, “keys” etc. • In this talk, we’ll show the actual BtW cases
  4. Problem: Well-known with problem • Newly added properties will hide

    the variable in the with block • @@unscopables (in ES6) hides some properties in with block – (“values”, “entries” etc.) (function () { var values = []; // If object inherits Array.prototype, inner "values" variable becomes // Array.prototype. with (object) { values = ...; } })());
  5. Outline • Background • Problem • Case Study • Conclusion

  6. Case Study 1: MooTools • MooTools has its own Array#contains

    – FYI: MooTools also has its own String#contains • However, it checks the methods enumerability when extending the other object – So MooTools doesn’t consider about newly added Array methods
  7. Case Study 1: MooTools • Investigating MooTools and non-enumerable object.forEachMethod

    = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; Array.implement({ // ... contains: function(item, from){ return this.indexOf(item, from) != -1; }, // ... });
  8. Case Study 1: MooTools • Investigating MooTools and non-enumerable Only

    listed standard methods are considered object.forEachMethod = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; Array.implement({ // ... contains: function(item, from){ return this.indexOf(item, from) != -1; }, // ... });
  9. Case Study 1: MooTools • Investigating MooTools and non-enumerable Only

    listed standard methods are considered object.forEachMethod = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; Array.implement({ // ... contains: function(item, from){ return this.indexOf(item, from) != -1; }, // ... }); There’s no standard method definition, but it becomes non-enumerable
  10. Case Study 1: MooTools • Investigating MooTools and non-enumerable Only

    listed standard methods are considered object.forEachMethod = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; Array.implement({ // ... contains: function(item, from){ return this.indexOf(item, from) != -1; }, // ... }); There’s no standard method definition, but it becomes non-enumerable Array.prototype.contains = function () { ...; }; If there’s non-enumerable “contains”, it becomes non-enumerable
  11. Case Study 1: MooTools • And Break the Web ...('Array',

    Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ]) ... // Now, 'contains' is not enumerated. Array.forEachMethod(function(method, name){ Elements.implement(name, method); }); element.contains(...);
  12. Case Study 1: MooTools • And Break the Web There’s

    no “contains” ...('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ]) ... // Now, 'contains' is not enumerated. Array.forEachMethod(function(method, name){ Elements.implement(name, method); }); element.contains(...);
  13. Case Study 1: MooTools • And Break the Web There’s

    no “contains” There’s no “contains” in Element ...('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ]) ... // Now, 'contains' is not enumerated. Array.forEachMethod(function(method, name){ Elements.implement(name, method); }); element.contains(...);
  14. Case Study 1: MooTools • And Break the Web There’s

    no “contains” There’s no “contains” in Element ...('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ]) ... // Now, 'contains' is not enumerated. Array.forEachMethod(function(method, name){ Elements.implement(name, method); }); element.contains(...); There’s no element.contains!
  15. Case Study 1: MooTools • This breaks the jsfiddle.net –

    https://bugzilla.mozilla.org/show_bug.cgi?id=107505 9 • MooTools update is introduced – https://github.com/mootools/mootools- core/commit/521471f5c0617edc12c680cdad8346c3e b95776a • es-discuss is going – https://esdiscuss.org/topic/having-a-non- enumerable-array-prototype-contains-may-not-be- web-compatible – https://esdiscuss.org/topic/array-prototype-contains- solutions
  16. Case Study 2: Breaking outlook.com • Array.prototype.values breaks the existing

    code in outlook.com – http://windowssystemspecialist.blogspot.jp/2014/10/fixing-blank-calendar- with-chrome-38.html
  17. Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8

    breaks the outlook.com in stable 38 // Old code. "values" in n n
  18. Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8

    breaks the outlook.com in stable 38 // Old code. "values" in n Array.prototype n [[Prototype]] … Object.prototype [[Prototype]]
  19. Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8

    breaks the outlook.com in stable 38 // Old code. "values" in n Array.prototype n [[Prototype]] … Object.prototype [[Prototype]] “values”: …
  20. Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8

    breaks the outlook.com in stable 38 // Old code. "values" in n Array.prototype n [[Prototype]] … Object.prototype [[Prototype]] “values”: … Trapped Becomes always true
  21. Case Study 2: Breaking outlook.com • Exposing Array.prototype.values in V8

    breaks the outlook.com in stable 38 // Old code. "values" in n Array.prototype n [[Prototype]] … Object.prototype [[Prototype]] “values”: … Trapped Becomes always true // Appropriate code. n.hasOwnProperty("values”)
  22. Case Study 2: Breaking outlook.com • Once Chrome stable 38

    is shipped, but updated later
  23. Case Study 2: Breaking outlook.com • Once Chrome stable 38

    is shipped, but updated later
  24. Outline • Background • Problem • Case Study • Conclusion

  25. Conclusion • Take the appropriate way – e.g. If you’d

    like to check own property existence, you should use obj.hasOwnProperty • Don’t trust the current shape of Objects overly – ECMAScript standards will extend the prototypes • And don’t use with – Use “use strict”