JavaScript و TypeScript: 11 تركيبات مضغوطة يجب أن تعرفها

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



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







في هذا المنشور ، سألقي نظرة على بعض التركيبات المدمجة المفيدة جدًا (وأحيانًا المشفرة) التي قد تجدها في JavaScript و TypeScript. بعد دراستها ، يمكنك استخدامها بنفسك ، أو على الأقل يمكنك فهم رمز هؤلاء المبرمجين الذين يستخدمونها.



1. المشغل ؟؟



عامل التحقق من قيم nullو undefined(عامل الاندماج الصفري) يشبه علامتي استفهام ( ??). من الصعب تصديق أن هذا الاسم هو المشغل الأكثر شعبية. صحيح؟



معنى هذا العامل هو أنه يعرض قيمة المعامل الأيمن إذا كانت قيمة المعامل الأيسر تساوي nullأو undefined. لا ينعكس هذا بوضوح في اسمها ، ولكن حسنًا ، ما هو - هذا هو. إليك كيفية استخدامه:



function myFn(variable1, variable2) {
  let var2 = variable2 ?? "default value"
  return variable1 + var2
}

myFn("this has ", "no default value") // "this has no default value"
myFn("this has no ") // "this has no default value"
myFn("this has no ", 0) // "this has no 0"


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



2. عامل التشغيل ؟؟ =



عامل يستخدم لتعيين قيمة إلى متغير فقط إذا كان له قيمة nullأو undefined(عامل التعيين المنطقي) يشبه علامتي استفهام متبوعين بعلامة يساوي ( ??=). فكر في الأمر على أنه امتداد للعامل أعلاه ??.



دعنا نلقي نظرة على مقتطف الكود السابق ، مع إعادة كتابته باستخدام ??=.



function myFn(variable1, variable2) {
  variable2 ??= "default value"
  return variable1 + variable2
}

myFn("this has ", "no default value") // "this has no default value"
myFn("this has no ") // "this has no default value"
myFn("this has no ", 0) // "this has no 0"


??=يسمح لك عامل التشغيل بالتحقق من قيمة معلمة الوظيفة variable2. إذا كانت تساوي nullأو undefined، فسوف تكتب قيمة جديدة لها. خلاف ذلك ، لن تتغير قيمة المعلمة.



ضع في اعتبارك أن التصميم ??=قد يبدو غير مفهوم لمن ليسوا على دراية به. لذلك ، إذا كنت تستخدمه ، فقد ترغب في إضافة تعليق قصير مع التوضيحات في المكان المناسب في الكود.



3. إعلان مختصر لمنشئات TypeScript



هذه الميزة خاصة بـ TypeScript. لذلك ، إذا كنت من المدافعين عن نقاء JavaScript ، فأنت تفتقد كثيرًا. (مجرد مزاح ، بالطبع ، لكن هذا لا ينطبق على JS العادية).



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



هكذا تبدو:



// ...
class Person {
  
  private first_name: string;
  private last_name: string;
  private age: number;
  private is_married: boolean;
  
  constructor(fname:string, lname:string, age:number, married:boolean) {
    this.first_name = fname;
    this.last_name = lname;
    this.age = age;
    this.is_married = married;
  }
}

// ,   ...
class Person {

  constructor( private first_name: string,
               private last_name: string,
               private age: number,
               private is_married: boolean){}
}


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



الشيء الرئيسي هنا هو عدم نسيان الإضافة {}مباشرة بعد وصف المُنشئ ، لأن هذا يمثل تمثيلًا لجسم الوظيفة. بعد أن يواجه المترجم مثل هذا الوصف ، سيفهم كل شيء وسيقوم بالباقي بنفسه. في الواقع ، نحن نتحدث عن حقيقة أن الجزأين الأول والثاني من كود TS سيتم تحويلهما في النهاية إلى نفس كود JavaScript.



4. مشغل ثلاثي



العامل الثلاثي هو بناء يسهل قراءته. غالبًا ما يتم استخدام هذا المشغل بدلاً من التعليمات القصيرة if…else، حيث يتيح لك التخلص من الأحرف الزائدة وتحويل بنية متعددة الأسطر إلى بنية من سطر واحد.



//   if…else
let isEven = ""
if(variable % 2 == 0) {
  isEven = "yes"
} else {
  isEven = "no"
}

//  
let isEven = (variable % 2 == 0) ? "yes" : "no"


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



