Skip to content

Instantly share code, notes, and snippets.

@towerofnix
Last active November 7, 2016 21:14
Show Gist options
  • Save towerofnix/de91f087b43d83171eaf62cedb1f1183 to your computer and use it in GitHub Desktop.
Save towerofnix/de91f087b43d83171eaf62cedb1f1183 to your computer and use it in GitHub Desktop.
Why is this so fast
const createObject = function() {
const properties = new Map()
let prototype = null
const obj = {
get: function(key, bindFunction = true) {
let value
// If we don't have this property, and we do have a prototype, search
// the prototype. For prototype searching bindFunction is false, since we
// can't rebind a function.
if (properties.has(key)) {
value = properties.get(key)
} else if (prototype) {
value = prototype.get(key, false)
} else {
value = undefined
}
// Functions should be bound to "this".. unless we're not at the top of
// the prototype chain!
if (bindFunction && value instanceof Function) {
value = value.bind(obj)
}
return value
},
set: function(key, val) {
properties.set(key, val)
return val
},
setPrototype: function(protoObj) {
prototype = protoObj
}
}
return obj
}
const createObjectProxy = function() {
const properties = new Map()
let prototype = null
let shouldBindFunction = true
return new Proxy({}, {
get: function(target, key, proxy) {
if (key === '__NO_FN_BIND_NEXT__') {
shouldBindFunction = false
return
}
if (properties.has(key)) {
value = properties.get(key)
} else if (prototype) {
prototype.__NO_FN_BIND_NEXT__
value = prototype[key]
} else {
value = undefined
}
if (shouldBindFunction && value instanceof Function) {
value = value.bind(proxy)
}
// Reset should bind function incase __NO_FN_BIND_NEXT__ was used
shouldBindFunction = true
return value
},
set: function(target, key, value) {
properties.set(key, value)
},
setPrototypeOf: function(target, proto) {
prototype = proto
return true
}
})
}
const timeit = function(msg, times, fn) {
const old = Date.now()
for (let i = 0; i < times; i++) {
fn()
}
const time = Date.now() - old
console.log(`${msg} * ${times}: ${time}ms`)
}
timeit('Custom library', 1000000, () => {
const cat = createObject()
cat.set('sayName', function() {
this.get('name')
})
const mark = createObject()
mark.setPrototype(cat)
mark.set('name', 'Mark')
mark.get('sayName')()
})
timeit('Custom library (proxy syntax)', 1000000, () => {
const cat = createObjectProxy()
cat.sayName = function() {
this.name
}
const mark = createObjectProxy()
Object.setPrototypeOf(mark, cat)
mark.name = 'Mark'
mark.sayName()
})
timeit('Native JS', 1000000, () => {
const cat = {
sayName: function() {
this.name
}
}
const mark = Object.create(cat)
mark.name = 'Mark'
mark.sayName()
})
@towerofnix
Copy link
Author

towerofnix commented Nov 7, 2016

Chrome Results

Custom library * 1000000: 752ms
Custom library (proxy syntax) * 1000000: 3370ms
Native JS * 1000000: 6132ms

Chrome Conclusion

  • JavaScript's native prototype system is slow.
  • JavaScript's native Proxys are slow.
  • For the best performance, rewrite JavaScript in JavaScript.

@towerofnix
Copy link
Author

towerofnix commented Nov 7, 2016

Firefox Results

Custom library * 1000000: 2965ms
Custom library (proxy syntax) * 1000000: 11336ms
Native JS * 1000000: 2690ms
Custom library * 10000000: 30013ms
Custom library (proxy syntax) * 10000000: 107851ms
Native JS * 10000000: 27432ms

Firefox Conclusion

  • JavaScript's native prototype system is okay I guess.
  • JavaScript's native Proxys are TERRIBLE and you should NEVER EVER EVER use them.
  • For the best performance, be smart and stick to native JavaScript stuff.

@towerofnix
Copy link
Author

towerofnix commented Nov 7, 2016

Safari Results

Custom library * 1000000: 1802ms
Custom library (proxy syntax) * 1000000: 3597ms
Native JS * 1000000: 963ms
Custom library * 10000000: 14900ms
Custom library (proxy syntax) * 10000000: 33182ms
Native JS * 10000000: 9421ms

Safari Conclusions

  • JavaScript's native prototype system is pretty fast.
  • JavaScript's native Proxys are STILL TERRIBLE and you should REALLY STILL NEVER use them.
  • For the best performance, be smart and stick to native JavaScript stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment