
أيا كانت تجربة البرمجة JavaScript لديك ، على الأرجح أنك لا تفهم تماما اللغة. يستكشف هذا الدليل المختصر الأنواع بعمق أكبر من جميع الكتب الحالية: سوف تتعلم كيفية عمل الأنواع ، ومشاكل تحويلها ، ومعرفة كيفية استخدام الميزات الجديدة.
مثل الكتب الأخرى في سلسلة
"أنت لا تعرف JS" ، فإنه يعرض الجوانب غير التافهة للغة التي يفضلها مبرمجو JavaScript على الابتعاد عنهم (أو الافتراض أنهم غير موجودين). مسلحًا بهذه المعرفة ، ستحقق إتقان جافا سكريبت الحقيقي.
مقتطفات. المساواة صارمة وغير صارمة.
يتم التحقق من المساواة غير الصارمة بواسطة العامل == ، والمساواة الصارمة من قبل العامل ===. يتم استخدام كلا المشغلين لمقارنة قيمتين "للمساواة" ، ولكن اختيار الشكل (صارم / غير صارم) يؤدي إلى اختلافات مهمة للغاية في السلوك ، وخاصة في كيفية اتخاذ القرار بشأن المساواة.
هناك اعتقاد خاطئ شائع بخصوص هذين المشغلين: "== يتحقق من المساواة في القيمة ، و === يتحقق من المساواة بين القيمتين والأنواع." تبدو معقولة
لكن غير دقيق. يقول عدد لا يحصى من كتب ومدونات جافا سكريبت ذات السمعة الطيبة ، لكن لسوء الحظ أنها كلها على خطأ.
الوصف الصحيح هو: "== يسمح بتحويل النوع عند التحقق من المساواة ، و === يحظر تحويل النوع."
أداء التحقق من المساواة
توقف وتفكر في كيف يختلف التفسير الأول (غير الدقيق) عن التفسير الثاني (الدقيق).
في التوضيح الأول ، يبدو من الواضح أن عامل التشغيل === يعمل أكثر من == لأنه يحتاج أيضًا إلى التحقق من النوع.
في التفسير الثاني ، يعمل العامل == بشكل أكبر ، لأنه مع أنواع مختلفة ، يجب أن يمر بتحويل الكتابة.
لا تسقط في الفخ الذي يسقط فيه الكثيرون. لا تعتقد أن هذا سيؤثر بطريقة ما على سرعة البرنامج ، و == سيكون أبطأ بشكل ملحوظ ===. على الرغم من أن التحويل يستغرق بعض الوقت ، إلا أنه يحتاج إلى بضع ثوانٍ (نعم ، ملايين من الثانية).
إذا كنت تقارن قيمتين من نفس النوع ، == و === استخدم نفس الخوارزمية ، لذلك إذا كنت لا تأخذ في الاعتبار الاختلافات الصغيرة في تنفيذ المحرك ، يجب أن تؤدي واحدة
ونفس الوظيفة.
إذا كنت تقارن قيمتين من أنواع مختلفة ، فإن الأداء ليس عاملاً مهمًا. عليك أن تسأل نفسك شيئًا آخر: إذا قارنت قيمتين ، هل أريد أن يحدث تحويل الكتابة أم لا؟
إذا كنت بحاجة إلى تحويل ، فاستخدم المساواة غير الصارمة == ، وإذا كان التحويل غير مرغوب فيه ، فاستخدم المساواة المطلقة ===.
كلا المشغلين ، == و === ، والتحقق من أنواع المعاملات الخاصة بهم. الفرق هو كيف يستجيبون لنوع عدم التطابق.
التحقق من المساواة مجردة
يتم تعريف سلوك العامل == في القسم 11.9.3 من مواصفات ES5 ("خوارزمية مدقق المساواة في الخلاصة"). فيما يلي خوارزمية مفصلة ولكنها بسيطة ، مع قائمة صريحة بجميع المجموعات الممكنة من الأنواع وطرق تحويل النوع (إذا لزم الأمر) التي ينبغي تطبيقها في كل مجموعة.
عندما يدين شخص ما تحويل النوع (الضمني) باعتباره معقدًا للغاية ويحتوي على الكثير من العيوب للاستخدام العملي المفيد ، فإنه يدين قواعد "التحقق من المساواة المجردة". يقال عادة أن هذه الآلية معقدة للغاية وغير طبيعية بالنسبة للدراسة والاستخدام العملي ، وأنها تخلق أخطاء في برامج JS بدلاً من تبسيط قراءة الكود.
أعتقد أن هذا افتراض خاطئ - أنتم القراء مطورين أكفاء يكتبون الخوارزميات ، أي الكود (وقراءته وفهمه أيضًا) ، طوال اليوم. لهذا السبب ، سأحاول شرح "اختبار المساواة المجردة" بكلمات بسيطة. ومع ذلك ، أوصي أيضًا بقراءة القسم 11.9.3 من مواصفات ES5. أعتقد أنه سيكون مفاجأة لك كيف كل شيء منطقي هناك.
في الواقع ، ينص القسم الأول (11.9.3.1) على أنه إذا كانت قيمتان مقارنتان من نفس النوع ، تتم مقارنتهما بطريقة بسيطة وطبيعية. على سبيل المثال ، 42 هو 42 فقط ، والسلسلة "abc" هي فقط "abc".
بعض الاستثناءات البسيطة التي يجب وضعها في الاعتبار:
- قيمة NaN لا تساوي أبدًا (انظر الفصل 2).
- +0 و -0 متساويان (انظر الفصل 2).
يخصص القسم الأخير في القسم 11.9.3.1 لاختبار صارم = = المساواة مع الأشياء (بما في ذلك الوظائف والمصفوفات). تساوي هاتان القيمتان
فقط إذا كان كلاهما يشير إلى
نفس القيمة بالضبط. لا يتم إجراء تحويل نوع.
يتم تعريف تحقق المساواة الصارمة === بشكل مماثل إلى 11.9.3.1 ، بما في ذلك توفير قيمتين للكائن. هذه الحقيقة معروفة جدًا ، ولكن == و === تتصرف تمامًا عند مقارنة كائنين!
يشير الجزء المتبقي من الخوارزمية في 11.9.3 إلى أنه يمكن استخدام المساواة المطلقة == لمقارنة نوعين مختلفين من القيم ، أحدهما أو كلاهما سيتطلب
تحويل ضمني. نتيجة للتحويل ، يتم تحويل التسميات إلى نوع واحد ، وبعد ذلك يمكن مقارنتها مباشرة من أجل المساواة عن طريق الهوية البسيطة
القيم.
تشغيل فحص ضعيف لعدم المساواة! = يتحدد تمامًا كما يتوقع المرء ؛ في الواقع ، يتم تنفيذ العملية == بالكامل ، تليها الحساب
إنكار النتيجة. وينطبق الشيء نفسه على عملية التحقق الدقيق من عدم المساواة! ==.
المقارنة: الأوتار والأرقام
لإظهار تحويل == ، قم أولاً بإنشاء أمثلة للسلاسل والأرقام ، والتي تم إجراؤها مسبقًا في هذا الفصل:
var a = 42; var b = "42"; a === b;
كما هو متوقع ، يفشل الفحص a === b لأن التحويل غير مسموح به ، والقيم 42 و "42" مختلفة.
ومع ذلك ، في المقارنة الثانية a == b ، يتم استخدام المساواة غير الصارمة ؛ هذا يعني أنه إذا كانت الأنواع مختلفة ، فسوف تقوم خوارزمية المقارنة بتحويل ضمني لأحدها
أو كلاهما.
ولكن ما نوع التحويل الذي يتم تنفيذه هنا؟ هل تصبح القيمة ، أي ، 42 ، سلسلة ، أم أن القيمة ب "42" ستصبح رقماً؟ مواصفات ES5 في الأقسام 11.9.3.4-5 تقول:
- إذا كان النوع (x) من النوع Number ، والنوع (y) من النوع String ، فأرجع نتيجة المقارنة x == ToNumber (y).
- إذا كان Type (x) من النوع String و Type (y) من النوع Number ، فأرجع نتيجة المقارنة ToNumber (x) == y.
في المواصفات ، يتم استخدام الأسماء الرسمية لأنواع الرقم والسلسلة ، بينما في كتاب الأنواع البدائية ، عادةً ما يتم استخدام رقم التدوين والسلسلة. لا تخلط بين حالة رمز الرقم في المواصفات والدالة المدمجة (). لأغراضنا ، لا تلعب حالة الأحرف في اسم النوع دورًا - إنها تعني نفس الشيء.
تشير المواصفات إلى أن القيمة "42" يتم تحويلها إلى رقم للمقارنة. حول كيفية إجراء التحويل ، تم بالفعل وصفه مسبقًا ، وبالتحديد عند وصف عملية الملخص ToNumber. في هذه الحالة ، الأمر واضح تمامًا
أن القيمتين الناتجة عن 42 متساوية.
المقارنة: أي شيء مع منطقية
تمت مصادفة أحد أخطر المصائد في التحويل الضمني للنوع == عند محاولة مقارنة القيم مباشرةً مع صواب أو خطأ.
مثال:
var a = "42"; var b = true; a == b;
انتظر ، ما الذي يحدث هنا؟ نحن نعلم أن الرقم "42" هو المعنى الحقيقي (انظر سابقًا في هذا الفصل). كيف اتضح أن مقارنتها بالصدق مع بيان المساواة الصارمة ==
لا يعطي صحيح؟
السبب بسيط ومضلل خادع في نفس الوقت. من السهل سوء الفهم ، العديد من مطوري JS لا يبذلون جهدًا لفهمه تمامًا.
مرة أخرى نقتبس المواصفات ، المقاطع 11.9.3.6-7:
- إذا كان Type (x) من النوع Boolean ، فارجع نتيجة المقارنة ToNumber (x) == y.
- إذا كان Type (y) من النوع Boolean ، فارجع نتيجة المقارنة x == ToNumber (y).
دعونا نرى ما هو هنا. الخطوة الأولى:
var x = true; var y = "42"; x == y;
النوع (x) ينتمي حقًا إلى النوع المنطقي ، لذلك يتم تنفيذ عملية ToNumber (x) ، والتي تحولت إلى 1. الآن يتم حساب الشرط 1 == "42". لا تزال الأنواع مختلفة ، وبالتالي (بشكل متكرر تقريبًا) تتكرر الخوارزمية ؛ كما في الحالة السابقة ، يتم تحويل "42" إلى 42 ، والشرط 1 == 42 خطأ واضح.
إذا قمت بتبديل المعاملات ، فستظل النتيجة كما هي:
var x = "42"; var y = false; x == y;
هذه المرة ، النوع (ص) من النوع Boolean ، لذلك يعطي ToNumber (y) 0. الشرط "42" == 0 يتحول بشكل متكرر إلى 42 == 0 ، والتي ، بالطبع ، خاطئة.
بمعنى آخر ، القيمة "42" ليست == صحيحة ولا == خطأ. للوهلة الأولى ، يبدو هذا البيان غير وارد على الإطلاق. كيف يمكن أن يكون المعنى غير صحيح ولا خطأ؟
لكن هذه هي المشكلة! أنت تسأل السؤال الخطأ. على الرغم من أنه في الواقع ليس خطأك ، إلا أن المخ هو الذي يخدعك.
إن القيمة "42" صحيحة بالفعل ، لكن البناء "42" == صحيح لا يؤدي اختبارًا منطقيًا / تحويليًا على الإطلاق ، أيا كان ما يقوله عقلك. "42" لا يتحول إلى منطقية (صواب) ؛ بدلاً من ذلك ، يتم تحويل true إلى 1 ، ثم يتم تحويل "42" إلى 42.
سواء أحببتم ذلك أم لا ، لا يتم استخدام ToBoolean على الإطلاق هنا ، وبالتالي فإن حقيقة أو خطأ "42" ليست مهمة على الإطلاق لعملية ==! من المهم أن نفهم كيف تتصرف خوارزمية المقارنة == في كل مجموعات الأنواع المختلفة. إذا كانت القيمة المنطقية على جانب واحد ، فسيتم تحويلها دائمًا إلى رقم أولاً.
إذا كان هذا يبدو غريبا بالنسبة لك ، فأنت لست وحدك. شخصيا ، أوصي أبدا ، أبدا ، تحت أي ظرف من الظروف ، باستخدام == صواب أو = = خطأ. أبدا.
ولكن تذكر أنني أتحدث فقط عن == هنا. لا تسمح الإنشاءات === true و === false بتحويل النوع ، لذا فهي محمية من تحويل ToNumber المخفي.
مثال:
var a = "42";
إذا تجنبت == true أو == false (مساواة فضفاضة مع المنطقية) في شفرتك ، فلن تقلق أبدًا من مصيدة الحقيقة / الزيف هذه.
المقارنة: لاغ مع غير محدد
مثال آخر على التحويل الضمني يحدث عند استخدام lax == المساواة بين القيم الخالية وغير المحددة. مرة أخرى ، سوف أقتبس مواصفات ES5 ،
الأقسام 11.9.3.2-3:
- إذا كانت x تحتوي على null و y تحتوي على غير معرّف ، فقم بإرجاع true.
- إذا كانت x تحتوي على غير معرف وكانت y تحتوي على قيمة فارغة ، فقم بإرجاع true.
لاغية وغير محددة عند مقارنتها بالمعامل غير الصارم == تساوي بعضها البعض (أي ، يتم تحويلها إلى بعضها البعض) ، ولا توجد قيم أخرى في اللغة بأكملها.
بالنسبة لنا ، هذا يعني أنه يمكن اعتبار لاغية وغير محددة تمييزًا لأغراض المقارنة إذا كنت تستخدم عامل اختبار المساواة غير الصارم == ، والذي يسمح بتحويلها الضمني المتبادل:
var a = null; var b; a == b;
التحويل بين null و undefined هو أمر آمن ويمكن التنبؤ به ، ولا يمكن لأي قيمة أخرى أن تعطي إيجابيات كاذبة لمثل هذا الفحص. أوصي باستخدام هذا التحويل حتى لا يختلف البرنامج فارغًا وغير معرف في البرنامج ويتم تفسيره كقيمة واحدة.
مثال:
var a = doSomething(); if (a == null) {
يتم تمرير التحقق == فقط إذا قام doSomething () بإرجاع قيمة خالية أو غير محددة وفشل في أي قيمة أخرى (بما في ذلك 0 و false و "").
الشكل الصريح لهذا الفحص ، الذي يحظر أي تحويلات من هذا النوع ، يبدو (في رأيي) أبشع بكثير وقد يعمل بكفاءة أقل قليلاً!
var a = doSomething(); if (a === undefined || a === null) {
أعتقد أن النموذج == null هو مثال آخر على الموقف الذي يسهّل فيه التحويل الضمني قراءة التعليمات البرمجية ، ولكنه يفعلها بشكل موثوق وآمن.
المقارنة: الأشياء وغير الأشياء
إذا تمت مقارنة كائن / دالة / صفيف بدائية عددية بسيطة (سلسلة أو رقم أو منطقية) ، فإن مواصفات ES5 تقول ما يلي (القسم 11.9.3.8-9):
- إذا كان النوع (x) من النوع String أو Number ، والنوع (y) من النوع Object ، فأرجع نتيجة المقارنة x == ToPrimitive (y).
- إذا كان النوع (x) من النوع Object والنوع (y) من النوع String أو Number ، فأرجع نتيجة المقارنة ToPrimitive (x) == y.
ربما لاحظت أنه في هذه المقاطع من المواصفات ، يتم ذكر السلسلة والرقم فقط ، ولكن ليس Boolean. والحقيقة هي أنه ، كما ذكر أعلاه ، تضمن الأقسام من 11.9.3.6 إلى 7 أن يتم تمثيل أي مُعامل منطقي أولاً كرقم.
مثال:
var a = 42; var b = [ 42 ]; a == b;
بالنسبة للقيمة [42] ، تسمى العملية المجردة ToPrimitive (انظر "عمليات الملخص") ، والتي تعطي النتيجة "42". من هذه اللحظة فصاعدًا ، يبقى الشرط البسيط "42" == 42 ، والذي ، كما اكتشفنا بالفعل ، يتحول إلى 42 == 42 ، بحيث يساوي a و b كتابة التحويل.
كما تتوقع ، جميع ميزات عملية ToPrimitive المجردة التي تمت مناقشتها سابقًا في هذا الفصل ((toString () ، valueOf ()) قابلة للتطبيق في هذه الحالة أيضًا ، وقد يكون ذلك مفيدًا للغاية إذا كان لديك بنية بيانات معقدة وترغب في قم بتعريف طريقة متخصصة valueOf () لها ، والتي يجب أن توفر قيمة بسيطة لأغراض التحقق من المساواة.
فحص الفصل 3 "تفريغ" غلاف الجهاز حول قيمة بدائية (كما في السلسلة الجديدة ("abc") ، على سبيل المثال) ، مما أدى إلى عودة البدائية الأساسية
القيمة ("abc"). يرتبط هذا السلوك بتحويل ToPrimitive في خوارزمية ==:
var a = "abc"; var b = Object( a );
a == b يعطي صحيحًا لأن b يتم تحويلها (أو "تفريغها") من خلال عملية ToPrimitive إلى القيمة البدائية العددية البسيطة الأساسية "abc" ، والتي تطابق القيمة من a.
هناك بعض القيم التي لا يكون ذلك بسبب قواعد التجاوز الأخرى في خوارزمية ==. مثال:
var a = null; var b = Object( a );
لا يمكن تعبئة القيم الفارغة وغير المحددة (لا تحتوي على غلاف مكافئ للكائن) ، وبالتالي فإن الكائن (فارغة) لا يختلف بشكل أساسي عن الكائن (): ينشئ كلا الاستدعاءين المعتاد
كائن نيويورك.
يمكن حزم NaN في غلاف كائن Number المكافئ ، لكن عندما = = تسبب التفريغ ، تفشل مقارنة NaN == NaN لأن قيمة NaN لا تساوي أبدًا نفسها (انظر الفصل 2).
»يمكن الاطلاع على مزيد من المعلومات حول الكتاب على
موقع الناشر»
المحتويات»
مقتطفاتخصم 25 ٪ على كوبون الباعة المتجولين -
جافا سكريبتعند دفع النسخة الورقية من الكتاب ، يتم إرسال كتاب إلكتروني عبر البريد الإلكتروني.