[object Object] То, чего вы не знали о valueOf
В порядке прекрасно. Это название ужасно. Но сейчас мы все конкурируем с BuzzFeed за читателей, и мне нужно было что-нибудь запоминающееся, так что вот и мы.
Прямо тогда.
Итак, что такое Object # valueOf и почему это должно вас волновать?
Более или менее, это метод, который JavaScript вызывает автоматически каждый раз, когда видит объект в ситуации, когда ожидается примитивное значение.
Давайте посмотрим на небольшой пример.
var obj = {}; console.log( 7 + obj ); // “7[object Object]”
О безумие неявного преобразования типов в JavaScript написано много, поэтому я действительно не заинтересован в том, чтобы идти по этому пути. Надеюсь, ваш вывод из примера заключается в том, что мы использовали оператор сложения с числом с одной стороны и объектом с другой, и мы получили значение, которое на самом деле не имеет большого смысла.
Впрочем, здесь вряд ли можно винить JavaScript. У него нет способа узнать, как преобразовать наш объект в число.
Но, как назло, мы действительно можем определить это поведение.
var obj = { valueOf: function() { return 42; } }; console.log( 7 + obj ); // 49
Видеть? Я раньше не лгал. Спецификация EcmaScript диктует, что всякий раз, когда объект встречается в ситуации, когда ожидается примитив, будет вызываться метод valueOf этого объекта *.
* На самом деле, иногда вместо этого вызывается toString (), но я не буду здесь вдаваться в подробности. Вы можете предположить, что каждый раз, когда ожидается Number, будет вызываться valueOf. Если вас это очень интересует, поищите абстрактную операцию ToPrimitive в спецификации ES.
Какая разница?
Послушайте, я не собираюсь лгать вам здесь (я чувствую, что мы становимся друзьями, и у меня патологическая потребность в доверии). Информация о valueOf не повлияет существенно на то, как вы пишете код в повседневной жизни. Этот пост посвящен небольшой интересной функции JS, о которой многие люди не знают. Но при этом есть несколько практических приложений.
Напишем небольшой класс Integer. Я не буду здесь вдаваться в подробности, так как предполагаю, что вы, уважаемый читатель, уже хорошо знакомы с концепцией целых чисел.
Вы можете просто пропустить эту часть, если хотите ...
function Int( val ) { if ( !( this instanceof Int ) ) { return new Int( val ); } if ( isNaN( val ) ) { throw new TypeError(‘Int value must be a number’); } if ( val % 1 !== 0 ) { throw new TypeError(‘Int value must be an integer’); } if ( val instanceof Int ) { return Int( val._value ); } this._value = Math.floor( val ); } Int.prototype.add = function( n ) { return Int( this._value + Int( n )._value ); }; Int.prototype.subtract = function( n ) { return Int( this._value — Int( n )._value ); }; Int.prototype.multiply = function( n ) { return Int( this._value * Int( n )._value ); }; Int.prototype.divide = function( n ) { try { return Int( this._value / Int( n )._value ); } catch ( e ) { throw new Error(‘Division resulted in non-integer quotient’); } };
Хорошо. Теперь у нас есть класс Int.
Он выполняет некоторую проверку типов. Это гарантирует, что вы не пройдете мимо поплавка. И в нем есть несколько основных арифметических методов.
Вот пара быстрых примеров, чтобы вы лучше поняли, как это выглядит:
Int( 6 ).multiply( 2 ); // { _value: 12 } Int( 4 ).divide( 2 ).add( 3 ); // { _value: 5 }
Достаточно просто. Но что, если мне нужно сделать что-то подобное?
var a = Int( 6 ); var b = Int( 11 ); Math.min( a, b ); // NaN
Он практически взрывается. Я имею в виду, что он не вызывает исключения, но дает мне NaN, что почти бесполезно.
Я надеюсь, что на этом этапе вы видите, куда мы движемся.
Int.prototype.valueOf = function() { return this._value; };
Вот так просто. Теперь мы попробуем тот же пример, но на этот раз наш класс Int имеет метод valueOf в своем прототипе.
var a = Int( 6 ); var b = Int( 11 ); Math.min( a, b ); // 6
Неплохо, правда? Мы также можем использовать экземпляры Int в других местах, где ожидается примитивное значение:
Math.pow( Int( 9 ), 0.5 ); // 3
Чем больше вы знаете ™
— -
Если вам понравился этот пост, подпишитесь на меня в Medium или Twitter, чтобы узнать больше.
И если вы живете в районе Бостона и хотите работать со мной над безумными сверхсекретными вещами в Project Decibel, напишите мне по электронной почте. Я нанимаю.