[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, напишите мне по электронной почте. Я нанимаю.