اذهب إلى المحتوى

مبادئ كتابة جافا سكريبت متسقة ومفهومة


واثق الشويطر

يجب أن تبدو الشيفرات البرمجية أينما وجدت كما لو أن مبرمجًا واحدًا كتبها، بغض النظر عن عدد من ساهم فيها.

توضّح القائمة التالية بعض الإرشادات المُستخدمة في كل الشيفرات البرمجية.

اقتباس

"الجدل في الأسلوب لا يؤدي إلى شيء. يجب أن يكون هناك دليلاً للأسلوب (style guide)، ويجب اتباعه" _ريبيكا مورفي

"أحد العوامل التي تجعلك مديرًا جيدًا للمشاريع الناجحة هو أنه عليك أن تفهم أن كتابة التعليمات البرمجية بأسلوبك الخاص دائمًا هي ممارسة سيئة. إذا كان الآلاف من الناس يستخدمون شيفراتك، فاكتبها بأقصى قدر من الوضوح، بدلاً من محاولة أن تبدو ذكيًا في أسلوبك في الكتابة." _إيدان جازيت

مقدمة

توضّح الأقسام التالية دليل أسلوب معتدل reasonable لتطوير جافا سكريبت Javascript الحديثة وليس المقصود منها أن تكون مواصفات صارمة.

أهم ما يمكن تعلمه هو قانون اتساق نمط الشيفرات البرمجية، فكل ما تختاره أسلوبًا لمشروعك يجب أن يعد قانونًا. اتّخذ هذا المقال مرجعًا لالتزام مشروعك بتناسق نمط الشيفرات، وسهولة قراءتها، وقابليتها للصيانة.

بيان الأسلوب الاصطلاحي

المسافة الفارغة

هذه بعض النصائح الممكن الاستفادة منها بشأن المسافات الفارغة:

  • لا تخلط بين المسافات spaces ومسافة tab إطلاقًا.
  • عند بدء المشروع، وقبل أن تكتب أي شيفرة برمجية، اختر إما المسافات أو مسافات tab، وليس كليهما. اتّبع هذا على طول المشروع واجعله قانونًا.
  • لزيادة جودة القراءة، يُوصى دائمًا بتعيين حجم المسافة البادئة indent لمحررك على حرفين، وهذا يعني مسافتين.
  • إذا كان محررك يدعم إعداد "إظهار العناصر غير المرئية"، فيُنصح بتشغيله دائمًا؛ ففوائده هي:
  • فرض الاتساق consistency.
  • إزالة المسافة الفارغة في نهاية السطر.
  • إزالة الأسطر الفارغة.
  • تسهيل قراءة الإيداعات Commits والاختلافات diffs، والتي تأتي غالبًا أثناء معالجة إدارة النسخ version control.
  • استخدم Editorconfig متى ما أمكن ذلك، فهو يدعم معظم محررات التطوير، ويتعامل مع معظم إعدادات المسافات الفارغة.

الصيغ الجميلة Beautiful Syntax

أولًا: الأقواس الهلالية () والمعقوصة {} والمعقوفة []، وفواصل الأسطر Linebreaks

// مسافات فراغ، وأقواس وأسطر متعددة if و else و for و while و try سيكون لتعليمات 
// هذا سيحسّن جودة القراءة

// 2.A.1.1
// أمثلة على بناء جملة محشورة جدًأ

if(condition) doSomething();

while(condition) iterating++;

for(var i=0;i<100;i++) someIterativeFn();


// 2.A.1.1
// استخدم المسافة الفارغة لتعزيز جودة القراءة

if ( condition ) {

    // تعليمات برمجية
}

while ( condition ) {

    // تعليمات برمجية
}

for ( var i = 0; i < 100; i++ ) {

    // تعليمات برمجية
}

  // الأفضل

var i,
  length = 100;

for ( i = 0; i < length; i++ ) {

    // تعليمات برمجية
}

//  أو

var i = 0,
  length = 100;

for ( ; i < length; i++ ) {

    // تعليمات برمجية
}

var prop;

for ( prop in object ) {

    // تعليمات برمجية
}


if ( true ) {

    // تعليمات برمجية
} else {

    // تعليمات برمجية
}

ثانيًا: الإسنادات Assignments والتصريحات Declarations والدوال

مثل الدالة المسمّاة Named Function، والتعبير Expression والباني Constructor

// 2.B.1.1
// المتغيرات
var foo = "bar",
  num = 1,
  undef;

// القيم المجردة:
var array = [],
  object = {};


// 2.B.1.2
// واحد لكل متغير `var` واحد فقط لكل نطاق (دالة) أو `var` استخدام
// يعزز جودة القراءة ويحافظ على قائمة التصريحات خالية من الفوضى.
// واحد التحكم أكثر في إصداراتك `var` يمكنك باستخدام 
// ويسهل إعادة ترتيب السطور
// واحد لكل نطاق اكتشاف المتغيرات غير المصرّحة `var` يسهّل استخدام 
// والتي قد تصبح ضمنًا متغيرات عامة
// اختر الأفضل لمشروعك ولا تخلط بينهما أبدًا.

// سيء
var foo = "",
  bar = "";
var qux;


// جيد
var foo = "";
var bar = "";
var qux;

// أو
var foo = "",
  bar = "",
  qux;

//  أو
var // التعليق على هذه المتغيرات
foo = "",
bar = "",
quux;

// 2.B.1.3
// دائمًا في بداية النطاق (الدالة) التي تتعامل معها var يجب أن تكون تعليمات 



// سيء
function foo() {

// بعض التعليمات البرمجية هنا

  var bar = "",
    qux;
}


// جيد
function foo() {
  var bar = "",
    qux;

// كل التعليمات البرمجية بعد التصريح عن المتغيرات
}

// 2.B.1.4
//‏أيضًا في بداية النطاق ECMAScript 6  من  const و let يجب أن يكون 

// سيء
function foo() {
  let foo,
    bar;
  if ( condition ) {
    bar = "";

    // تعليمات برمجية
  }
 }

 // جيد
 function foo() {
  let foo;
  if ( condition ) {
    let bar = "";

// تعليمات برمجية
  }
}

أما الدالة المُسماة:

// 2.B.2.1
// تصريح الدالة المسماة
function foo( arg1, argN ) {

}


// الاستخدام
foo( arg1, argN );


// 2.B.2.2
// تصريح الدالة المسماة
function square( number ) {
  return number * number;
}


// الاستخدام
square( 10 );

// أسلوب التمرير المتتالي عن طريق تمرير دالة رد نداء ‪‫callback
function square( number, callback ) {
  callback( number * number );
}

square( 10, function( square ) {
  // تعليمات برمجية داخل دالة رد النداء 
});


// 2.B.2.3
// تعبير الدالة
var square = function( number ) {
  // إرجاع شيء ذي قيمة وذي صلة
  return number * number;
};

// تعبير الدالة مع المعرّف
//  ميزة هذه الطريقة هو أنها قادرة على استدعاء نفسها، وتقدر أن تتبع
// المعرّف في تعقبات المكدس

var factorial = function factorial( number ) {
  if ( number < 2 ) {
    return 1;
  }

  return number * factorial( number - 1 );
};


// 2.B.2.4
// تصريح الباني 
function FooBar( options ) {

  this.options = options;
}

// الاستخدام
var fooBar = new FooBar({ a: "alpha" });

fooBar.options;
// { a: "alpha" }

ثالثًا: الاستثناءات Exceptions وبعض التفاصيل

// 2.C.1.1
// الدوال مع رد النداء
foo(function() {
  // لاحظ عدم وجود مسافة إضافية بين القوس الأول
  // "function" لاستدعاء دالة التنفيذ وكلمة‎‏‎
});

// الدالة تقبل تمرير مصفوفة دون فراغ
foo([ "alpha", "beta" ]);

// 2.C.1.2
// الدالة تقبل تمرير كائن دون فراغ
foo({
  a: "alpha",
  b: "beta"
});


// تمرير سلسلة نصية دون فراغ

foo("bar");

// أقواس التعبير، دون فراغ
if ( !("foo" in obj) ) {
  obj = (obj.bar || defaults).baz;
}

رابعًا: يفوز الاتساق دائمًا

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

// 2.D.1.1

if (condition) {

  // تعليمات برمجية

}

while (condition) {

  // تعليمات برمجية
}

for (var i = 0; i < 100; i++) {

  // تعليمات برمجية
}

if (true) {

  // تعليمات برمجية
} else {

  // تعليمات برمجية
}

خامسًا: الاقتباسات

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

سادسًا: نهاية الأسطر والأسطر الفارغة

يمكن للمسافة الفارغة أن تجعل مقارنة التغييرات diffs غير مقروءة في معالجة التحكم في الإصدارات، لذلك فكّر في إضافة أداة أو خطّاف قبل الإيداع pre-commit hook، ليزيل المسافات الفارغة في نهاية كل سطر، ويزيل الأسطر الفارغة تلقائيًا.

التحقق من النوع

أولَا: الأنواع الفعلية

  • السلسلة النصية String:
typeof variable === "string"
  • الرقم Number:
typeof variable === "number"
  • القيمة البوليانية Boolean:
typeof variable === "boolean"
  • الكائن Object:
typeof variable === "object"
  • المصفوفة Array:
Array.isArray( كائن_شبيه_بالمصفوفة )
 (إذا أمكن ذلك)
  • العنصر Node:
elem.nodeType === 1
  • القيمة الفارغة Null:
variable === null
  • القيمة غير المعرفة undefined:
variable === null
  • المتغيرات العامة:
typeof variable === "undefined"
  • المتغيرات المحلية:
variable === undefined
  • الخاصيات Properities:
object.prop === undefined
object.hasOwnProperty( prop )
"prop" in object

ثانيًا: الأنواع القسرية أو الإجبارية Coerced Types

ضع بالحسبان ما يلي: ألقِ نظرةً على هذا الجزء من شيفرة HTML:

<input type="text" id="foo-input" value="1">

وشيفرة جافا سكريبت التالية:

// 3.B.1.1
// ‏‎‫‫عُرّفت `foo` بقيمة `0` ونوعها `number`

var foo = 0;

// typeof foo;
// "number"
...

// ‫في مكان ما في الكود، تحتاج إلى تحديث `foo` بقيمة جديدة مشتقة من عنصر إدخال

foo = document.getElementById("foo-input").value;

// ‫إذا كنت تختبر `typeof foo` الآن، ستكون النتيجة `string`
// ‫هذا يعني أنه إذا كان لديك منطق يختبر `foo` مثل

if ( foo === 1 ) {

  importantTask();

}

// ‫لن تُقيّم `()importantTask` أبدًا، على الرغم من أن `foo` لديه قيمة "1"



// 3.B.1.2
// ‫يمكنك حل هذه المشكلة من خلال استخدام التحويل الذكي باستخدام عوامل التشغيل ‫الأحادية: عوامل السالب `-` أو   الموجب `+`

foo = +document.getElementById("foo-input").value;
// سيحوّل عامل الإيجاب الأحادي `^` العملية المُشير إليها على الجانب الأيمن إلى رقم

// typeof foo;
// "number"

if ( foo === 1 ) {

  importantTask();

}
// ‫ستُستدعى `()importantTask`

فيما يلي بعض الحالات الشائعة مع عمليات التحويل:

// 3.B.2.1

var number = 1,
  string = "1",
  bool = false;

number;
// 1

number + "";
// "1"

string;
// "1"

+string;
// 1

+string++;
// 1

string;
// 2

bool;
// false

+bool;
// 0

bool + "";
// "false"

وهذه الشيفرة:

    // 3.B.2.2

var number = 1,
  string = "1",
  bool = true;

string === number;
// false

string === number + "";
// true

+string === number;
// true

bool === number;
// false

+bool === number;
// true

bool === string;
// false

bool === !!string;
// true

وهذه:

// 3.B.2.3

var array = [ "a", "b", "c" ];

!!~array.indexOf("a");
// true

!!~array.indexOf("b");
// true

!!~array.indexOf("c");
// true

!!~array.indexOf("d");
// false

// لاحظ أن ما ورد أعلاه كان ذكيًا بلا داع

// ‫الرجاء استخدام الطريقة الواضحة لمقارنة القيمة التي تُرجع لـ IndexOf، مثل:

if ( array.indexOf( "a" ) >= 0 ) {
  // ...
}

والشيفرة التالية:

// 3.B.2.4

var num = 2.5;

parseInt( num, 10 );

// ‫بالضبط مثل...

~~num;

num >> 0;

num >>> 0;

// الكل نتيجته 2

// ‫مع ذلك، ضع في حسبانك أنه سيُتعامل مع الأرقام السالبة تعاملًا مختلفًا...

var neg = -2.5;

parseInt( neg, 10 );

// الكل نتيجته 2

~~neg;

neg >> 0;

// ‫الكل نتيجته -2
// لكن...

neg >>> 0;

// النتيجة هي 4294967294

التقييم المشروط

// 4.1.1

// ‫عند تقييم ما إذا كان للمصفوفة طول (أي لها عناصر وليست فارغة)

// بدلًا من هذا
if ( array.length > 0 ) ...

// ‫... قيّم صوابية القيمة، مثل هذه:
if ( array.length ) ...


// 4.1.2
// ‫عند تقييم ما إذا كان المصفوفة فارغة فقط،
// بدلًا من هذا
if ( array.length === 0 ) ...
// ‫... تقييم القيمة، مثل هذه:
if ( !array.length ) ...


// 4.1.3
// ‫عند تقييم ما إذا كان المصفوفة ذات قيمة وليست فارغة فقط
// بدلًا من هذا
if ( string !== "" ) ...

// ‫... تقييم القيمة، مثل هذه:
if ( string ) ...


// 4.1.4
// عند تقييم ما إذا كانت السلسلة النصية فارغة فقط
// بدلًا من هذا
if ( string === "" ) ...
// ‫... قيّم خطأ القيمة، مثل هذه:
if ( !string ) ...


// 4.1.5
// عند تقييم ما إذا كان المرجع صوابًا

// بدلًا من هذا
if ( foo === true ) ...

// ‫... قيّم صواب القيمة أو خطأها كما تريد، واستفد من الإمكانيات المضمّنة
if ( foo ) ...


// 4.1.6
// ‫عند تقييم ما إذا كان قيمة المتغير خاطئة،

// بدلًا من هذا
if ( foo === false ) ...

// ‫...استخدم النفي لفرض التقييم الحقيقي
if ( !foo ) ...

// ‫...كن حذرًا، سيتطابق هذا أيضًا مع هذه القيم: 0, ""، null,undefined , NaN
// ‫إذا كان يجب عليك اختبار القيمة الخاطئة فاستخدم ما يلي:

if ( foo === false ) ...


// 4.1.7
// ‫عند تقييم ما إذا كان المرجع قيمته null أو undefined، ولكن ليس "" أو 0،

// بدلًا من هذا
if ( foo === null || foo === undefined ) ...


// ‫... الاستفادة من المعاملين "==" الذيَن يفرضان تحويلًا لنوع البيانات مثل هذا:
if ( foo == null ) ...

// ‫تذكر أن استخدام المعاملين "==" سيطابق `null` مع كل من `null` و `undefined`
// ‫ولكن لن يطابق `null` مع القيم `false` أو "" أو 0

