Last active
August 29, 2015 14:08
-
-
Save royling/70830c14ecb8ecd2ce22 to your computer and use it in GitHub Desktop.
#explain-js-type-coercion-in-js: EcmaScript Internal [[ToPrimitive]]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe("isPrimitive", function() { | |
it("should return true for primitive values", function() { | |
expect(isPrimitive(0)).toBe(true); | |
expect(isPrimitive('')).toBe(true); | |
expect(isPrimitive(false)).toBe(true); | |
expect(isPrimitive(null)).toBe(true); | |
expect(isPrimitive(undefined)).toBe(true); | |
expect(isPrimitive(NaN)).toBe(true); | |
}); | |
it("should return false for others", function() { | |
expect(isPrimitive({})).toBe(false); | |
expect(isPrimitive([])).toBe(false); | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function toPrimitive(value, preferredType) { | |
if (isPrimitive(value)) return value; | |
if (undefined === preferredType) { | |
// omitted | |
// convert to string for dates and number for other values | |
preferredType = value.constructor === Date ? 'string' : 'number'; | |
} | |
var ret; | |
if ('number' === preferredType) { | |
ret = use(value, 'valueOf'); | |
if (ret.done) return ret.value; | |
ret = use(value, 'toString'); | |
if (ret.done) return ret.value; | |
fail(); | |
} else if ('string' === preferredType) { | |
ret = use(value, 'toString'); | |
if (ret.done) return ret.value; | |
ret = use(value, 'valueOf'); | |
if (ret.done) return ret.value; | |
fail(); | |
} | |
} | |
function isPrimitive(value) { | |
// nonvalues: null or undefined | |
if (value === null || value === undefined) return true; | |
switch (typeof value) { | |
case 'string': case 'number': case 'boolean': | |
return true; | |
default: | |
return false; | |
} | |
} | |
function use(value, method) { | |
var result, done = false; | |
if (typeof value[method] == 'function') { | |
result = value[method](); | |
done = isPrimitive(result); | |
} | |
return { done: done, value: result }; | |
} | |
function fail() { | |
throw new TypeError('Cannot convert to a primitive value!'); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe("toPrimitive", function() { | |
it("should return primitive as is", function() { | |
[0, NaN, '', false, null, undefined].forEach(function(value) { | |
expect(toPrimitive(value)).toEqual(value); | |
}); | |
}); | |
it("should use valueOf() if preferred is number", function() { | |
var obj = Object.create(null); | |
obj.valueOf = function() { | |
return 'test'; | |
}; | |
expect(toPrimitive(obj, 'number')).toEqual('test'); | |
}); | |
it("should use toString() if preferred is number and no valueOf()", function() { | |
var obj = Object.create(null); | |
obj.toString = function() { | |
return 'test'; | |
}; | |
expect(toPrimitive(obj, 'number')).toEqual('test'); | |
}); | |
it("should use valueOf() if preferred is string", function() { | |
var obj = Object.create(null); | |
obj.toString = function() { | |
return 'test'; | |
}; | |
expect(toPrimitive(obj, 'string')).toEqual('test'); | |
}); | |
it("should use valueOf() if preferred is string and no toString()", function() { | |
var obj = Object.create(null); | |
obj.valueOf = function() { | |
return 'test'; | |
}; | |
expect(toPrimitive(obj, 'string')).toEqual('test'); | |
}); | |
it("should fail if neither valueOf() nor toString() exists", function() { | |
var f = function() { | |
return toPrimitive(Object.create(null)); | |
}; | |
expect(f).toThrow(); | |
}); | |
it("should default preferredType as number if it's omitted", function() { | |
var obj = Object.create(null); | |
obj.valueOf = function() { | |
return 'valueOf'; | |
}; | |
obj.toString = function() { | |
return 'toString'; | |
}; | |
expect(toPrimitive(obj)).toEqual('valueOf'); | |
}); | |
it("should default preferredType as string if it's omitted and a Date object", function() { | |
var now = new Date(); | |
now.toString = function() { | |
return 'now'; | |
}; | |
now.valueOf = function() { | |
return 'valueOf'; | |
}; | |
expect(toPrimitive(now)).toEqual('now'); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment