TLDR
Примус типу або неявна конверсія типів дозволяє слабко вводити текст і використовується в усьому JavaScript. Більшість операторів (за винятком винятків операторів суворої рівності ===та !==) та операцій з перевірки значення (наприклад, if(value)...) будуть примушувати надані їм значення, якщо типи цих значень не одразу сумісні з операцією.
Точний механізм, який використовується для примусового значення, залежить від вираження, що оцінюється. У питанні використовується оператор додавання .
Оператор додавання спочатку переконається, що обидва операнди є примітивними, що в цьому випадку передбачає виклик valueOfметоду. toStringМетод не викликається в даному випадку , оскільки переопределяется valueOfметод на об'єкті xповертає елементарне значення.
Потім, оскільки один із операндів у питанні є рядком, обидва операнди перетворюються на рядки. Цей процес використовує абстрактні, внутрішні операції ToString(примітка: з великої літери) і відрізняється від toStringметоду на об'єкті (або його прототипі ланцюга).
Нарешті, отримані рядки з'єднуються.
Деталі
На прототипі кожного об'єкта функції конструктора, відповідного кожному мовному типу в JavaScript (тобто номер, BigInt, String, Boolean, Symbol та Object), є два способи: valueOfі toString.
Мета valueOf- отримати первісне значення, пов'язане з об'єктом (якщо він має його). Якщо об’єкт не має основного примітивного значення, то об'єкт просто повертається.
Якщо valueOfвиклик проти примітиву, то примітив автоматично заповнюється в звичайний спосіб, а базове примітивне значення повертається. Зауважте, що для рядків основне примітивне значення (тобто значення, яке повертається valueOf) є самим представленням рядків.
Наступний код показує, що valueOfметод повертає основне примітивне значення з об’єкта обгортки, і він показує, як немодифіковані екземпляри об'єкта, які не відповідають примітивам, не мають примітивного значення для повернення, тому вони просто повертаються самі.
console.log(typeof new Boolean(true)) // 'object'
console.log(typeof new Boolean(true).valueOf()) // 'boolean'
console.log(({}).valueOf()) // {} (no primitive value to return)
Мета toString, з іншого боку, - повернути рядкове представлення об'єкта.
Наприклад:
console.log({}.toString()) // '[object Object]'
console.log(new Number(1).toString()) // '1'
У більшості операцій JavaScript безшумно намагатиметься перетворити один або кілька операндів у потрібний тип. Така поведінка була обрана для полегшення використання JavaScript. JavaScript спочатку не мав винятків , і це, можливо, також зіграло певну роль у цьому дизайнерському рішенні. Цей вид неявного перетворення типу називається типом примусу, і він є основою слабкої (слабкої) системи JavaScript. Складні правила, що стоять за такою поведінкою, покликані перенести складність введення тексту в саму мову та з вашого коду.
Під час процесу примусу можуть відбуватися два режими перетворення:
- Перетворення об'єкта в примітив (який може включати перетворення типу), і
- Пряме перетворення до примірника типу специфічний, використовуючи функцію конструктора об'єкт одного з примітивних типів (тобто ..
Number(), Boolean(), І String()т.д.)
Перетворення в примітив
При спробі перетворити непримітивні типи в примітиви, якими слід керувати, абстрактна операція ToPrimitiveвикликається необов'язковим "натяком" на "число" або "рядок". Якщо підказку пропущено, підказкою за замовчуванням є "число" (якщо @@toPrimitiveметод не був переопрацьований). Якщо натяк 'string', то toStringспробується перший, а valueOfдругий, якщо toStringне повернув примітив. Інше, навпаки. Підказка залежить від операції, яка вимагає перетворення.
Оператор додавання не надає підказки, тому valueOfспробується спочатку. Оператор віднімання надає підказку «число», тому valueOfспробується спочатку. Єдині ситуації, які я можу знайти в специфікації, в якій натяк "строка", є:
Object#toString
- Абстрактна операція
ToPropertyKey, яка перетворює аргумент у значення, яке може використовуватися як ключ властивості
Пряме перетворення типу
У кожного оператора є свої правила завершення своєї роботи. Оператор додавання спочатку використає ToPrimitiveдля забезпечення кожного операнда примітивом; тоді, якщо будь-який операнд є рядком, то він навмисно буде викликати абстрактну операцію ToStringна кожному операнді, щоб надати порядок конкатенації рядків, який ми очікуємо за допомогою рядків. Якщо після ToPrimitiveкроку обидва операнди не є рядками, то виконується арифметичне додавання.
На відміну від додавання, оператор віднімання не має перевантаженої поведінки, і тому він буде викликати toNumericкожен операнд, попередньо перетворивши їх у примітиви за допомогою ToPrimitive.
Так:
1 + 1 // 2
'1' + 1 // '11' Both already primitives, RHS converted to string, '1' + '1', '11'
1 + [2] // '12' [2].valueOf() returns an object, so `toString` fallback is used, 1 + String([2]), '1' + '2', 12
1 + {} // '1[object Object]' {}.valueOf() is not a primitive, so toString fallback used, String(1) + String({}), '1' + '[object Object]', '1[object Object]'
2 - {} // NaN {}.valueOf() is not a primitive, so toString fallback used => 2 - Number('[object Object]'), NaN
+'a' // NaN `ToPrimitive` passed 'number' hint), Number('a'), NaN
+'' // 0 `ToPrimitive` passed 'number' hint), Number(''), 0
+'-1' // -1 `ToPrimitive` passed 'number' hint), Number('-1'), -1
+{} // NaN `ToPrimitive` passed 'number' hint', `valueOf` returns an object, so falls back to `toString`, Number('[Object object]'), NaN
1 + 'a' // '1a' Both are primitives, one is a string, String(1) + 'a'
1 + {} // '1[object Object]' One primitive, one object, `ToPrimitive` passed no hint, meaning conversion to string will occur, one of the operands is now a string, String(1) + String({}), `1[object Object]`
[] + [] // '' Two objects, `ToPrimitive` passed no hint, String([]) + String([]), '' (empty string)
1 - 'a' // NaN Both are primitives, one is a string, `ToPrimitive` passed 'number' hint, 1-Number('a'), 1-NaN, NaN
1 - {} // NaN One primitive, one is an object, `ToPrimitive` passed 'number' hint, `valueOf` returns object, so falls back to `toString`, 1-Number([object Object]), 1-NaN, NaN
[] - [] // 0 Two objects, `ToPrimitive` passed 'number' hint => `valueOf` returns array instance, so falls back to `toString`, Number('')-Number(''), 0-0, 0
Зауважте, що Dateвласне об'єкт унікальний тим, що це єдиний внутрішній @@toPrimitiveспосіб, який переорієнтовує метод за замовчуванням , у якому підказка за замовчуванням вважається "рядок" (а не "число"). Причиною цього є те, що Dateекземпляри перекладають на читабельні рядки за замовчуванням замість їх числового значення для зручності програміста. Ви можете змінювати @@toPrimitiveвласні об'єкти за допомогою Symbol.toPrimitive.
Наступна сітка показує результати примусу для оператора абстрактної рівності ( ==) ( джерело ):

Дивіться також .
window.console.log (x);чиalert (x);?