جافا سكريبت: الكبير كله حسنا لماذا


منذ وقت ليس ببعيد ، تباهى JavaScript بنوع بيانات BigInt بدائي جديد للعمل بأرقام دقة عشوائية. الحد الأدنى الضروري من المعلومات قد تم إخباره / ترجمته بالفعل حول حالات الدوافع والاستخدام. وأود أن أولي اهتمامًا أكبر بـ "explicitness" المحلي الذي تم تجاوزه في تحويل النوع و TypeError غير المتوقع. هل سنبخ أو نفهم ونغفر (مرة أخرى)؟

ضمني يصبح صريحا؟


في اللغة التي اعتاد على تحويل النوع الضمني إليها منذ فترة طويلة ، أصبح هذا الأمر بمثابة أي مؤتمر تقريبًا وفاجأ عدد قليل من الأشخاص بمثل هذه التعقيدات مثل:

1 + {}; // '1[object Object]' 1 + [[0]]; // '10' 1 + new Date; // '1Fri Feb 08 2019 00:32:57 GMT+0300 (,  )' 1 - new Date; // -1549616425060 ... 

لقد حصلنا فجأة على TypeError ، في محاولة لإضافة رقمين على ما يبدو:

 1 + 1n; // TypeError: Cannot mix BigInt and other types, use explicit conversions 

وإذا لم تؤد التجربة السابقة للشهود إلى انهيار في تعلم اللغة ، فهناك فرصة ثانية لتحطيم وطرح كتاب ECMA والدخول في بعض برامج Java.

علاوة على ذلك ، تستمر اللغة في "التصيد" للمطورين js:

 1n + '1'; // '11' 

أوه نعم ، لا تنسى عامل التشغيل الأحادي + :

 +1n; // TypeError: Cannot convert a BigInt value to a number Number(1n); // 1 

باختصار ، لا يمكننا خلط BigInt و Number في العمليات. نتيجة لذلك ، لا يوصى باستخدام "أعداد صحيحة كبيرة" إذا كان 2 ^ 53-1 ( MAX_SAFE_INTEGER ) كافياً لأغراضنا.

القرار الرئيسي


نعم ، كان هذا هو القرار الرئيسي لهذا الابتكار. إذا نسيت أن هذا هو JavaScript ، فكل شيء منطقي جدًا: تساهم هذه التحويلات الضمنية في فقدان المعلومات.

عندما نضيف قيمتين لأنواع رقمية مختلفة (أعداد صحيحة كبيرة وأرقام الفاصلة العائمة) ، قد تكون القيمة الرياضية للنتيجة خارج نطاق القيم الممكنة. على سبيل المثال ، لا يمكن تمثيل قيمة التعبير (2n ** 53n + 1n) + 0.5 بدقة بواسطة أي من هذه الأنواع. لم يعد هذا عددًا صحيحًا ، ولكنه رقم حقيقي ، لكن دقته لم تعد مضمونة بتنسيق float64 :

 2n ** 53n + 1n; // 9007199254740993n Number(2n ** 53n + 1n) + 0.5; // 9007199254740992 

في معظم اللغات الديناميكية ، حيث يتم تقديم أنواع لكل من الأعداد الصحيحة والعوامات ، تتم كتابة الأولى على شكل 1 ، والثانية في 1.0 . وبالتالي ، خلال العمليات الحسابية على وجود فاصل عشري في المعامل ، يمكننا أن نستنتج أن دقة التعويم في العمليات الحسابية مقبولة. لكن جافا سكريبت ليست واحدة منهم ، و 1 تعويم! وهذا يعني أن الحوسبة 2n ** 53n + 1 ستعيد تعويم 2 ^ 53. والتي بدورها تقطع الوظائف الرئيسية لـ BigInt :

 2 ** 53 === 2 ** 53 + 1; // true 

حسنًا ، ليس هناك سبب للحديث عن تطبيق "البرج العددي" أيضًا ، حيث أنك لن تكون قادرًا على أخذ الرقم الموجود كنوع بيانات رقمي عام (لنفس السبب).

ولتجنب هذه المشكلة ، تم حظر المصبوب الضمني بين Number و BigInt في العمليات. نتيجةً لذلك ، لا يمكن إدخال "عدد صحيح كبير" بأمان في أي من وظائف JavaScript أو Web API ، حيث يتوقع الرقم المعتاد:

 Math.max(1n, 10n); // TypeError 

يجب عليك تحديد أحد هذين النوعين بشكل صريح باستخدام Number () أو BigInt () .

بالإضافة إلى ذلك ، بالنسبة للعمليات ذات الأنواع المختلطة ، هناك تفسير حول التنفيذ المعقد أو فقدان الأداء ، وهو أمر شائع جدًا للتوافق مع ابتكارات اللغة.

بالطبع ، هذا ينطبق على التحويلات العددية الضمنية مع البدائل الأخرى:

 1 + true; // 2 1n + true; // TypeError 1 + null; // 1 1n + null; // TypeError 

ولكن ستعمل السلاسل التالية (بالفعل) ، حيث أن النتيجة المتوقعة عبارة عن سلسلة:

 1n + [0]; // '10' 1n + {}; // '1[object Object]' 1n + (_ => 1); // '1_ => 1' 