let variable = true;

(variable) ? console.log("It's TRUE") : console.log("It's FALSE")


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



5. باستخدام دورة حساب قصيرة يستخدمها عامل التشغيل ||



في JavaScript (وفي TypeScript أيضًا) ، يقوم عامل التشغيل المنطقي ( ||) بتنفيذ نموذج حوسبة مختصرة. أي أنها تُرجع التعبير الأول الذي تم تقييمه كـ true، ولا يتحقق من التعبيرات المتبقية.



وهذا يعني أنه إذا كان هناك البيان التالي if، حيث التعبير expression1يحتوي على قيمة زائفة (تقليص ل false)، و expression2- صحيح (اختزال ل true)، وعندئذ فقط expression1و سيتم احتساب expression2. تعبيرات espression3و expression4لن يتم تقييمها.



if( expression1 || expression2 || expression3 || expression4)


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



function myFn(variable1, variable2) {
  let var2 = variable2 || "default value"
  return variable1 + var2
}

myFn("this has ", " no default value") // "this has no default value"
myFn("this has no ") // "this has no default value"


يوضح هذا المثال كيف يمكنك استخدام عامل ||للكتابة إلى متغير إما قيمة المعلمة الثانية لوظيفة أو قيمة افتراضية. ومع ذلك ، إذا نظرت عن كثب إلى هذا المثال ، يمكنك رؤية مشكلة صغيرة فيه. الحقيقة هي أنه إذا كانت هناك variable2قيمة في 0أو سلسلة فارغة ، var2فسيتم كتابة القيمة الافتراضية إليها ، حيث 0يتم تحويل كل من السلسلة النصية الفارغة إلى false.



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



6. عامل بت مزدوج ~



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



إذا تحدثنا عن عامل البت NOT ( ~) ، فإنه يأخذ رقمًا ، ويحوله إلى عدد صحيح 32 بت (تجاهل البتات "الإضافية") ويعكس بتات هذا الرقم. يؤدي هذا إلى xتحويل القيمة إلى قيمة -(x+1). لماذا نحن مهتمون بمثل هذا التحويل للأرقام؟ وحقيقة أنك إذا استخدمتها مرتين ، فسوف تعطينا نفس نتيجة استدعاء الطريقة Math.floor.



let x = 3.8
let y = ~x // x   -(3 + 1),    ,    
let z = ~y //  y ( -4)  -(-4 + 1)   -  3

//   :

let flooredX = ~~x //      


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



7. تعيين قيم لخصائص الكائن



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



هذا مثال مكتوب بلغة TypeScript.



let name:string = "Fernando";
let age:number = 36;
let id:number = 1;

type User = {
  name: string,
  age: number,
  id: number
}

// 
let myUser: User = {
  name: name,
  age: age,
  id: id
}

// 
let myNewUser: User = {
  name,
  age,
  id
}


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



8. إرجاع ضمني للقيم من دالات الأسهم



هل تعلم أن دالات السهم أحادي السطر ترجع نتائج العمليات الحسابية التي تم إجراؤها على سطر واحد؟



يتيح لك استخدام هذه الآلية التخلص من التعبيرات غير الضرورية return. غالبًا ما تستخدم هذه التقنية في وظائف الأسهم التي يتم تمريرها إلى طرق المصفوفة مثل filterأو map. فيما يلي مثال على TypeScript:



let myArr:number[] = [1,2,3,4,5,6,7,8,9,10]

//  :
let oddNumbers:number[] = myArr.filter( (n:number) => {
  return n % 2 == 0
})

let double:number[] = myArr.map( (n:number) => {
  return n * 2;
})

//  :
let oddNumbers2:number[] = myArr.filter( (n:number) => n % 2 == 0 )

let double2:number[] = myArr.map( (n:number) =>  n * 2 )


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



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



const m = _ => if(2) console.log("true"else console.log("false")


في القسم التالي ، سنواصل الحديث عن وظائف السهم أحادي السطر ، لكننا الآن سنتحدث عن الوظائف التي لا يمكن إنشاؤها بدون الأقواس المتعرجة.



9. معلمات الوظيفة ، والتي يمكن أن يكون لها قيم افتراضية



قدم ES6 القدرة على تحديد القيم التي تم تعيينها لمعلمات الوظيفة الافتراضية. في السابق ، لم يكن لدى JavaScript مثل هذه الإمكانيات. لذلك ، في المواقف التي كان من الضروري فيها تعيين قيم مماثلة للمعلمات ، كان من الضروري اللجوء إلى شيء مثل نموذج حسابات المشغل المخفضة ||.



ولكن الآن يمكن حل نفس المشكلة بكل بساطة:



//    2  
//     ,   
function myFunc(a, b, c = 2, d = "") {
  //   ...
}


آلية بسيطة ، أليس كذلك؟ ولكن ، في الواقع ، كل شيء أكثر إثارة للاهتمام مما يبدو للوهلة الأولى. النقطة المهمة هي أن القيمة الافتراضية يمكن أن تكون أي شيء - بما في ذلك استدعاء الوظيفة. سيتم استدعاء هذه الوظيفة إذا لم يتم تمرير المعلمة المقابلة لها عند استدعاء الوظيفة. هذا يجعل من السهل تنفيذ نمط معلمة الوظيفة المطلوبة:



const mandatory = _ => {
  throw new Error("This parameter is mandatory, don't ignore it!")
}

function myFunc(a, b, c = 2, d = mandatory()) {
  //    ...
}

// !
myFunc(1,2,3,4)

// 
myFunc(1,2,3)


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



10. صب أي قيم لنوع منطقي باستخدام !!



تعمل هذه الآلية على نفس مبدأ البناء أعلاه ~~. أي أننا نتحدث عن حقيقة أنه لإضفاء أي قيمة على نوع منطقي ، يمكنك استخدام عاملين منطقيين NOT ( !!):



!!23 // TRUE
!!"" // FALSE
!!0 // FALSE
!!{} // TRUE


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



يمكن أن يكون هذا البناء القصير مفيدًا في مجموعة متنوعة من المواقف. أولاً ، عندما تحتاج إلى التأكد من تعيين قيمة منطقية حقيقية لمتغير (على سبيل المثال ، إذا كنا نتحدث عن نوع متغير TypeScript boolean). ثانيًا ، عندما تحتاج إلى إجراء مقارنة صارمة (باستخدام ===) شيء مع trueأو false.



11. تدمير ونشر النحو



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



▍ إعادة هيكلة الأشياء



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



يتيح لك استخدام إتلاف الكائنات حل المشكلات المشابهة باستخدام الحد الأدنى من التعليمات البرمجية:



const myObj = {
  name: "Fernando",
  age: 37,
  country: "Spain"
}

// :
const name = myObj.name;
const age = myObj.age;
const country = myObj.country;

// 
const {name, age, country} = myObj;


أي شخص استخدم TypeScript قد رأى هذا التركيب في التعليمات import. يسمح لك باستيراد طرق مكتبة فردية دون تلويث مساحة اسم المشروع بالعديد من الوظائف غير الضرورية:



import { get } from 'lodash'


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



▍ نشر بناء الجملة وإنشاء كائنات ومصفوفات جديدة بناءً على العناصر الموجودة



يؤدي استخدام صيغة ( ) إلى تبسيط مهمة إنشاء مصفوفات وكائنات جديدة بناءً على العناصر الموجودة. الآن يمكن حل هذه المهمة عن طريق كتابة سطر واحد من التعليمات البرمجية حرفيًا ودون اللجوء إلى أي طرق خاصة. هذا مثال:



const arr1 = [1,2,3,4]
const arr2 = [5,6,7]

const finalArr = [...arr1, ...arr2] // [1,2,3,4,5,6,7]

const partialObj1 = {
  name: "fernando"
}
const partialObj2 = {
  age:37
}

const fullObj = { ...partialObj1, ...partialObj2 } // {name: "fernando", age: 37}


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



▍ الجمع بين تدمير ونشر بناء الجملة



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



const myList = [1,2,3,4,5,6,7]
const myObj = {
  name: "Fernando",
  age: 37,
  country: "Spain",
  gender: "M"
}

const [head, ...tail] = myList

const {name, age, ...others} = myObj

console.log(head) //1
console.log(tail) //[2,3,4,5,6,7]
console.log(name) //Fernando
console.log(age) //37
console.log(others) //{country: "Spain", gender: "M"}


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



const [...values, lastItem] = [1,2,3,4]


هذا الرمز لن يعمل.



النتيجة



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



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



ما التركيبات المدمجة التي تستخدمها في كود JavaScript و TypeScript؟










All Articles