انتقل إلى المحتوى

٢.٢ الدوال المجهولة

إنشاء الدوال المجهولة

التمرين 1: أنشئ دالة تعرض "Hello" في وحدة التحكم.

function displayHello(){
  console.log("Hello");
}

displayHello();

النتيجة هي: Hello

التمرين 2: أعدنا كتابة شيفرة التمرين 1. تأمّلها.

var displayHello = function(){
  console.log("Hello");
};

displayHello();

النتيجة هي: Hello

الدالة المجهولة

تعلّمنا التصريح عن متغير وإسناد قيمة إليه، مثل var greet = "Hello"; وهو متغير اسمه greetوقيمته "Hello". في التمرين 2أنشأنا متغيرًا باسم displayHelloوأسندنا function إليه. لكن هذه الدالة بلا اسم، لذا نُسمّيها دالة مجهولة.

التمرين 3: أنشئ دالة مجهولة تجمع عددين.

var addTwoNumbers = function(num1, num2){
  return num1 + num2;
}

console.log(addTwoNumbers(5, 6));
console.log(addTwoNumbers(10, 20));

النتيجة كما يلي:

11
30

التمرين 4: أنشئ دالة مجهولة تتحقّق مما إذا كانت القيمة صادقة.

var isTrue = function(value){
  if(value === true){
    return true;
  }else{
    return false;
  }
}

console.log(isTrue(""));
console.log(isTrue("Hello"));
console.log(isTrue(true));
console.log(isTrue(null));

النتيجة كما يلي:

false
false
true
false

التمرين 5: أنشئ دالة مجهولة تضيف 123 إلى قيمة مُعطاة.

var add123 = function(value){
  return (value + 123);
}

console.log(add123("Hello"));
console.log(add123("nana"));
console.log(add123(undefined));
console.log(add123(null));
console.log(add123(false));

النتيجة كما يلي:

Hello123
nana123
NaN
123
123

ما الذي يحدث؟ ما معنى NaN؟ ولماذا null + 123 يساوي 123؟ ولماذا false + 123 يساوي 123؟ لنُجب عن هذه الأسئلة!

التحويل الضمني للأنواع

التمرين 6: أنشئ دالة مجهولة تحوّل قيمة إلى عدد. تلميح: استخدم Number(value).

var convertToNumber = function(value){
  return Number(value);
}
console.log(convertToNumber("123"));
console.log(convertToNumber(false));
console.log(convertToNumber(true));
console.log(convertToNumber("lala"));
console.log(convertToNumber(null));
console.log(convertToNumber(undefined));

النتيجة كما يلي:

123
0
1
NaN
0
NaN

Number(value)

Number(value) تحوّل القيمة إلى عدد إن أمكن؛ وإلا فتُرجع NaN، أي ليست عددًا (Not a Number)

لنفهم الشيفرة السابقة:

  1. تحويل "123" إلى عدد: يمكن تحويل هذه السلسلة إلى عدد. Number("123") تساوي 123.
  2. تحويل false إلى عدد: Number(false) تتحوّل إلى 0 في JavaScript.
  3. تحويل true إلى عدد: Number(true) تتحوّل إلى 1 في JavaScript.
  4. تحويل "lala" إلى عدد: لا تمثّل هذه السلسلة عددًا صالحًا. Number("lala") ليست عددًا صالحًا. NaN.
  5. تحويل null إلى عدد: Number(null) تتحوّل إلى 0 في JavaScript.
  6. تحويل undefined إلى عدد: undefined لا يمكن تحويلها إلى عدد صالح. Number(undefined) ليست عددًا صالحًا. NaN.

التمرين 7: أنشئ دالة مجهولة تحوّل قيمة إلى سلسلة نصية. تلميح: استخدم String(value).

var convertToString = function(value){
  return String(value);
}
console.log(convertToString(123));
console.log(convertToString(false));
console.log(convertToString(true));
console.log(convertToString("lala"));
console.log(convertToString(null));
console.log(convertToString(undefined));

النتيجة كما يلي:

123
false
true
lala
null
undefined

String(value)

String(value) تحوّل القيمة إلى سلسلة نصية.

لنفهم الشيفرة السابقة:

  1. تحويل 123 إلى سلسلة نصية: يمكن تحويل العدد بسهولة. String(123) تساوي "123".
  2. تحويل false إلى سلسلة نصية: String(false)تساوي "false".
  3. تحويل true إلى سلسلة نصية: String(true) قيمتها true.
  4. تحويل "lala" إلى سلسلة نصية: String("lala") هي سلسلة نصية أصلًا.
  5. تحويل null إلى سلسلة نصية: String(null) تساوي "null".
  6. تحويل undefined إلى سلسلة نصية: String (undefined) تساوي "undefined".

التمرين 8: أعدنا كتابة التمرين 5 كما يلي.

var add123 = function(value){
  var result = value + 123
  console.log(`value: ${value}, value type: ${typeof value}`);
  console.log(`${typeof value} + ${typeof 123} = ${typeof result}`);
  console.log(`${value} + 123 = ${result}`)
  console.log("..............................");
}

add123("Hello");
add123("nana");
add123(undefined);
add123(null);
add123(false);

النتيجة كما يلي:

value: Hello, value type: string
string + number = string
Hello + 123 = Hello123
..............................
value: nana, value type: string
string + number = string
nana + 123 = nana123
..............................
value: undefined, value type: undefined
undefined + number = number
undefined + 123 = NaN
..............................
value: null, value type: object
object + number = number
null + 123 = 123
..............................
value: false, value type: boolean
boolean + number = number
false + 123 = 123

لا تقلق الآن بشأن صياغة الجمل باستخدام console.log؛ سنشرحها لاحقًا. ركّز على تحليل النتائج.

عند جمع سلسلة نصية مع قيمة أخرى، تُحوَّل القيمة الأخرى ضمنيًا إلى سلسلة نصية.

"Hello" is a string
123 is a number
string + number =  string + (String(number))
Then, "Hello" + 123 = "Hello" + "123" = "Hello123

"nana" is a string
123 is a number
string + number =  string + (String(number))
Then, "nana" + 123 = "nana" + "123" = "nana123"

لاحظ أنه عند إضافة null أو false إلى عدد، تحوّل JavaScript القيمة null و false إلى صفر.

null is an object(but it evaluates to zero)
null + 123 = 0 + 123 = 123

false is a boolean
123 is a number
boolean + number = Number(boolean) + number
Then, false + 123 = 0 + 123 = 123

لاحظ أنه عند إضافة true إلى عدد، تحوّل JavaScript القيمة true إلى واحد.

true is a boolean
123 is a number
boolean + number = Number(boolean) + number
Then, true + 123 = 1 + 123 = 124

ماذا عن undefined؟ تذكّر أن undefined تعني عدم إسناد قيمة بعد. لا يمكن تحويل undefined إلى عدد صالح. Number(undefined) هي NaN.

undefined is undefined
123 is a number
undefined + 123 = NaN

التمرين 9: اكتب دالة مجهولة تفحص نوع NaN.

var typeofNaN = function(){
  console.log(typeof NaN);
}

typeofNaN();

النتيجة هي: number

التمرين 10: اكتب دالة مجهولة تتحقّق هل القيمة المُعطاة NaN. تلميح: استخدم isNaN().

var isNotANumber = function(value){
  console.log(isNaN(value));
}

isNotANumber(5);
isNotANumber(null);
isNotANumber(NaN);
isNotANumber(undefined);
isNotANumber(false);

النتيجة كما يلي:

false
false
true
true
false

ما NaN؟

NaN تمثّل نتيجة عددية غير صالحة في JavaScript. نوع NaN هو number. ويمكنك التحقّق منها NaN باستخدام isNaN(value)

التمرين 11: أنشئ دالة مجهولة تضيف 456 إلى قيمة مُعطاة.

var add456 = function(value){return (value + 456);}

console.log(add456("Hello"));
console.log(add456("nana"));
console.log(add456(undefined));
console.log(add456(null));
console.log(add456(false));

النتيجة كما يلي:

Hello456
nana456
NaN
456
456

لماذا نستخدم الدوال المجهولة؟

التمرين 12: أنشئ دالة مجهولة تعرض "Hi, I am a great washing machine, I will clean your clothes now." في وحدة التحكم.

var washingMachine = function(){
  console.log("Hi, I am a great washing machine, I will clean your clothes now.");
}

التمرين 13: أنشئ دالة مجهولة تعرض "Hi, I am set on the extra cleaning mode now" في وحدة التحكم.

var extraCleaningMode = function(){
  console.log("Hi, I am set on the extra cleaning mode now");
}

التمرين 14: أنشئ دالة مجهولة تعرض "Hi, I am set on the extra extra cleaning mode now" في وحدة التحكم.

var extraCleaningMode = function(){
  console.log("Hi, I am set on the extra extra cleaning mode now");
}

التمرين 15: أنشئ دالة عادية تحاكي الغسالة وتستقبل وضع التشغيل. إذا كان 1، فتُرجع الدالة المنشأة في التمرين 12. وإذا كان 2، فتُرجع الدالة المنشأة في التمرين 13. وإذا كان 3، فتُرجع الدالة المنشأة في التمرين 14. الوضع الافتراضي هو 1.

function washClothes(mode){
  if(mode === 2){
    return function(){
     console.log("Hi, I am set on the extra cleaning mode now.");
   }
  }else if(mode == 3){
    return function(){
      console.log("Hi, I am set on the extra extra cleaning mode now.");
    }
  }else{
    return function(){
      console.log("Hi, I am a great washing machine, I will clean your clothes now.");
    }
  }
}

washClothes(1)();
washClothes(2)();
washClothes(3)();
washClothes()();

النتيجة كما يلي:

Hi, I am a great washing machine, I will clean your clothes now.
Hi, I am set on the extra cleaning mode now.
Hi, I am set on the extra extra cleaning mode now.
Hi, I am a great washing machine, I will clean your clothes now.

كتبنا شيفرة أكثر تقدّمًا! لنفهمها:

  1. أنشأنا دالة عادية باسم washClothes.
  2. washClothes تستقبل معاملًا واحدًا هو mode.
  3. إذا كان mode يساوي 2، فتُرجع الدالة العادية التمرين 13 وهي دالة مجهولة.
  4. إذا كان mode يساوي 3، فتُرجع الدالة العادية التمرين 14 وهي دالة مجهولة.
  5. وفي غير ذلك تُرجع الدالة العادية التمرين 12 وهي دالة مجهولة.
  6. استدعينا الدالة العادية باسمها washClothes مع الوسيط 1.
  7. بما أنها تُرجع دالة، نحتاج أيضًا إلى استدعاء الدالة المُرجعة باستخدام ().
  8. النتيجة هي الجملة الموجودة في الدالة التمرين 12 : Hi, I am a great washing machine, I will clean your clothes now.
  9. استدعينا الدالة العادية باسمها washClothes مع الوسيط 2.
  10. بما أنها تُرجع دالة، نحتاج أيضًا إلى استدعاء الدالة المُرجعة باستخدام ().
  11. النتيجة هي الجملة الموجودة في الدالة التمرين 13 : Hi, I am set on the extra cleaning mode now.
  12. استدعينا الدالة العادية باسمها washClothes مع الوسيط 3.
  13. بما أنها تُرجع دالة، نحتاج أيضًا إلى استدعاء الدالة المُرجعة باستخدام ().
  14. النتيجة هي الجملة الموجودة في الدالة التمرين 14 : Hi, I am set on the extra extra cleaning mode now.
  15. استدعينا الدالة العادية باسمها washClothes دون وسائط.
  16. بما أنها تُرجع دالة، نحتاج أيضًا إلى استدعاء الدالة المُرجعة باستخدام ().
  17. النتيجة هي الجملة الموجودة في الدالة التمرين 12 : Hi, I am a great washing machine, I will clean your clothes now.

دالة تُرجع دالة!

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

هل يمكن استخدام دوال مسمّاة بدلًا منها؟

في التمارين 12 و13 و14 و15؛ كان يمكن إرجاع دوال مسمّاة، وهذا صحيح. استخدمنا الدوال المجهولة لأننا لا نحتاج إلى إعادة استخدامها في مواضع أخرى.

متى نستخدم الدوال المجهولة؟

  1. حين نحتاج دالة لا نعتزم استخدامها في موضع آخر من الشيفرة.

التمرين 16: أنشئ دالة مجهولة تضبط allPermissions على "Granted" وتعرض "You are allowed here." في وحدة التحكم.

var accessAllowed = function(){
  var allPermissions = "Granted";
  console.log("You are allowed here.");
}

التمرين 17: أنشئ دالة مجهولة تضبط allPermissions على "Removed" وتعرض "You are not allowed here." في وحدة التحكم.

var accessDenied = function(){
  var allPermissions = "Removed";
  console.log("You are not allowed here.");
}

التمرين 18: أنشئ دالة عادية تتحقّق من تسجيل الدخول. إذا كان المستخدم مسجّلًا، فتُرجع دالة التمرين 16؛ وإلا فتُرجع الدالة المنشأة في التمرين 17.

function isLoggedIn(userLoggedIn){
  if(userLoggedIn){
    return function(){
      var allPermissions = "Granted";
      console.log("You are allowed here.");
    }
  }else{
    return function(){
      var allPermissions = "Removed";
      console.log("You are not allowed here.");
    }
  }
}

isLoggedIn(true)();
isLoggedIn(false)();

النتيجة كما يلي:

You are allowed here.
You are not allowed here.

كيف تعمل الشيفرة السابقة؟

  1. أنشأنا دالة عادية باسم isLoggedIn.
  2. isLoggedIn تستقبل معاملًا واحدًا هو userLoggedIn.
  3. إذا كان userLoggedIn صادقًا، فتُرجع الدالة العادية دالة مجهولة.
  4. إذا كان userLoggedIn كاذبًا، فتُرجع دالة مجهولة أخرى.
  5. كتبنا هاتين الدالتين المجهولتين سابقًا في التمرين 16 و التمرين 17
  6. استدعينا الدالة العادية باسمها isLoggedIn مع الوسيط true.
  7. بما أنها تُرجع دالة، نحتاج أيضًا إلى استدعاء الدالة المُرجعة باستخدام ().

لماذا لا نستدعي الدالة المجهولة مباشرةً بدلًا من استدعائها بعد الدالة العادية؟

تعبيرات الدوال المستدعاة فورًا

التمرين 19: أعدنا كتابة التمرين 16 مجددًا. تأمّل الشيفرة التالية.

var accessAllowed = function(){
  var allPermissions = "Granted";
  console.log("You are allowed here.");
}()

النتيجة هي: You are allowed here.

يمكننا بالطبع كتابة accessAllowed() لاستدعاء الدالة المجهولة، لكننا نريد هنا تشغيلها فور تعريفها.

التمرين 20: أعد تنفيذ التمرين 17، واحرص على تنفيذ الدالة فور تعريفها.

var accessDenied = function(){
  var allPermissions = "Removed";
  console.log("You are not allowed here.");
}()

النتيجة هي: You are not allowed here.

التمرين 21: أعد تنفيذ التمرين 18، واحرص على تنفيذ الدالة فور تعريفها.

function isLoggedIn(userLoggedIn){
  if(userLoggedIn){
    return function(){
      var allPermissions = "Granted";
      console.log("You are allowed here.");
    }()
  }else{
    return function(){
      var allPermissions = "Removed";
      console.log("You are not allowed here.");
    }()
  }
}

isLoggedIn(true);
isLoggedIn(false);

النتيجة كما يلي:

You are allowed here.
You are not allowed here.

متى نستخدم الدوال المجهولة؟

  1. حين تحتاج دالة لن تستخدمها في موضع آخر.
  2. حين تريد تنفيذ الدالة فور إنشائها.

التمرين 22: اقرأ الشيفرة وحاول فهم النتيجة.

(function(){
  console.log("Hello");
})();

النتيجة هي: Hello

في التمرين 22؛ أنشأنا دالة مجهولة واستدعيناها فورًا، أي أنشأنا IIFE.

IIFE

اختصار IIFE يعني «تعبير دالة يُستدعى فورًا»، وهو أسلوب لتنفيذ الدالة فور تعريفها.

التمرين 23: اقرأ الشيفرة وحاول فهم النتيجة.

function(){
  console.log("Hello");
}();

النتيجة كما يلي:

Uncaught SyntaxError: Function statements require a function name

لماذا حدث ذلك؟ لإنشاء IIFE بهذه الصياغة، أحط تعبير الدالة بـ ()، وإلا فستحصل على خطأ.

التمرين 24: أنشئ IIFE تضبط كلمة المرور على "topSecret" وتعرض "It is set" في وحدة التحكم.

(function(){
  var password = "topSecret";
  console.log("It is set");
})();

النتيجة هي: It is set

التمرين 25: أنشئ IIFE تضبط الكلمة السرية على "thisIsASecertWord".

(function(){
  var secretWord = "thisIsASecertWord";
})();

التمرين 26: اعرض secretWork في التمرين 25 بوحدة التحكم. اكتب تعليمة العرض خارج الدالة.

(function(){
  var secretWord = "thisIsASecertWord";
})();
console.log(secretWord);

النتيجة هي: Uncaught ReferenceError: secretWord is not defined

لماذا؟ المتغيرات المحلية المصرّح عنها داخل IIFE غير متاحة خارجها. يمكن استخدام هذا النطاق لإخفاء المتغيرات عن بقية الشيفرة.

متى نستخدم الدوال المجهولة؟

  1. حين تحتاج دالة لن تستخدمها في موضع آخر.
  2. حين تريد تنفيذ الدالة فور إنشائها.
  3. حين تستخدمها بصيغة IIFE وتريد إبقاء متغيراتها محلية.

التمرين 27: أنشئ IIFE تُجري عملية حسابية على عدد مُعطى، ثم تضيف 10 إلى النتيجة. افترض أن العملية هي: (num + 5)/10.

(function(num){
  var result = (num + 5)/10;
  console.log(result + 10);
})(50);

النتيجة هي: 15.5

التمرين 28: يحاول شخص الوصول إلى المتغير result في التمرين 27. ما النتيجة؟

(function(num){
  var result = (num + 5)/10;
  console.log(result + 10);
})(50);
console.log(result);

ستكون النتيجة كما يلي:

15.5
Uncaught ReferenceError: result is not defined

رائع! المتغير result غير متاح خارج الدالة؛ فهو محلي لها.

التمرين 29: أنشئ IIFE تضبط المتغير secretNumber على "1111"، ثم تُرجع هذه القيمة.

(function(){
  var secretNumber = 1111;
  return secretNumber;
})();

التمرين 30: يحاول شخص الوصول إلى المتغير secretNumber في التمرين 29. ما النتيجة؟

(function(){
  var secretNumber = 1111;
  return secretNumber;
})();
console.log(secretNumber);

ستكون النتيجة: Uncaught ReferenceError: result is not defined

التمرين 31: في التمرين 30؛ نريد أن يكون secretNumber متاحًا خارج الدالة.

var getSecretNumber = (function(){
  var secretNumber = 1111;
  return secretNumber;
})();
console.log(getSecretNumber);

النتيجة هي: 1111

ماذا حدث هنا؟

  1. أنشأنا متغيرًا جديدًا باسم getSecretNumber.
  2. قيمة getSecretNumber هي القيمة المُرجعة من IIFE، أي 1111.
  3. تظهر 1111 في وحدة التحكم.