تبدیل نوع (coercion) به این معناست که یک مقدار از یک نوع، به نمایش متناظر خود در نوعی دیگر تبدیل شود (تا جایی که امکانپذیر باشد). همانطور که در فصل ۴ بررسی خواهیم کرد، coercion یکی از ستونهای اصلی زبان JS است، نه یک ویژگی اختیاری که بتوان بهراحتی از آن اجتناب کرد.
اما جایی که coercion با عملگرهای مقایسه (مثل برابری) ترکیب میشود، معمولاً باعث سردرگمی و نارضایتی میشود.
کمتر ویژگیای در JS به اندازه عملگر == (که معمولاً «برابری سست» یا loose equality نامیده میشود) در جامعه JS مورد انتقاد قرار گرفته است. بیشتر مطالب و بحثهای عمومی درباره JS این عملگر را بدطراحیشده و خطرناک/مستعد باگ میدانند. حتی خالق زبان، Brendan Eich، نیز از طراحی آن بهعنوان یک اشتباه بزرگ یاد کرده است.
تا جایی که میتوانم تشخیص دهم، بیشتر این نارضایتیها از تعداد نسبتاً کمی از موارد خاص گیجکننده ناشی میشود، اما مشکل عمیقتر، یک سوءبرداشت بسیار رایج است: اینکه == بدون در نظر گرفتن نوع (type) مقادیر، مقایسه انجام میدهد.
عملگر == مقایسه برابری را تقریباً مشابه === انجام میدهد. در واقع، هر دو عملگر نوع مقادیر را در نظر میگیرند. و اگر دو مقدار از یک نوع باشند، == و === دقیقاً یک کار را انجام میدهند و هیچ تفاوتی ندارند.
اگر نوع مقادیر متفاوت باشد، تفاوت == با === این است که == اجازه میدهد قبل از مقایسه، تبدیل نوع انجام شود. به عبارت دیگر، هر دو عملگر میخواهند مقادیری با نوع یکسان را مقایسه کنند، اما == ابتدا اجازه تبدیل نوع میدهد و پس از یکسان شدن نوعها، همانند === عمل میکند. بنابراین، بهجای «برابری سست»، بهتر است == را "برابری همراه با تبدیل نوع (coercive equality) "بنامیم.
42 == "42"; // true 1 == true; // true
در هر دو مقایسه، نوع مقادیر متفاوت است، بنابراین == باعث میشود مقادیر غیرعددی ("42" و true) به عدد تبدیل شوند (بهترتیب 42 و 1) و سپس مقایسه انجام شود.
فقط با آگاهی از این ویژگی ==—اینکه تمایل دارد مقایسهها را بهصورت عددی انجام دهد—میتوانید از بیشتر موارد مشکلساز اجتناب کنید، مثلاً دوری از مواردی مثل "" == 0 یا 0 == false.
ممکن است فکر کنید: «خب، من همیشه از === استفاده میکنم تا از این مشکلات دور بمانم!» اما واقعیت این است که این کار به آن سادگی که فکر میکنید نیست.
به احتمال زیاد از عملگرهای مقایسهای مانند < و > (و حتی <= و >=) استفاده خواهید کرد.
این عملگرها نیز مانند == عمل میکنند: اگر نوع مقادیر یکسان باشد، رفتار «سختگیرانه» دارند، اما اگر نوعها متفاوت باشند، ابتدا coercion انجام میدهند (معمولاً به عدد) و سپس مقایسه میکنند.
مثال:
var arr = [ "1", "10", "100", "1000" ]; for (let i = 0; i < arr.length && arr[i] < 500; i++) { // will run 3 times }
مقایسه i < arr.length از coercion در امان است، چون هر دو مقدار همیشه عدد هستند. اما arr[i] < 500 شامل coercion میشود، چون مقادیر arr[i] همگی رشته هستند. بنابراین این مقایسهها به این شکل تبدیل میشوند: 1 < 500، 10 < 500، 100 < 500 و 1000 < 500. از آنجا که مورد چهارم false است، حلقه بعد از سه بار اجرا متوقف میشود.
این عملگرهای مقایسهای معمولاً از مقایسه عددی استفاده میکنند، مگر زمانی که هر دو مقدار رشته باشند؛ در این صورت، از مقایسه الفبایی (مانند ترتیب لغتنامهای) استفاده میکنند:
var x = "10"; var y = "9"; x < y; // true, مراقب باشید!
هیچ راهی وجود ندارد که این عملگرها را مجبور کنید از coercion اجتناب کنند، مگر اینکه همیشه مقادیر همنوع را با هم مقایسه کنید. این هدف خوبی است، اما در عمل احتمال زیادی دارد با مواردی مواجه شوید که نوعها متفاوت باشند.
رویکرد عاقلانهتر این نیست که از مقایسههای همراه با تبدیل نوع اجتناب کنید، بلکه باید آنها را بپذیرید و جزئیاتشان را یاد بگیرید.
مقایسههای همراه با coercion در بخشهای دیگری از JS نیز ظاهر میشوند، مانند دستورات شرطی (if و غیره)