استثناء آخر في شكل عوامل المقارنة (مثل < و > و == ) بين Number و BigInt . لا يوجد أيضًا فقدان للدقة ، حيث إن النتيجة منطقية.

حسنًا ، إذا كنت تتذكر نوع بيانات Symbol الجديد السابق ، فهل لم تعد TypeError تبدو مثل هذه الإضافة الجذرية؟

 Symbol() + 1; // TypeError: Cannot convert a Symbol value to a number 

ونعم ، ولكن لا. في الواقع ، ليس رمز المفهوم رقماً على الإطلاق ، بل برمته - كثيرًا:

  1. من غير المرجح أن يقع هذا الرمز في مثل هذا الموقف. ومع ذلك ، هذا أمر مشبوه للغاية و TypeError مناسب تمامًا هنا.
  2. من المحتمل جدًا والمعتاد أن يصبح "الكل الكبير" في العمليات واحدًا من المعاملات عندما لا يكون هناك أي خطأ.

يطرح عامل التشغيل unary + استثناء بسبب مشكلة التوافق مع asm.js ، حيث يتوقع Number . لا يمكن أن تعمل علامة unary plus مع BigInt بنفس طريقة الرقم ، لأنه في هذه الحالة ، ستصبح شفرة asm.js السابقة غامضة.

عرض بديل


على الرغم من البساطة النسبية و "النظافة" لتطبيق BigInt ، تؤكد أكسل راوشماير على نقص الابتكار. وهي توافقه الخلفي الجزئي الوحيد مع العدد الحالي وما يليه:
استخدم الأرقام لما يصل إلى 53 بت. استخدم الأعداد الصحيحة إذا كنت بحاجة إلى مزيد من البتات
كبديل ، اقترح ما يلي .

اسمح ليصبح Number نموذجًا فائقًا لـ Int و Double الجديد:

  • اكتب 123.0 === 'number' ، و Number.isDouble (123.0) === true
  • typeof 123 === 'number' و Number.isInt (123) === true

مع وظائف جديدة لتحويلات Number.asInt () و Number.asDouble () . وبطبيعة الحال ، مع التحميل الزائد للمشغل ويلقي اللازمة:

  • Int × مزدوج = مزدوج (مسبوكة)
  • مزدوج × كثافة العمليات = مزدوج (مع الزهر)
  • مزدوج × مزدوج = مزدوج
  • Int × Int = Int (جميع المشغلين باستثناء القسمة)

ومن المثير للاهتمام ، في النسخة المبسطة ، تدير هذه الجملة (في البداية) دون إضافة أنواع جديدة إلى اللغة. بدلاً من ذلك ، يتم توسيع تعريف "نوع الرقم" : بالإضافة إلى جميع الأرقام المزدوجة الممكنة 64 بت (IEEE 754-2008) ، يشمل الرقم الآن جميع الأعداد الصحيحة. ونتيجة لذلك ، فإن "الرقم غير الدقيق" 123.0 و "الرقم الدقيق" 123 هما رقمان منفصلان من نوع الرقم الفردي.

يبدو مألوفا جدا ومعقولة. ومع ذلك ، هذه ترقية خطيرة للعدد الحالي ، والذي من المرجح أن يؤدي إلى "كسر الويب" وأدواته:

  • هناك فرق بين 1 و 1.0 ، وهو ما لم يكن هناك من قبل. يستخدمهم الكود الموجود بشكل متبادل ، والذي بعد الترقية يمكن أن يؤدي إلى الارتباك (على عكس اللغات التي كان هذا الاختلاف موجودًا في البداية).
  • هناك تأثير عندما يكون 1 === 1.0 (من المفترض أن يكون ترقية) ، وفي الوقت نفسه ، Number.isDouble (1)! == Number.isDouble (1.0) : مرة أخرى ، يكون الأمر كذلك.
  • تختفي "خصوصية" المساواة 2 ^ 53 و 2 ^ 53 + 1 ، مما يؤدي إلى كسر الشفرة التي تعتمد عليها.
  • نفس مشكلة التوافق مع asm.js والمزيد.

لذلك ، في النهاية ، لدينا حل وسط في شكل نوع بيانات منفصل جديد. يجدر التأكيد على أنه تم بحث ومناقشة خيار آخر.

عندما تجلس على كرسيين


في الواقع ، يبدأ تعليق اللجنة بالكلمات:
احصل على توازن بين الحفاظ على حدس المستخدم والحفاظ على الدقة

من ناحية ، أردت أخيرًا إضافة شيء "دقيق" إلى اللغة. ومن ناحية أخرى ، للحفاظ على سلوكها المألوف بالفعل للعديد من المطورين.

إنه فقط لا يمكن إضافة هذا "الدقيق" ، لأنه لا يمكنك كسره: الرياضيات ، وبيئة العمل اللغوية ، asm.js ، وإمكانية التوسع في نظام الكتابة والإنتاجية ، وفي نهاية المطاف ، الويب نفسها! ولا يمكنك كسر كل شيء في نفس الوقت ، مما يؤدي إلى ذلك.

ولا يمكنك كسر حدس مستخدمي اللغة ، والتي كانت ، بالطبع ، موضع نقاش ساخن . صحيح ، هل نجحت؟

Source: https://habr.com/ru/post/ar439402/


All Articles