Skip to content

Instantly share code, notes, and snippets.

@qntm
Last active November 18, 2017 20:33
Show Gist options
  • Save qntm/d899c00aa1ac2c663ac6db23bcffcaba to your computer and use it in GitHub Desktop.
Save qntm/d899c00aa1ac2c663ac6db23bcffcaba to your computer and use it in GitHub Desktop.
An implementation of the JavaScript `==` operator without using `==` itself.
'use strict'
// <http://www.ecma-international.org/ecma-262/5.1/#sec-8> Type
var type = function (x) {
// The `typeof` operator gives us almost what we want
return (
typeof x === 'function' ? 'object'
: x === null ? 'null'
: typeof x
)
}
// <http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.8> DefaultValue
// <http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.2> IsPrimitive
// Always returns a primitive, although not necessarily a
// number or a string. Booleans, `undefined` and `null` are
// also primitives.
var defaultValue = function (o) {
var methodNames = o instanceof Date ? ['toString', 'valueOf'] : ['valueOf', 'toString']
for (var i = 0; i < methodNames.length; i++) {
var methodName = methodNames[i]
var method = o[methodName]
if (method instanceof Function) {
var result = method.bind(o).call()
if (type(result) !== 'object') {
return result
}
}
}
throw new TypeError('Cannot convert object to primitive value')
}
// <http://www.ecma-international.org/ecma-262/5.1/#sec-9.1> ToPrimitive
// <http://www.ecma-international.org/ecma-262/5.1/#sec-9.3> ToNumber
// <http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3> The Abstract Equality Comparison Algorithm
var doubleEquals = function (x, y) {
var typeX = type(x) // "undefined", "null", "boolean", "number", "string" or "object"
var typeY = type(y) // "undefined", "null", "boolean", "number", "string" or "object"
if (typeX === 'object' && typeY === 'object') {
return x === y
}
if (typeX === 'object' && (typeY === 'boolean' || typeY === 'number' || typeY === 'string')) {
x = defaultValue(x)
typeX = type(x) // "undefined", "null", "boolean", "number" or "string"
// And drop through
}
if ((typeX === 'boolean' || typeX === 'number' || typeX === 'string') && typeY === 'object') {
y = defaultValue(y)
typeY = type(y) // "undefined", "null", "boolean", "number" or "string"
// And drop through
}
var nuX = typeX === 'undefined' || typeX === 'null'
var nuY = typeY === 'undefined' || typeY === 'null'
if (nuX && nuY) {
return true
}
if (nuX !== nuY) {
return false
}
// At this point `x` and `y` must each be a Boolean, a number or a string.
if (typeX === 'string' && typeY === 'string') {
return x === y
}
// All other combinations of Boolean, number and string.
// It's not strictly necessary to cast numbers to number but
// it's idempotent. Casting two Booleans to number does not
// alter the result of the comparison.
return +x === +y
}
module.exports = doubleEquals
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment