الدوال

This translation is incomplete. Please help translate this article from English.

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

ألقي أيضا نظرة على مرجع مفصل عن دوال الجافاسكريبت حتى تتعرف على التفاصيل.

انشاء الدوال

الاعلان عن الدوال - الصيغة الافتراضية - Function declarations

تعريف الدالة: تتألف الدالة من الكلمة المحجوزة function، متبوعة بـ :

  • إسم الدالة.
  • قائمة بارامترات الدالة، محصورة بين قوسين ويفصل بينها بفواصل.
  • تعليمات الجافاسكريبت التي تُعرف الدالة، داخل الأقواس المتعرجة {}.

على سبيل المثال، الكود التالي يعرف دالة بسيطة تسمى square :

function square(number) {
  return number * number;
}

الدالة square تحتوي على بارامتر واحد، هو number. وتعليمة برمجية واحدة تقوم بإرجاع بارامتر الدالة number مضروباً في نفسه. والتعليمة return تحدد القيمة التي سترجعها الدالة.

return number * number;

يتم تمرير البارامترات الاولية primitives (كالسلاسل الحرفية، الاعداد...الخ) الى الدالة، بوسيلة تسمى، الاستدعاء بالقيمة، call by value وهي، ان يتم تحليل argument الدالة، والقيمة الناتجة سَتُرسل نسخة منها فقط إلى المتغير المستقبل (بارامتر الدالة)، بعد ذالك تقوم الدالة بتخزين هذه القيمة في الذاكرة، ثم، إذا كانت الدالة قادرة على تعيين القيم للبارامترات الخاصة بها، تبدا بالعمل على هذه النسخة فقط، دون تغيير اي شئ في قيمة المتغير الاصلي.

في المثال التالي، لدينا المتغير value سنقوم بتمريره إلى الدالة square. ثم ضمن الدالة square سيتم إنشاء نسخة جديدة، وهكذا، حتى وان قمنا بتغيير القيمة داخل الدالة، فلن يؤثر هذا على قيمة المتغير الأصلي الذي تم تعريفه خارج الدالة. لان هذا التغيير لن يكون مرئيا خارج الدالة:

function square(number) { // parameter = recipient
  return number * number;
}
var value = 10;
square(value);   // argument = Sender
document.write(value);  // log: 10

على عكس المثال السابق، اذا قمت بتمرير كائن (مثلا، قيمة غير اولية ك Array او كائن معرف من قبل المستخدم) كبارامتر، وقامت الدالة على تغيير خصائص الكائن، سيكون هذا التغيير مرئيا خارج الدالة:

function myFunc(theObject) {
  theObject.make = "Toyota";
}

var mycar = {make: "Honda", model: "Accord", year: 1998};
var x, y;

x = mycar.make; // x gets the value "Honda"

myFunc(mycar);
y = mycar.make; // y gets the value "Toyota"
                // (the make property was changed by the function)

الاعلان عن الدوال بصيغة ال Function expressions

في حين ان الدالة اعلاه تم الاعلان عنها بصيغة  function declaration  او function statement، يمكن ايضا انشاء الدوال بصيغة ال function expression. كما يمكن ايضا للدوال ان تكون بصيغة ال anonymous (دوال غير مسماة). على سبيل المثال، الدالة square يمكن ايضا تعريفها بصيغة function expression على هذا النحو التالى:

var square = function(number) { return number * number };
var x = square(4) // x gets the value 16

يمكن تعيين اسم للدوال بصيغة ال function expression، لجعل الدالة تعيد استدعاء ذاتها (الاستدعاء الداتي)، أو استخدامه في المنقح debugger. او لتعقب اخطاء الدالة من خلال stack traces.

var factorial = function fac(n) { return n<2 ? 1 : n*fac(n-1) };

console.log(factorial(3));

يمكنك تمرير دالة كبارامتر لدالة اخرى. يظهر المثال التالي الدالة map تستخدم دالة اخرى كبارامتر اول لها :

function map(f,a) {
  var result = [], // Create a new Array
      i;
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
  return result;
}

طريقة استخدامها مع الصيغة function expression:

var multiply = function(x) { return x * x * x; };
map(multiply, [0, 1, 2, 5, 10]);

طريقة استخدامها مع الصيغة anonymous function مباشرة:

map(function(x) {return x * x * x}, [0, 1, 2, 5, 10]);

returns [0, 1, 8, 125, 1000].

في الجافاسكريبت يمكن الاعلان عن دالة اعتمادا على شرط. على سبيل المثال، ستقوم التعليمة البرمجية التالية بالاعلان عن الدالة myFunc شرط ان يكون  num يساوي 0:

var myFunc;
if (num === 0){
  myFunc = function(theObject) {
    theObject.make = "Toyota"
  }
}

بالإضافة إلى انشاء الدوال كما هو موضح اعلاه، يمكنك ايضا استخدام الصيغة Function constructor لانشاء دوال من خلال السلاسل الحرفية، تنفذ وقت التشغيل، تماما كما في  ()eval.

هناك فرق بين الدوال (functions) والوظائف (methods)، الدوال هي ما نناقشه في هذا الفصل، والوظائف هي تلك المرتبطة بالكائنات (قيمة لخاصية في الكائن)، اقرأ المزيد حول الكائنات والوظائف العمل مع الكائنات [عربي].

إستدعاء الدوال

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

square(5);

تقوم التعليمة البرمجية اعلاه باستدعاء الدالة مع البارامتر 5. ثم تقوم بتنفيذ التعليمات البرمجية المرتبطة بها وترجع القيمة 25.

الدوال يجب ان تنتمي للنطاق الذي استدعيت فيه، ولان الدوال بصيغة function declaration ترفع اعلى النطاق، فمن الممكن أن تعرف في وقت لاحق، كما في المثال التالي:

console.log(square(5));
/* ... */
function square(n) { return n*n }

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

ملاحظة: سيعمل هذا فقط عندما يتم تعريف الدالة باستخدام الصيغة (مثل {}()function funcName). اما التعليمة البرمجية التالية فسوف لن تعمل، وهذا يعني ان الية الرفع (hoisting) بالنسبة للدوال تعمل فقط مع الدوال ذات الصيغة function declaration ولا تعمل مع الدوال ذات الصيغة function expression.

console.log(square); // square is hoisted with an initial value undefined.
console.log(square(5)); // TypeError: square is not a function
var square = function(n) {
  return n * n;
}

arguments الدالة لا تقتصر على الاعداد او السلاسل الحرفية فقط، يمكنك تمرير الكائنات ايضا. في القسم الخاص بالعمل مع الكائنات العمل مع الكائنات[عربي] توجد دالة باسم ()showProps توضح كيفية تمرير كائن ك argument للدالة.

يمكن للدالة أن تقوم بإستدعاء ذاتها. على سبيل المثال، هذه الدالة تقوم بحساب المضروب بشكل متكرر :

function factorial(n){
  if ((n === 0) || (n === 1))
    return 1;
  else
    return (n * factorial(n - 1));
}

في كل مرة تقوم الدالة باستدعاء ذاتها، يتم خصم 1 من قيمة البرامتر الممرر، ويتم ضرب القيمة المحصلة في القيمة العائدة من جديد، فرضا، اذا كانت القيمة الممررة 5، الاستدعاء الاول سيكون 5، الاستدعاء الثاني سيكون 4، الاستدعاء الثالث سيكون 3، وهكذا، ويتم ضرب هذه القيم العائدة مع بعضها البعض. بهذا الشكل :  5 * 4 * 3 * 2 * 1 == 120

امثلة متنوعة:

var a, b, c, d, e;
a = factorial(1); // a gets the value 1    // 1 * 1
b = factorial(2); // b gets the value 2    // 2 * 1
c = factorial(3); // c gets the value 6    // 3 * 2 * 1
d = factorial(4); // d gets the value 24   // 4 * 3 * 2 * 1
e = factorial(5); // e gets the value 120  // 5 * 4 * 3 * 2 * 1

هناك طرق أخرى لاستدعاء الدوال. غالبا ما تكون هناك حالات تحتاج الى دالة تُستدعى بشكل ديناميكي، حيث يمكن التعامل مع مجموعة من ال arguments، وحيث سياق (context) استدعاء الدالة يجب ان ينشا في وظيفة لكائن يحدد وقت التشغيل. وهذا يبين ان الدوال هي نفسها كائنات، وهذه الكائنات بدورها لديها وظائف ( شاهد Function object). من ضمن هذه الوظائف، الوظيفة ()apply يمكن استخدامها لتحقيق هذه الاهداف.

نطاق الدالة

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

// المتغيرات التالية معرفة في النطاق العام
var num1 = 20,
    num2 = 3,
    name = "Chamahk";

// هذه الدالة معرفة في النطاق العام
function multiply() {
  return num1 * num2;
}

multiply(); // Returns 60

// مثال على دالة داخل دالة
function getScore () {
  var num1 = 2,
      num2 = 3;

  function add() {
    return name + " scored " + (num1 + num2);
  }

  return add();
}

getScore(); // Returns "Chamahk scored 5"

النطاق ومكدس الدوال

الاستدعاء الذاتي

يمكن للدالة ان تستدعي داتها بثلاثة طرق:

  1. من خلال اسم الدالة
  2. arguments.callee
  3. من خلال المتغيرات التي تشير إلى الدالة

على سبيل المثال، انظر الدالة التالية:

var foo = function bar() {
   // statements go here
};

تضمين الاستدعاء الذاتي داخل جسم الدالة bar:

  1. ()bar
  2. ()arguments.callee
  3. ()foo

الدوال التي تقوم باستدعاء نفسها تسمى recursive function. الاستدعاء الداتي يشبه آلِية الحلقات في بعض النواحي، كلاهما ينفذان التعليمات البرمجية نفسها عدة مرات، وايضا كلاهما يتطلبان تعبير شرطي (لتجنب التكرار الى ما لا نهاية، او بالاحرى، الاستدعاء الذاتي الى ما لا نهاية في حالتنا هذه). على سبيل المثال، الحلقة التالية:

var x = 0;
while (x < 10) { // "x < 10" is the loop condition
   // do stuff
   x++;
}

المثال التالي يبين دالة تقوم بالاستدعاء الذاتي، يمكنها محاكات الحلقة :

function loop(x) {
  if (x >= 10) // "x >= 10" is the exit condition (equivalent to "!(x < 10)")
    return;
  // do stuff
  loop(x + 1); // the recursive call
}
loop(0);

ومع ذلك، لا يمكن أن تكون بعض الخوارزميات حلقات تكرارية بسيطة. على سبيل المثال، الحصول على كافة العقد nodes من بنية الشجرة (DOM) اسهل باستخدام الاستدعاء الذاتي:

function walkTree(node) {
  if (node == null) //
    return;
  // do something with node
  for (var i = 0; i < node.childNodes.length; i++) {
    walkTree(node.childNodes[i]);
  }
}

على عكس المثال السابق مع الحلقة، هنا، كل استدعاء داتي يستدعي نفسه ينتج عنه العديد من الاستدعاءات. 

من الناحية النظرية، من الممكن تحويل أي خوارزمية الاستدعاء الذاتي الى خوارزمية الاستدعاء العادي (مع الحلقات، على سبيل المثال). عموما، المنطق الناتج أكثر تعقيداً ويتطلب استخدام  المكدس. الاستدعاء الذاتي أيضا يستخدم المكدس، مكدس الدالة function stack.

سلوك مكدس الذاكرة المؤقتة يمكن أن ينظر إليه كما في المثال التالي:

function foo(i) {
  if (i < 0)
    return;
  console.log('begin:' + i);
  foo(i - 1);
  console.log('end:' + i);
}
foo(3);

// Output:

// begin:3
// begin:2
// begin:1
// begin:0
// end:0
// end:1
// end:2
// end:3

الدوال المتداخلة  و الاغلاق (closures)

يمكن انشاء دالة داخل دالة اخرى. الدالة الداخلية هي دالة خاصة private بالدالة الخارجة. الدالة الداخلية تشكل الاغلاق closure، والإغلاق هو فقط تعبير (عموما الاغلاق هو دالة). والذي يمكنه الوصول إلى المتغيرات المجانية free variables (المصطلح free variable يشير الى المتغيرات المستخدمة في الدالة، وهي ليست متغيرات محلية او بارامترات لهذه الدالة. بمعنى اخر هي متغيرات معرفة خارج الدالة وتستفيد منها الدالة، وهذا هو سبب تسميتها بالمتغيرات المجانية)،  كما يمكنه ايضا، الوصول الى اي شئ في البيئة التي ترتبط بها هذه المتغيرات المجانية.

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

الخلاصة:

  • لايمكن الوصول إلى الدالة الداخلية الا من خلال التعليمات البرمجية الخاصة بالدالة الخارجية.
  • الدالة الداخلية تشكل الاغلاق closure: الدالة الداخلية يمكنها استخدام البرامترات والمتغيرات الخاصة بالدالة الخارجية، فيما لا يمكن للدالة الخارجية استخدام البرامترات والمتغيرات الخاصة بالدالة الداخلية.

يظهر المثال التالي الدوال المتداخلة:

function addSquares(a,b) {
  function square(x) {
    return x * x;
  }
  return square(a) + square(b);
}
a = addSquares(2,3); // returns 13
b = addSquares(3,4); // returns 25
c = addSquares(4,5); // returns 41

بما ان الدالة الداخلية تشكل closure. فمن الضروري استدعاء الدالة الخارجية أولا، بعد ذالك يمكنك تحديد ال  arguments لكل منهما :

function outside(x) {
  function inside(y) {
    return x + y;
  }
  return inside;
}
fn_inside = outside(3); // Think of it like: give me a function that adds 3 to whatever you give it
result = fn_inside(5); // returns 8

result1 = outside(3)(5); // returns 8

الحفاظ على المتغيرات

في المثال اعلاه، لاحظ كيف تم الحفاظ على x عندما تم ارجاع الدالة inside. الاغلاق يحفاظ على البرامترات والمتغيرات في جميع النطاقات التي تشير إليه. مع كل استدعاء للدالة الخارجية، يمكنك تعيين arguments مختلفة، سيتم إنشاء إغلاق جديد مع كل استدعاء للدالة outside. يمكن تحرير الذاكرة فقط عندما يكون عائد الدلة inside غير متاحا.

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

الدوال الاكثر تداخلا

الدوال يمكن ان تكون اكثر تداخلا، بمعنى، الدالة (A) تحتضن الدالة (B)، والدالة (B) تحتضن الدالة (C). هنا كل من الدالة B و C تشكل closures، وهكذا B يمكنها الوصول الى  A، و  C يمكنها الوصول الى B. بالاضافة الى ذالك، C يمكنها الوصول الى B و A، وبالتالي، الإغلاق يمكن أن يحتوي على عدة نطاقات. وهذا ما يسمى بسلسلة النطاق scope chaining. (سيتم شرح لماذا يطلق عليه "تسلسل" في وقت لاحق).

انظر في المثال التالي:

function A(x) {
  function B(y) {
    function C(z) {
      console.log(x + y + z);
    }
    C(3);
  }
  B(2);
}
A(1); // logs 6 (1 + 2 + 3)

في هذا المثال C تصل الى y الخاصة ب B وايضا الى x الخاصة ب A، أصبح هذا ممكناً لأن:

  1. B تشكل closure، وتمتلك A، بمعنى B يمكنها الوصول الى البارامترات والمتغيرات الخاصة ب A.
  2. C تشكل closure، وتمتلك B.
  3. بسبب ان B تمتلك A، فقد اصبح C يمتلك A، وعليه ف C يمكنه الوصول الى البارامترات والمتغيرات الخاصة ب B و A. بعبارات أخرى، C سلسلة نطاقات ل B و A في هذا الترتيب.

العكس ليس صحيحاً. A لا يمكنها الوصول الى C، لان A لا يمكنها الوصول لاي من البارامترات او المتغيرات الخاصة ب B. (فيما C هي متغير لها). وهكذا، C ستصبح خاصة private فقط ب B.

تضارب الاسماء

عند وجود اثنين من البارامترات أو المتغيرات التي تحمل نفس الاسم في نطاقات الاغلاق، فهذا يسمى تضارب في الاسماء، وفي هذه الحالة، ستكون الاسبقية للنطاقات الاكثر عمقا في استخدام هذا الاسم، اما بالنسبة للنطاقات الأكثر سطحية سوف تحظى بأولوية أدنى لاستخدام هذا الاسم، من وجهة نظر سلسلة النطاق، النطاق الاول في السلسلة هو النطاق الاكثر عمقا ( اسفل السلسلة)، والنطاق الاخير في السلسلة هو النطاق الاكثر سطحية (اعلى السلسلة). شاهد المثال التالي:

function outside() {
  var x = 10;
  function inside(x) {
    return x;
  }
  return inside;
}
result = outside()(20); // returns 20 instead of 10

يحدث تعارض الاسم  في التعليمة return x، وهو مابين الباراميتر x الخاص ب inside وبين المتغير x الخاص ب outside. سلسلة النطاق سترى الامر على هذا النحو {inside, outside, global object}. وبناءا عليه x الخاص ب inside سياخد الاسبقية على x الخاص ب outside، وبالتالي الناتج هو 20 (inside x) بدلا من 10 (outside x).

الاغلاقات - Closures

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

var pet = function(name) {   // The outer function defines a paramrter called "name"
  var getName = function() {
    return name;             // The inner function has access to the "name" paramrter of the outer function
  }
  return getName;            // Return the inner function, thereby exposing it to outer scopes
},
myPet = pet("Vivie");

myPet();                     // Returns "Vivie"

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

var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex === "string" && (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
pet.getName();                  // Vivie

pet.setName("Oliver");
pet.setSex("male");
pet.getSex();                   // male
pet.getName();                  // Oliver

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

var getCode = (function(){
  var secureCode = "0]Eal(eh&2";    // A code we do not want outsiders to be able to modify...

  return function () {
    return secureCode;
  };
})();

getCode();    // Returns the secureCode

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

var createPet = function(name) {  // Outer function defines a variable called "name"
  return {
    setName: function(name) {    // Enclosed function also defines a variable called "name"
      name = name;               // ??? How do we access the "name" defined by the outer function ???
    }
  }
}

الكلمة المحجوزة this (في بعض الاحيان تسمى بالمتغير العجيب)، ينبغي التعامل معها بحذر في حالات الإغلاق. احذر، ف this تشير إلى السياق حيث سيتم استدعاء الدالة وليس إلى المكان حيث تم تعريف الدالة.

استخدام الكائن arguments

يمكنك التعامل مع  arguments الدالة من الداخل، من خلال الكائن (arguments او الحجج). يمكنك معالجة ال  arguments الممررة الى الدالة على النحو التالي:

arguments[i]

حيث ان i هو الفهرس الرقمي لل arguments، ويبتدئ من 0، وبالتالي، ال argument الاول الممرر الى الدالة سيكون arguments[0]. لمعرفة عدد ال arguments الممررة نستخدم arguments.length.

باستخدام الكائن arguments، يمكنك استدعاء دالة مع arguments أكثر من التي تم التصريح بها رسميا. وهي مفيذة جدا، خصوصا إذا كنت لا تعرف مسبقاً كم عدد ال  arguments التي ستمرر اثناء استدعاء الدالة. يمكنك استخدام arguments.length لمعرفة عدد البرامترات الممرة الى الدالة، حتى تتمكن بعد ذالك من التعامل معها من خلال الكائن arguments.

على سبيل المثال، يمكننا انشاء دالة تقوم بوصل عدة سلاسل حرفية. ال argument الوحيد المحدد رسميا في الدالة، هو السلسلة الحرفية التي ستفصل بين باقي السلاسل الحرفية التي ستمرر ك arguments بعد ال argument الرسمي للدالة.  كما في المثال التالي:

function myConcat(separator) {
   var result = "", // initialize list
       i;
   // iterate through arguments
   for (i = 1; i < arguments.length; i++) {
      result += arguments[i] + separator;
   }
   return result;
}

يمكنك تمرير اي عدد من ال arguments لهذه الدالة، وسترتبط ببعضها البعض من خلال ما سيمرر الى ال argument الرسمي:

// returns "red, orange, blue, "
myConcat(", ", "red", "orange", "blue");

// returns "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// returns "sage. basil. oregano. pepper. parsley. "
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

ملاحظة: المتغير arguments هو شبه مصفوفة، ولكنه ليس مصفوفة. وانما يتصرف كالمصفوفة، يستخدم الفهرسة الرقمية، يستخدم الخاصية length، ومع ذالك، لا يمكنه استخدام الوظائف الخاصة بالمصفوفات مثل push او join ...الخ.

الفرق بين parameters و arguments

Function parameters او بارامترات الدالة، هي الأسماء المدرجة في تعريف الدالة. فيما Function arguments هي القيم الحقيقية التي تمرر إلى الدالة عند الاستدعاء (راجع التعريف والاستدعاء اعلى الصفحة). انظر المثال التالي:

function foo( param1, param2, ...) // parameters {
    // Do things
}
foo(arg1, arg2, ...); // arguments

ملاحظة: ال parameter هو متغير باسم معين يمرر الى الدالة. تستخدم الباراميترات لجلب ال arguments داخل الدوال.

راجع الكائن Function في مرجع الجافا سكريبت لمزيد من المعلومات.

بارامترات الدالة

بدأً من ECMAScript 6، أصبح هناك نوعان من البارامترات: البارامترات الإفتراضية وبقية البارامترات.

البارامترات الإفتراضية

في الجافاسكريبت، القيمة الافتراضية لبرامترات الدوال هي undefined. ومع ذالك، في بعض الحالات، قد يكون من المفيد تعيين قيمة افتراضية مختلفة. البارامترات الافتراضية يمكنها تدارك الموقف.

قبل ECMAScript 2015، كانت الاستراتيجية العامة لوضع الافتراضات هي اختبار قيمة البارامتر في جسم الدالة وتعيين قيمة له اذا كانت قيمته undefined. على سبيل المثال، في التعليمة البرمجية التالية، لم يتم تحديد قيمة للبارامتر b في الاستدعاء، وبالتالي قيمتة ستساوي undefined، عند اختبار (a * b) ستعود الدالة multiply ب NaN. لتجنب هذا،  يقوم السطر الثاني في التعليمة البرمجية اسفله بتعيين قيمة افتراضية للبارامتر b:

function multiply(a, b) {
  b = typeof b !== 'undefined' ?  b : 1;

  return a*b;
}

multiply(5); // 5

ابتداءا من ECMAScript 2015، اصبح من الممكن عمل اعدادات افتراضية على غرار (php)، والاختبار في جسم الدالة لم يعد ضروريا. الان، ببساطة يمكنك تعيين 1 كقيمة افتراضية للبارامتر b في تعريف الدالة:

function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5

لمزيد من التفاصيل، راجع  default parameters في مرجع الجافاسكريبت.

بقية البارامترات - rest parameter

الصيغة rest parameter تسمح بتمثيل عدد غير محدود من ال arguments كمصفوفة. في هذا المثال، نستخدم بقية البارامترات لتجميع ال arguments ابتداءا من البرامتر الثاني لغاية النهاية. ثم نقوم بضربها باول بارامتر. هذا المثال يستخدم دالة السهم، والتي سندرسها في القسم التالي.

function multiply(multiplier, ...theArgs) {
  return theArgs.map(x => multiplier * x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

دوال السهم - Arrow functions

تعبيرات دوال السهم تسمح لك باستخدام تعبيرا أكثر إيجازاً من التعبير عن الوظائف الكلاسيكية. والقيمة this يتم ربطها بشكل نحوي. فيما تكون دوال السهم مجهولة الاسم anonymous. راجع ايضا هذه المدونة  ES6 In Depth: Arrow functions.

اثنين من العوامل التي أثرت في مقدمة دوال السهم: الدوال المختصرة و lexical this.

الدوال المختصرة

في بعض الأنماط الوظيفية، الدوال المختصرة هي موضع ترحيب. قارن التعليمات البرمجية التالية:

var a = [
  "Hydrogen",
  "Helium",
  "Lithium",
  "Beryl­lium"
];

var a2 = a.map(function(s){ return s.length });

var a3 = a.map( s => s.length );

التعليمة Lexical this

قبل وجود وظائف السهم، كانت كل دالة جديدة تعرف قيمة ال this الخاصة بها (كائن جديد في حالة الدالة الإنشائية، undefined في استدعاءات الدوال مع الوضع الصارم، في سياق الكائن قيد التشغيل في حالة الوظيفة، إلخ.). وهذا يمكن أن يسبب بعض المشاكل مع نمط البرمجة الكائنية:

function Person() {
  // The Person() constructor defines 'this' as itself.
  this.age = 0;

  setInterval(function growUp() {
    // In nonstrict mode, the growUp() function defines 'this'
    // as the global object, which is different from the 'this'
    // defined by the Person() constructor.
  this.age++;
  }, 1000);
}
var p = new Person();

في ECMAScript 3/5، تم إصلاح هذه المشكلة عن طريق تخزير القيمة this في متغير اخر.

function Person() {
  var self = this; // Some choose `that` instead of `self`.
                   // Choose one and be consistent.
  self.age = 0;

  setInterval(function growUp() {
    // The callback refers to the `self` variable of which
    // the value is the expected object.
    self.age++;
  }, 1000);
}

بدلا من ذلك، يمكننا إنشاء دالة ملزمة bound function بحيث تكون "احسن"  قيمة this سيتم تمريرها إلى الدالة ()growUp.

دوال السهم تلتقط القيمة this من السياق المغلق (enclosing context)، لذا ستعمل التعليمة البرمجية التالية كما هو متوقع.

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| properly refers to the person object
  }, 1000);
}

var p = new Person();

دوال معرفة مسبقا

جافا سكريبت لديها العديد من الوظائف المدمجة ذات المستوى الاعلى top-level :

()eval

الوظيفة ()eval تستخدم لاختبار شفرة الجافا سكريبت على شكل سلسلة حرفية.

()uneval

الوظيفة ()uneval تستخدم لانشاء سلسلة حرفية عبارة عن مصدر كود الكائن Object.

()isFinite

الدالة العامة () isFinite تقوم بتحديد ما إذا كانت القيمة التي تم تمريرها عدد محدود. إذا لزم الأمر، يتم تحويل البارامتر إلى رقم.

()isNaN

تستخدم الدالة()isNaN للتاكد من ان القيمة ليست رقمية NaN  ملاحظة: يمكننا ايضا استخدام Number.isNaN(), الجديدة في ECMAScript 6 او استخدام التعليمة typeof. كلها تادي نفس الغرض.

()parseFloat

تستخدم الدالة ()parseFloat لتحويل سلسلة حرفية الى عدد كسري.

()parseInt

تستخدم الدالة ()parseInt لتحويل سلسلة حرفية الى عدد صحيح (البارامتر الثاني خاص بالتعامل مع القاعدة في الأنظمة العددية الرياضية).

()decodeURI

تستخدم الدالة ()decodeURI لفك تشفير معرف الموارد الموحد (Uniform Resource Identifier (URI التي تم إنشاؤها مسبقا من طرف encodeURI او عن طريق نفس الروتين.

()decodeURIComponent

تستخدم الوظيفة ()decodeURIComponent لفك تشفير معرف عناصر الموارد الموحدة (Uniform Resource Identifier (URI التي تم إنشاؤها مسبقا من طرف encodeURIComponent او عن طريق نفس الروتين.

()encodeURI

تستخدم الوظيفة ()encodeURI لتشفير معرف الموارد الموحد (Uniform Resource Identifier (URI باستبدال كل مثيل من أحرف معينة بواحد، اثنان، ثلاثة، أو أربعة تهريبات متوالية تمثل ترميز الاحرف UTF-8 (لن يكون إلا أربع تهريبات متوالية لرموز تتألف من اثنين من الحروف "البديلة").

()encodeURIComponent

تستخدم الوظيفة ()encodeURIComponent لتشفير معرف عناصر الموارد الموحدة (Uniform Resource Identifier (URI باستبدال كل مثيل من أحرف معينة بواحد، اثنان، ثلاثة، أو أربعة تهريبات متوالية تمثل ترميز الاحرف UTF-8 (لن يكون إلا أربع تهريبات متوالية لاحرف تتألف من اثنين من الحروف "البديلة").

()escape

الوظيفة ()escape الغير مرغوب فيها. تحتسب سلسلة جديدة من بعض الأحرف التي يجب استبدلها من قبل hexadecimal escape sequence. استخدم encodeURI او استخدم encodeURIComponent بدلا عنها.

()unescape

الوظيفة()unescape الغير مرغوب فيها. تحتسب سلسلة جديدة بحيث hexadecimal escape sequence. اسبدلت مع الرمز الذي يمثلها. متتالية التهريب يمكن ان تاتى من دالة مثل escape. على العموم استخدم decodeURI() او استخدم decodeURIComponent بدلا عنها.

 

Document Tags and Contributors

 Contributors to this page: Youssef-Belmeskine, Benseidseid
 Last updated by: Youssef-Belmeskine,