null == undefined

قيّم دائمًا للحصول على أفضل النتائج، وأدقها، ما ورد أعلاه هو أسلوب توجيهي وليس مبدأ متزمت.

// 4.2.1
// ‫ملاحظات فرض النوع Type coercion والتقييم

// ‫تُفضّل `===` على `==` ما لم تتطلب الحالة تقييمًا فضفاضًا للنوع loose type evaluation


// المعاملات "===" لا يفرض النوع، مما يعني أن:


"1" === 1;
// خطأ

// ‫أما المعاملَين `==` فهما يفرضان تحويل النوع مما يعني أن

"1" == 1;
// صواب


// 4.2.2
/// القيم البوليانية وقيم الصواب والخطأ

// القيم البوليانية
true, false

// القيم الصائبة
"foo", 1

// القيم الخاطئة

"", 0, null, undefined, NaN, void 0

الأسلوب العملي

// 5.1.1

// وحدة عملية

(function( global ) {
  var Module = (function() {

    var data = "secret";

    return {
      // هذه خاصية بوليانية 
      bool: true,
      // قيمة نصية ما
      string: "a string",
      // خاصية المصفوفة
      array: [ 1, 2, 3, 4 ],
      // خاصية الكائن
      object: {
        lang: "en-Us"
      },
      getData: function() {
        // ‫احصل على القيمة الحالية لـ `data`.
        return data;
      },
      setData: function( value ) {
        // ‫عيّن قيمة `data` وأرجعها
        return ( data = value );
      }
      };
      })();

      // قد تحدث أشياء أخرى هنا


      // كشف وحدتنا للكائن العام

      global.Module = Module;

})( this );

ألقِ نظرةً على الشيفرة التالية:

// 5.2.1
// باني عملي

(function( global ) {

  function Ctor( foo ) {

    this.foo = foo;

    return this;
  }

  Ctor.prototype.getFoo = function() {
    return this.foo;
  };

  Ctor.prototype.setFoo = function( val ) {
    return ( this.foo = val );
   };


  // ‫لاستدعاء الباني دون الكلمة المفتاحية `new`، يمكنك فعل ذلك:
  var ctor = function( foo ) {
    return new Ctor( foo );
  };


  // كشف الباني للكائن العام
  global.ctor = ctor;

})( this );

التسمية

أولًا: أنت لست مصرّفًا compiler أو ضاغطًا compressor للشيفرة

ضع في بالك أنك لستَ مصرّفًا compiler أو ضاغطًا compressor بشريًا للشيفرة البرمجية، لذا لا تحاول أن تفعل ما يفترض أن يفعله المُصرّف أو الضاغط.

الشيفرة البرمجية التالية هي مثال على التسمية السيئة:

// 6.A.1.1
// مثال على شيفرة برمجية ذي تسميات سيئة

function q(s) {
  return document.querySelectorAll(s);
}
var i,a=[],els=q("#foo");
for(i=0;i<els.length;i++){a.push(els[i]);}

دون شك، لقد كتبتَ شيفرة برمجية سيئة مثل هذه، ونأمل ألا يحدث ذلك مجددًا.

إليك نفس المنطق البرمجي، ولكن مع تسمية ألطف وأكثر عناية وجودة قراءة:

// 6.A.2.1
// مثال على شيفرة برمجية ذات تسميات أفضل

function query( selector ) {
  return document.querySelectorAll( selector );
}

var idx = 0,
  elements = [],
  matches = query("#foo"),
  length = matches.length;

for ( ; idx < length; idx++ ) {
  elements.push( matches[ idx ] );
}

إليك بعض المؤشرات الإضافية للتسمية:

// 6.A.3.1
// تسمية السلاسل النصية

`dog` is a string


// 6.A.3.2
// تسمية المصفوفات

`dogs` is an array of `dog` strings

// 6.A.3.3
// تسمية الدوال، والكائنات، والنُسَخ

camelCase; function and var declarations

// 6.A.3.4
// ‫تسمية الدوال البانية، ودوال النماذج الأولية prototypes، إلخ.

PascalCase; constructor function


// 6.A.3.5

// تسمية التعابير النمطية

rDesc = //;


// 6.A.3.6
// ‫من دليل أسلوب مكتبة Google للـ Closure
functionNamesLikeThis;
variableNamesLikeThis;
ConstructorNamesLikeThis;
EnumNamesLikeThis;
methodNamesLikeThis;
SYMBOLIC_CONSTANTS_LIKE_THIS;

ثانيًا: أوجه الكلمة المفتاحية this

بعيدًا عن حالات الاستخدام المعروفة عمومًا للكلمات call و apply، يُفضّل دائمًا استخدام (bind(this أو ما يعادلها وظيفيًا لإنشاء تعريفات BoundFunction للاستدعاء لاحقًا. لا تلجأ إلى الاسم البديل aliasing إلا في حالة عدم توفر خيار أفضل.

// 6.B.1
function Device( opts ) {

  this.value = null;

  // فتح مجرى غير متزامن،
  // سيُستدعى هذا استدعاء مستمرًا

  stream.read( opts.path, function( data ) {

    // حدّث القيمة الحالية لهذه النسخة
    // بأحدث قيمة من
    // مجرى المعلومات
    this.value = data;

  }.bind(this) );

  //  التحكم في عدد مرات تشغيل الأحداث من
  // نسخة الجهاز هذا


  setInterval(function() {

    // انبعاث حدث مُقيّد
    this.emit("event");
  }.bind(this), opts.freq || 100 );
}


// ‫تظاهر فقط بأننا ورثنا EventEmitter 

عندما لا ينجح ذلك، تتوفر وظيفة مكافئة لـ bind. في معظم مكتبات جافا سكريبت الحديثة.

// 6.B.2

// مثال: lodash/underscore, _.bind()
function Device( opts ) {

  this.value = null;

  stream.read( opts.path, _.bind(function( data ) {

    this.value = data;

  }, this) );

  setInterval(_.bind(function() {

    this.emit("event");

  }, this), opts.freq || 100 );
}

// مثال: jQuery.proxy
function Device( opts ) {

  this.value = null;

  stream.read( opts.path, jQuery.proxy(function( data ) {

    this.value = data;

  }, this) );

  setInterval( jQuery.proxy(function() {

    this.emit("event");

  }, this), opts.freq || 100 );
}

// على سبيل المثال. dojo.hitch
function Device( opts ) {

  this.value = null;

  stream.read( opts.path, dojo.hitch( this, function( data ) {

    this.value = data;

}) );

  setInterval( dojo.hitch( this, function() {

    this.emit("event");

  }), opts.freq || 100 );
}

عرّف اسمًا بديلًا للكلمة this باستخدام self معرفًا، هذا يعرّضك للأخطاء بشدة ويجب تجنّبه كلما أمكن ذلك.

// 6.B.3

function Device( opts ) {
  var self = this;

  this.value = null;

  stream.read( opts.path, function( data ) {

    self.value = data;
  });

    setInterval(function() {

      self.emit("event");

  }, opts.freq || 100 );
}

ثالثًا: استخدم thisArg

تأتي العديد من توابع النماذج الأولية prototype methods لمكونات ES 5.1 المضمنة مع بصمة thisArg خاصة، والتي يجب استخدامها كلما أمكن ذلك.

// 6.C.1

var obj;

obj = { f: "foo", b: "bar", q: "qux" };

Object.keys( obj ).forEach(function( key ) {


  // ‫تشير |this| إلى `obj`
  console.log( this[ key ] );

}, obj ); // ← ‫آخر arg هو `thisArg`

// النتيجة

// "foo"
// "bar"
// "qux"

يمكن أن تُستخدم thisArg مع Array.prototype.every، و Array.prototype.forEach، و Array.prototype.some، و Array.prototype.map، و Array.prototype.filter.

متفرقات

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

أولًا: يجب تجنّب استخدام switch، إذ سيدرج التتبع بالتوابع الحديثة الدوال التي تحتوي على عبارات switch في القائمة السوداء.

يبدو أن هناك تحسينات جذرية في تنفيذ عبارات switch في أحدث إصدارات Firefox و Chrome.

يمكن ملاحظة التحسينات الملحوظة على الرابط.

// 7.A.1.1
// ‫مثال على عبارة switch

switch( foo ) {
  case "alpha":
    alpha();
    break;
  case "beta":
    beta();
    break;
  default:
    // القيمة الافتراضي
    break;
}

// 7.A.1.2

// ‫البديل القابل للتركيب وإعادة الاستخدام هو استخدام الكائنات لتخزين "الحالات"
// ‫واستخدم دالة للتفويض:



var cases, delegator;

// المثال يرجع بعض النتائج للتوضيح فقط
cases = {
  alpha: function() {

    // تعليمات برمجية
    // رجوع
    return [ "Alpha", arguments.length ];
  },
  beta: function() {

    // تعليمات برمجية

    // رجوع
    return [ "Beta", arguments.length ];
  },
  _default: function() {

    // تعليمات برمجية

    // رجوع
    return [ "Default", arguments.length ];
  }
};

delegator = function() {
  var args, key, delegate;

  // ‫تحويل قائمة الوسطاء إلى مصفوفة
  args = [].slice.call( arguments );

  // إزاحة مفتاح الحالة من الوسطاء 
  key = args.shift();

  // تعيين معالج الحالة الافتراضي
  delegate = cases._default;

  // اشتقاق الدالة لتفويض العملية
  if ( cases.hasOwnProperty( key ) ) {
    delegate = cases[ key ];
  }


  // يمكن ضبط وسيط النطاق على شيء محدد،
  // ‫في هذه الحالة، |null| ستكفي
  return delegate.apply( null, args );
};

// 7.A.1.3
// ضع الواجهة البرمجية في 7.A.1.2 لتعمل:

delegator( "alpha", 1, 2, 3, 4, 5 );
// [ "Alpha", 5 ]


// ‫يمكن بسهولة طبعًا تعيين قيمة مفتاح "الحالة" `case` بناءً على بعض المواقف المختلفة الأخرى.

var caseKey, someUserInput;

// ربما نوع من المدخلات؟
someUserInput = 9;

if ( someUserInput > 10 ) {
  caseKey = "alpha";
} else {
  caseKey = "beta";
}

// أو

caseKey = someUserInput > 10 ? "alpha" : "beta";

  // ثم
delegator( caseKey, someUserInput );
// [ "Beta", 1 ]

// وطبعًا
delegator();
// [ "Default", 0 ]

ثانيًا: يمكن أن يؤدي إرجاع القيم مقدمًا إلى تحسين إمكانية القراءة وليس له تأثير يذكر على الأداء.

ألقِ نظرةً على الشيفرة التالية:

// 7.B.1.1
// سيء:
function returnLate( foo ) {
  var ret;

  if ( foo ) {
    ret = "foo";
  } else {
    ret = "quux";
  }
  return ret;
}

// جيد

function returnEarly( foo ) {

  if ( foo ) {
    return "foo";
  }
  return "quux";
}

الكائنات الأصلية والمضيفة Native and Host Objects

المبدأ الأساسي هو: "لا تفعل أشياء غبية وسيكون كل شيء على ما يرام".

التعليقات

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

شيفرة لغة واحدة

يجب أن تكون البرامج مكتوبة بلغة واحدة، مهما كانت لغة البرمجة تلك، حسب ما يمليه المشرف أو الفريق.

ترجمة -وبتصرف- لدليل Principles of Writing Consistent, Idiomatic JavaScript لصاحبه Rick Waldron وكل المساهمين.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...