Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NodeConf Remote 2020: JavaScript Prototypes Behind the Scenes

NodeConf Remote 2020: JavaScript Prototypes Behind the Scenes

JavaScript is a prototype-oriented language. But for many, what this really means or how it actually works is a little fuzzy. It seems, that as quickly as you learn how prototypes work behind the scenes, as quickly do you forget it again. If you can relate to this feeling, this talk is for you.

We’ll dive into why JavaScript is a prototype-oriented language instead of class-based and what that means for you as a developer. You’ll learn exactly how prototypal inheritance works in JavaScript - in a way that’s easy to remember - so that you can become a better programmer.

Finally, you’ll learn about the security pitfalls that exist in the way prototypal inheritance is implemented in JavaScript and how to write more secure code to avoid those pitfalls.

Thomas Watson

November 03, 2020
Tweet

More Decks by Thomas Watson

Other Decks in Programming

Transcript

  1. wa7son Who am I? • Thomas Watson • Principal Software

    Engineer at Elastic • Open Source developer at github.com/watson • Node.js Core Member • Tweets as @wa7son • Slides: github.com/watson/talks
  2. wa7son Image - height - width - bitmap Button -

    height - width - label - onClick()
  3. wa7son Button - label - onClick() Image - bitmap -

    onClick() UIElement - height - width
  4. wa7son Button - label Image - bitmap UIElement - height

    - width ClickableElement - onClick()
  5. wa7son Banana / Gorilla Problem wa7son “You wanted a banana

    but what you got was a gorilla holding the banana and the entire jungle” - Joe Armstrong, the creator of Erlang
  6. wa7son wa7son Delegates • Shared properties / methods • Don’t

    find what you’re looking for on the object? Look at its prototype! • Conserve memory Prototypes
  7. wa7son Java’isk Object Creation wa7son function Cat (name) { this.name

    = name } Cat.prototype.talk = function () { return `Meow, I'm ${this.name}!` } const whiskers = new Cat('Mr. Whiskers') whiskers.talk() // Meow, I'm Mr. Whiskers!
  8. wa7son Cleaner Prototype Object Creation wa7son const proto = {

    talk () { return `Meow, I'm ${this.name}!` } } const whiskers = Object.create(proto) whiskers.name = 'Mr. Whiskers' whiskers.talk() // Meow, I'm Mr. Whiskers!
  9. wa7son wa7son Delegates Cloning / Concatenation • Shared properties /

    methods • Don’t find what you’re looking for on the object? Look at its prototype! • Conserve memory • Default state / mixins Prototypes
  10. wa7son Mixing it up wa7son class Image extends UIElement {

    // ... } const myImage = new Image() Object.assign(myImage, Clickable)
  11. wa7son wa7son Delegates Cloning / Concatenation • Shared properties /

    methods • Don’t find what you’re looking for on the object? Look at its prototype! • Conserve memory • Default state / mixins Prototypes
  12. {} Object Object.prototype Function Function.prototype null Object Function __proto__ __proto__

    __proto__ __proto__ __proto__ prototype prototype constructor constructor constructor constructor constructor Inherited Own © Thomas Watson // @wa7son
  13. {} Object Object.prototype Function Function.prototype null Object Function __proto__ __proto__

    __proto__ __proto__ __proto__ prototype prototype constructor constructor constructor constructor constructor Inherited Own {} __proto__ constructor Object with custom prototype © Thomas Watson // @wa7son
  14. MyObj.prototype Object Object.prototype Function Function.prototype null Object Function © Thomas

    Watson // @wa7son __proto__ __proto__ __proto__ __proto__ __proto__ prototype prototype constructor constructor constructor constructor constructor Inherited Own new MyObj() __proto__ constructor MyObj prototype constructor __proto__
  15. wa7son How to clone an object wa7son function merge (target,

    source) { for (const [key, value] of Object.entries(source)) { target[key] = value } return target } merge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 } merge( { a: { foo: 1 } }, { a: { bar: 2 } } ) // { a: { bar: 2 } }
  16. wa7son wa7son function merge (target, source) { for (const [key,

    value] of Object.entries(source)) { target[key] = value } return target } merge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 } merge( { a: { foo: 1 } }, { a: { bar: 2 } } ) // { a: { bar: 2 } } How to clone an object
  17. wa7son wa7son function merge (target, source) { for (const [key,

    value] of Object.entries(source)) { target[key] = value } return target } merge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 } merge( { a: { foo: 1 } }, { a: { bar: 2 } } ) // { a: { bar: 2 } } ✅ ❌ How to clone an object
  18. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object
  19. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object
  20. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object
  21. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object
  22. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object ✅ deepMerge( { a: { foo: 1 } }, { a: { bar: 2 } } ) // { a: { foo: 1, bar: 2 } }
  23. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  24. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  25. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  26. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  27. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  28. wa7son wa7son • Look out for `__proto__` in untrusted user

    input • Look out for `constructor.prototype` in untrusted user input The Antidote
  29. {} Object Object.prototype Function Function.prototype null Object Function __proto__ __proto__

    __proto__ __proto__ __proto__ prototype prototype constructor constructor constructor constructor constructor Inherited Own © Thomas Watson // @wa7son
  30. wa7son wa7son • Look out for `__proto__` in untrusted user

    input • Look out for `constructor.prototype` in untrusted user input npm install secure-json-parse The Antidote