Posts Tagged ‘ecmascript’

ترجمة JavaScript. The Core.

13/11/2010

السلام عليكم و رحمة الله و بركاته

هذه المقالة ترجمة JavaScript. The Core للكاتب Dmitry A. Soshnikov طلب منى ترجمتها للعربية و أنا سعيد جدا بذلك و اتمنى ان تستفيدوا جميعا من قرائتها.

  1. الكائن
  2. سلسلة ال prototype
  3. ال constructor
  4. كومة سياق التنفيذ Execution context stack
  5. سياق التنفيذ Execution context
  6. كائن المتغير Variable object
  7. كائن التنشيط Activation object
  8. سلسلة النطاق Scope chain
  9. الإغلاق Closure
  10. القيمة this
  11. ختام

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

جمهور هذه المقالة : المبرمجين ذوى الخبرة و المحترفين .

سنبدأ بالنظر إلى مفهوم الكائن الذى هو أساس ECMAScript المواصفات التى بنيت عليها الجافاسكربت.

الكائن

ال ECMAScript لغة برمجة موجهة بالكائنات – شيئية – مجردة الغاية highly abstracted تعمل على الكائنات ، و بالرغم من وجود البدائيات primitives إلا إنه يمكن تحويلها لكائنات إذا ما لزم الأمر .

الكائن هو مجموعة من الخصائص لها كائن prototype واحد فقط ، هذا ال prototype قد يكون كائن او القيمة null.

هيا بنا نأخذ كائن بسيط لكى نطبق عليه الوصف السابق ، ال prototype لكائن ما يتم الإشارة إليه داخليا – فى محركات ترجمة الجافاسكربت كمثال- عن طريق الخاصية [[prototype]] لا يمكن الوصول إليها من الكود ، سيتم الإشارة إليها فى الصور بالخاصية __proto__ هذه الخاصية غير قياسية موجودة فى محرك الترجمة SpiderMonkey تستخدم للوصول لل prototype من الكود ، أنظر لهذا المثال البسيط :

var foo = {
    x: 10,
    y: 20
};

لدينا الان خاصيتين صريحتين own  أى غير موروثة x و y ، و خاصية ضمنية __proto__ التى تشير إلى prototype الكائن foo كما يلى :

 

A basic object with a prototype.

إذا ما الفائدة من ال prototype ? للإجابة على السؤال هيا بنا نلقى نظرة على مفهوم سلسلة ال  prototype

سلسلة ال prototype

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

سلسلة ال prototype هى سلسلة محدودة من الكائنات تستخدم لتحقيق الوراثة و الخصائص المشتركة .

فكر فى الحالة عندما يكون هناك كائنين يختلفان فى صفات قليلة معينة و يشتركان فى باقى الصفات أخرى ، و عليه فمن الطبيعى اننا سنعيد إستخدام الصفات و الوظائف المشتركة بدون تكرارها فى كل كائن ، فى لغات البرمجة المعتمدة على الاصناف class-based إعادة إستخدام الكود تتم عن طريق وراثة الأصناف class inheritance ، حيث تقوم بوضع الصفات المشتركة فى الصنف A ، بحيث يقوم الصنف  B و C بورث هذه الصفات و يزيد كل صنف منهما بالصفات المميزة له و بذلك يتحقق إعادة إستخدام الكود بدون تكرار .

فى ال ECMAScript لا يوجد مفهوم الأصناف ، تتم الوراثة و إعادة الإستخدام عن طريق سلسلة ال prototype  لا تختلف كثيرا عن الوراثة المعتمدة على الأصناف و قد تكون أكثر مرونة فى بعض الجوانب ، هذا النوع من الوراثة يسمى الوراثة المعتمدة على التفويض Delegation based inheritance أو كما تسميها ECMAScript بالوراثة المعتمدة على ال prototype .

لنرجع لمثال الاصناف A و B و C ، فى ال ECMAScript يمكنك إنشاء الكائنات a و  b  و  c بحيث ان الكائن a يحتوى على الصفات المشتركة بين b  و  c  ، أما الكائنين a  و  b فيحتويان على الخصائص التى تميزهما :

 

var a = {
  x: 10,
  calculate: function (z) {
    return this.x + this.y + z
  }
};

var b = {
  y: 20,
  __proto__: a
};

var c = {
y: 30,
__proto__: a
};

// إستدعى الصفات الموروثة
b.calculate(30); // 60
c.calculate(40); // 80

سهل بما يكفى ، أليس كذلك ؟ كما نرى الكائنات c و b لديهم القدرة على الوصول إلى الوظيفة calculate التى تم تعريفها فى الكائن a ، قد تم تحقيق هذا عن طريق سلسلة ال prototype حيث ان كائن ال prototype لكل من الكائنين b و c هو a عن طريق الخاصية __proto__ كما يوضح الكود السابقة ، قاعدة الوصول إلى الخصائص و الوظائف من سلسلة ال prototype بسيطة : لو ان الكائن لا يحتوى على الوظيفة او الخاصية التى يتم البحث عنها فإنه سيتم البحث هذه الخاصية او الوظيفة فى سلسلة ال prototype ، إن لم تكن موجودة سيتم البحث فى protoype ال prototype و هكذا إلى أن يتم العثور على خاصية او وظيفة بنفس الاسم الذى نبحث عنه فيتم إرجاعه للعمل به ، و لذلك هذه الخاصية التى تم ايجادها فى سلسلة ال  prototype إسمها خاصية موروثة أما الخصائص التى تم تعريفها للكائن مباشرة خصائص مملوكة own properties ، لو لم يتم العثور على شىء بعد فحص سلسلة ال prototype سيتم إرجاع القيمة undefined .

لاحظ أن القيمة this عند إستخدامها فى الوظيفة الموروثة calculate فإنها تشير إلى الكائن الأصلى ولا تشير إلى كائن ال prototype الذى يحتوى على هذه الوظيفة أصلا ، فى المثال السابق “this.y” تأخذ من b و c ، مع ذلك فإن “this.y” تأخذ من a عن طريق آلية عمل سلسلة ال prototype .

لو أن ال prototype لكائن ما لم يتم تعريفه صراحاً فإن الخاصية __proto__ ستأخذ قيمتها الافتراضية Object.prototype و هذا سبب ان كل الكائنات ترث صفاتها و وظائفها الأساسية من الكائن Object ، لاحظ ان ال “Object.prototype” الذى هو اخر حلقة فى سلسلة ال prototype له الخاصية __proto__ تشير إلى null.

الصورة التالية توضح العلاقة الوراثية بين كل من الكائنات a و b و c :

 

A prototype chain

ال Constructor

ال constructor يقدم وظيفة مفيدة عند إنشاء الكائنات حيث يقوم بضبط كائن prototype أوتوماتيكيا للكائنات الجديدة التى يتم إنشاءها ، كائن ال prototype يتم تخزينه فى الخاصية ConstructorFunction.prototype .

يمكننا إعادة المثال السابق مع إنشاء الكائنات a و b و c بإستخدام constructor function ، فى المثال التالى الكائن a يلعب دور Foo.prototype حيث أن Foo هى ال constructor function الذى سيتم منه إنشاء الكائنات b و c كما يلى :

// a constructor function
function Foo(y) {
  // تقوم بإنشاء كائنات جديدة
  // y الكائن الجديد سيحتوى على الخاصية المملوكة
  this.y = y;
}

// أيضا الكائن Foo يحتوى على مرجع إلى
// كائن ال prototype للكائنات الجديدة
// لذلك ربما نستخدمه لتعريف الصفات و الوظائف المشتركة أو الموروثة
// كما فى المثال السابقة لدينا الخاصية
// الموروثة x
Foo.prototype.x = 10;

// و الوظيفة الموروثة calcualate
Foo.prototype.calculate = function (z) {
  this.x + this.y + z;
};

// الان سننشأ الكائنات b و c
// بإستخدام Foo
var b = new Foo(20);
var c = new Foo(30);

// يمكننا إستدعاء الخواص الموروثة
b.calculate(30); // 60
c.calculate(40); // 80

// يمكننا إختبار العلاقة بين الكائنات كما يلى

console.log(

  b.__proto__ === Foo.prototype, // true
  c.__proto__ === Foo.prototype, // true

  // أيضا Foo.prototype ينشأ أتوماتيكيا
// خاصية خاصة constructor التى تشير إلى ال
// وظيفة ال constructor نفسها
// يمكنن للكائنات b و c إختبار ال constructor لهما عن طريق

  b.constructor === Foo, // true
  c.constructor === Foo, // true
  Foo.prototype.constructor === Foo // true

  b.calculate === b.__proto__.calculate, // true
  b.__proto__.calculate === Foo.prototype.calculate // true

);

يمكن تمثيل الكود السابق بهذ الصورة

 

العلاقة بين ال constructor و الكائنات التى تم إنشائها منه

يمكننا مرة أخرى من الصورة أن نرى ان كل كائن له كائن prototype حتى أن وظيفة ال constructor Foo لها prototype و هو Foo.prototype الذى هو بدوره له prototype و هو Object.prototype يشير إليه بالخاصية __proto__ ، مرة أخرى Foo.prototype هو خاصية صريحة ل Foo و تلعب دور ال prototype للكائنات b و c .

الشرح الكامل و المفصل لهذا الموضوع يمكنك إيجاده فى الفصل السابع من سلسلة ES3 الذى ينقسم لجزئين Chapter 7.1. OOP. The general theory يحتوى على وصف و مقارنة البرمجة الموجهة بالكائنات فى ECMAScript بالأنظمة و الأساليب الأخرى ، أما Chapter 7.2. OOP. ECMAScript implementation فكتب خصيصا لشرح البرمجة الموجة بالكائنات فى ECMAScript .

الان بعدما عرفنا الجوانب الأساسية المتعلقة بالكائنات ، هيا بنا نرى كيف يتم تنفيذ برنامج ECMAScript  فى ال runtime . هذا ما يسمى بال execution context stack كومة سياق التنفيذ ،  كل عنصر فيها يمكن تمثيله تدريجيا عن طريق كائن ههههههه نعم تقريبا ال  ECMAScript تعمل فى كل مكان بمفهوم الكائن .

كومة سياق التنفيذ Execution context stack

هناك 3 أنواع من الكود فى ECMAScript : كود ال golobal و كود ال function و كود ال eval ، كل نوع ينفذ فى سياق تنفيذ execution context خاص به ، هناك سياق تنفيذ واحد فقط خاص بالكود الشامل global code ، لكن قد يكون هناك سياقات تنفيذ عديدة لأكواد ال function و اكواد ال eval ، كل إستدعاء لوظيفة يقوم بإنشاء سياق تنفيذ خاص بها و تنفيذ الكود بداخلها فى هذا السياق ، كل إستدعاء لل eval يقوم بإنشاء سياق تنفيذ لل eval و تنفيذ الكود الخاص بها فى هذا السياق .

لاحظ انه يمكن لوظيفة واحدة ان تولد عدد لانهائى من سياقات التنفيذ لأنه يتم إنشاء سياق تنفيذ فى كل مرة تنادى فيها نفسها recursively ،

function foo(bar) {}

// إستدعاء نفس الوظيفة
// يولد 3 سياقات تنفيذ مختلفة
// فى كل إستدعاء مع
// حال سياق مختلف نتيجة تغير قمة
// العبارة التى تمرر لها

foo(10);
foo(20);
foo(30);

قد يقوم سياق تنفيذ بتنشيط سياق تنفيذ اخر مثال : عندما تطلب وظيفة وظيفة اخرى ، او عندما تطلب وظيفة من السياق الشامل و هكذا ، منطقيا يتم تنفيذ هذا عن طريق Stack كومة تسمى كومة سياق التنفيذ Execution context stack .

السياق الذى يقوم بتنشيط سياق اخر يسمى ال caller اما السياق الذى يتم تنشيطه فيسمى callee ، قد يكون ال callee بمثابة caller لسياق اخر ، مثلا الكود الشامل يطلب وظيفة – هنا الشامل هو ال caller و الوظيفة هى ال callee – إذا قامت الوظيفة بإستدعاء وظيفة اخرى فإنها ستكون caller للوظيفة التى طلبتها التى ستصبح callee .

عندما يقوم caller بتنشيط callee ، فإن ال Caller يعلق التنفيذ و ينقل التحكم إلى ال callee ، يتم إضافة ال callee إلى كومة سياق التنفيذ و يصبح سياق التنفيذ النشط ، بعد الانتهاء من تنفيذ ال callee يرجع التحكم إلى ال caller مرة اخرى لتنفيذه و يتم محو ال calle من كومة سياق التنفيذ ، قد يقوم ال caller بتنشيط سياق اخر و تتكرر الدورة إلى ان يتم التنفيذ ، قد ينتهى ال caller طبيعيا او ينتهى بإستثناء exeption ، الإستثناء الذى لا يتم إلتقاطه للتعامل معه قد يؤدى إلى الخروج من اكثر من سياق تنفيذ و قد ينتهى المطاف إلى السياق الشامل .

كل برامج ال ECMAScript فى ال runtime يتم تمثيلها ككومة من سياقات التنفيذ أعلاها هو سياق التنفيذ النشط الذى يتم تنفيذه حاليا .

 

كومة سياق التنفيذ

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

الصورة التالية توضح لدينا سياق تنفيذ وظيفة “EC1″ و سياق تنفيذ الكود الشامل يسمى “Global EC1″ ستحدث هذه التعديلات على كومة سياق التنفيذ عند الدخول و الخروج من “EC1″ إلى الكود الشامل :

 

التعديلات التى تحدث لكومة سياق التنفيذ

هذا بالظبط ما يقوم به ال runtime system للتحكم فة تنفيذ الكود ال ECMAScript .

يمكنك إيجاد مزيد من المعلومات عن سياقات تنفيذ ECMAScript فى الفصل الاول Execution context .

كما ذكرت ان كل سياق تنفيذ فى الكومة يمكن تمثيله فى صورة كائن ، هيا نلقى نظرة على تركيب هذا الكائن و الخصائص اللازمة للسياق لتنفيذ كوده

سياق التنفيذ Execution Context

سياق التنفيذ يمكن تمثيله تجريديا فى صورة كائن بسيط ، كل سياق له مجموعة من الخصائص –  يمكن إطلاق عليها حالة السياق – لازمة لمتابعة التقدم فى تنفيذ كود هذا السياق ، الصورة التالية توضح تركيب الكائن الذى يمثل السياق :

 

تركيب سياق التنفيذ

بجانب هذه الوظائف الثلاثة (كائن المتغير ، القيمة this ، و سلسلة النطاق) ، قد يحتوى سياق التنفيذ على خصائص اخرى تعتمد على ال implementation لمحرك الجافاسكربت ، هيا بنا نلقى نظرة على خصائص سياق التنفيذ بالتفاصيل.

كائن المتغير Variable Object

كائن المتغير هو نطاق بيانات ذات صلة بسياق التنفيذ ، هو كائن خاص مرتبط بالسياق و يحتوى على المتغيرات و اعلانات الوظائف function declarations التى تم تعريفها فى هذا السياق .

عليك ملاحظة أن تعبير الوظيفة function expression | FE – بعكس إعلان الوظيفة function declaration | FD – لا يتم تخزينه فى كائن المتغير .

كائن المتغير هو مفهوم تجريدى ، فى مختلف أنواع السياقات يتم تمثيله بكائنات مختلفة ، على سبيل المثال فى السياق الشامل global context فإن كائن المتغير هو الكائن الشامل نفسه global object  :| لذلك يمكننا الإشارة إلى المتغيرات الشاملة عن طريق أسمائها كخصائص للكائن الشامل – الكائن الشامل فى المتصفح هو window .

هيا بنا نلقى نظرة على هذا المثال فى سياق التنفيذ الشامل global execution context :

var foo = 10;

function bar() {} // function declaration, FD
(function baz() {}); // function expression, FE

console.log(
  this.foo == foo, // true
  window.bar == bar // true
);

console.log(baz); // ReferenceError, "baz" is not defined

كائن المتغير للسياق الشامل سيحتوى على الخصائص التالية :

كائن المتغير الشامل

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

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

إستخدام الوظيفة eval يدخل أيضا فى سياق تنفيذ جديد كما تعلمنا من قبل ، لكن ال eval تستخدم كائن متغير السياق الشامل او كائن متغير ال caller – الوظيفة التى تم إستدعاء eval بداخلها .

ماذا عن كائن المتغير الخاص بالوظائف ؟ فى سياق الوظيفة يتم تمثيل كائن المتغير بكائن يسمى كائن التنشيط .

كائن التنشيط Activation Object

عندما يتم تنشيط وظيفة – اى عندما يتم إستدعائها – بواسطة ال caller  – الوظيفة التى إستدعتها – فإنه يتم إنشاء كائن خاص بإسم كائن التنشيط activation object ، يتم ملأه بالعبارات التى أستدعت الوظيفة بها و كائن العبارات arguments object ، يتم إستخدام كائن التنشيط هذا ككائن المتغير فى سياق الوظيفة .

انظر للمثال التالى :

function foo(x, y) {
    var z = 30;
    function bar() {} // FD
    (function baz() {}); // FE
}
foo(10, 20);

سنحصل على كائن التنشيط التالى لسياق الوظيفة foo :

 

كائن تنشيط الوظيفة foo

لاحظ مرة أخرى :) أن الوظيفة baz كونها تعبير وظيفة لايتم تضمينها فى كائن المتغير/التنشيط للوظيفة foo لذلك إذا حاولت الوصول إليها ستحصل على ReferenceError لأنها غير موجودة فى كائن المتغير .

يمكنك إيجاد شرح وافى لكائن المتغير و كائن التنشيط فى الفصل الثانى Chapter 2: Variable object .

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

سلسلة النطاق Scope chain

سلسلة النطاق هى سلسلة من الكائنات يتم البحث بداخلها عن المعرفات identifiers التى تظهر فى كود سياق التنفيذ الحالى .

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

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

كقاعدة عامة : سلسلة النطاق تحتوى على سلسلة من كائنات المتغير فى مقدمتها كائن المتغير/التنشيط للوظيفة الحالية ، ربما تحتوى سلسلة النطاق على كائنات اخرى مثل الكائنات التى تتم إضافتها ديناميكيا لسلسلة النطاق خلال تنفيذ السياق مثل كائنات-with او كائنات-catch .

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

أنظر لهذا المثال :

var x = 10;

(function foo() {
    var y = 20;
    (function bar() {
      var z = 30;
      // x و y متغيرات حرة
      // غير موجودة كائن تنشيط الوظيفة bar
      // لذلك يتم البحث عنها فى سلسلة النطاق
      // للوظيفة bar
      console.log(x + y + z); //10 + 20 + 30
    })();
})();

سنفترض أن الربط بين الكائنات فى سلسلة النطاق يتم عن طريق الخاصية __parent__ لهذه الكائنات التى تشير للكائن التالى فى السلسلة ، بالمناسبة هذه الخاصية موجودة فى محرك الجافاسكربت Rhino ، هذا التكنيك بعينه يستخدم فى  ECMASript5 فى ال lexical environments يسمى هناك بالرابط الخارجي outer links ، بإستخدام مفهوم ال __parent__ يمكننا تمثيل الكود السابق بالصورة التالية ، الخاصية __parent__ تشير إلى الخاصية [[scope]] للوظيفة ، بالطبع لايمكن الوصول للخاصية [[scope]] من كود الجافاسكربت ، يستخدمها المحرك وحده :

 

سلسلة النطاق

أثناء تنيفيذ الكود قد يتم الزيادة على سلسلة النطاق بكائنات من إستخدام try-statement و catch-clause ، و بما ان هذه الكائنات ماهى إلا كائنات بسيطة ، قد يحتوا على prototype و سلسلة prototype ، و هذا يؤدى إلى تغير فى البحث داخل سلسلة النطاق ، أولا يبدأ البحث داخل سلسلة النطاق لكن أثناء البحث داخل كل كائن متغير فى السلسلة يتم البحث أيضا فى سلسلة ال prototype لهذ الكائن – طبعا إذا وجد كائن protoype أنظر هذا المثال :

    Object.prototype.x = 10;

    var w = 20;
    var y = 30;

    // فى المحرك SpiderMonkey الكائن الشامل
    // يرث من كائن المتغير للسياق الشامل
// "Object.prototype" لذلك يمكننا الإشارة,
    // إلى المعرف x الذى لم يتم تعريفه
    // فى السياق الشامل لكنه موجود فى
    // سلسلة ال prototype

    console.log(x); // 10

    (function foo() {

        // المتغيرات المحلية للوظيفة foo
        var w = 40;
        var x = 100;

        // x موجود فى Object.protoype
        // لأن الكائن {z: 50} يرث منه

        with ({z: 50}) {
            console.log(w, x, y , z); // 40, 10, 30, 50
        }

        // بعد إزالة الكائن with من سلسلة النطاق
        // فإن المتغير x سيتم إيجاده فى كائن التنشيط
        // و المتغير w هو ايضا محلى
        console.log(x, w); // 100, 40

        // و بهذه الطريقة يمكننا الإشارة إلى المتغير الشامل
        // w الذى تم تغتطيته بالمتغير المحلى بنفس الإسمى
        // ، عن طريق إستخدام الكائن الشامل الذى هو
        // الكائن window فى المتصفح
        console.log(window.w); // 20

})();

سنحصل على التركيب التالى ، عليك ملاحظة انه قبل البحث فى ال __parent__ ، يتم أولا فحص __proto__ الكائن {z: 50 } كما توضح الصورة :

 

سلسلة النطاق تم الزيادة عليها عند إستخدام with

لاحظ انه ليس فى كل محركات الجافاسكربت يرث الكائن الشامل من Object.prototype ، ماتم شرحه فى الصورة السابقة يمكن تجربته فى محرك SpiderMonkey خاصة المتغير x الذى لم يتم تعريفه فى السياق الشامل لكنه موجود Object.prototype .

الإغلاق Closure

الوظائف فى ECMAScript درجة أولى first class بمعنى انه يمكن تمريرها للوظائف الاخرى و فى هذه الحالة تسمى funargs إختصار functional arguments ، الوظيفة التى تستقبل ال funargs تسمى وظيفة ذات ترتيب عالى Higher order function أو تسمى معاملات مثل ال + فى علم الرياضيات ،  أيضا يمكن لوظيفة ان ترجع وظيفة أخرى فى الجافاسكربت ، الوظائف التى ترجع وظائف فى الجافاسكربت تسمى وظائف ذات قيمة وظيفية function with functional values .

هناك مشكلتين بخصوص ال funargs و القيم الوظيفية ، و هاتين المشكلتين يتم تعميمهم بمشكلة واحدة تسمى “funargs problem”  أو مشكلة العبارت الوظيفية ، و لكى تحل هذه المشكلة تم اختراع مفهوم الإغلاق Closure ، هيا بنا نصف هاتين المشكلتين أكثر تفصيلا ، سنجد ان الحل فى ECMAScript عن طريق إستخدام الخاصية [[scope]] للوظائف كما توضح الصور السابقة .

أول مشكلة فى “funarg problem” هى “upward funarg problem” ، تظهر هذه المشكلة عندما يتم إرجاع وظيفة من وظيفة أخرى قد أستخدمت المتغيرات الحرة بداخل الوظيفة الأم، لكى تتمكن من الوصول إلى المتغيرات فى سياق الوظيفة الأم بعد انتهاء تنفيذ هذا السياق و الخروج من الوظيفة ، الوظيفة الداخلية لحظة إنشائها تقوم بحفظ سلسلة المطاق للوظيفة الأم داخل ال [[scope]] الخاص بها ، عندما تقوم الوظيفة الأم بإرجاع الوظيفة الداخلية ، لاتزال الوظيفة الداخلية محتفظة بسلسلة نطاق الوظيفة الأم و عليه عندما يتم تنشيطها -الوظيفة التى رجعت- فإن سلسلة نطاقها ستكون عبارة عن كائن التنشيط + الخاصية [[scope]]  .

Scope Chain = Activation object + [[scope]]

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

function foo() {
    var x = 10;
    return function bar() {
        console.log(x);
    };
}

// الوظيفة فوو ترجع وظيفة ، هذه الوظيفة
 // تستخدم المتغير الحر إكس
var returnedFunction = foo();

// global variable "x"
var x = 20;

// تنفيذ الوظيفة التى رجعت من فوو
returnedFunction(); // 10, but not 20

هذا النوع من النطاق يسمى النطاق الساكن ، يمكنك رؤية ان المتغير x محفوظ فى الخاصية [[scope]] للوظيفة bar التى رجعت من الوظيفة foo ، يوجد نوع اخر من النطاق : النطاق الديناميكى ، إذا كانت الجافاسكربت لغة ذات نطاق ديناميكى فإن المثال السابق سينتج عنه 20 بدلا من 10 .

النوع الثانى من مشكلة “funarg problem” هى مشكلة ال “downward funarg problem” ، فى هذه الحالة قد يكون هناك سياق ام ، المشكلة تكمن فى محاولة ايجاد معنى المتغيرات : من النطاق الساكن اثناء انشاء الوظيفة أم من النطاق الديناميكى اثناء تنشيط و تنفيذها ، لحل هذه المشكلة و لتكوين إغلاق فإن النطاق الساكن هو الذى يستخدم ، إنظر لهذا المثال حتى تتضح الامور أكثر :

// المتغير الشامل إكس
var x = 10;

// وظيفة شاملة فوو
function foo() {
  console.log(x);
}

(function (funArg) {

  // المتغير المحلى إكس
  var x = 20;
  // لا يوجد حيرة هنا حيث سيتم إستخدام
  // المتغير الشامل إكس الذى تم حفظه فى
 // الخاصية [[scope]] للوظيفة أثناء إنشائها
  // ولا يتم إستخدام الإكس الموجودة فى نطاق ال
  // caller الوظيفة التى تم نداء فووو منها

    funArg(); // 10, but not 20

})(foo); // مرر الوظيفة فوو ك funarg

يمكننا ملاحظة ان النطاق الساكن static scope ضرورة للإغلاق فى اللغات التى تدغم الإغلاق . لكن بالرغم من ذلك هناك بعض اللغات تدعم النطاق الساكن و الديناميكى و تترك المبرمج يختار متى يغلق و متى لا ، و بما ان ال ECMAScript لا تدعم غير النطاق الساكن فإن ال ECMAScript تدعم الإغلاق بصورة كاملة عن طريق إستخدام الخاصية [[scope]] للوظائف ، ربما نعرف الان التعرف الأكثر صحة للإغلاق :

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

لاحظ انه بما ان كل الوظائف فى جافاسكربت تقوم بحفظ ال [[scope]]  أثناء إنشائها ، فنظريا كل الوظائف فى ال ECMAScript عبارة عن اغلاقات closures .

شىء اخر مهم عليك ملاحظته ان وظائف عدة قد تملك نفس النطاق الأم ، أبسط مثال هو وجود اكثر من وظيفة فى السياق الشامل ، او وظيفتين داخليتين داخل وظيفة أم ، و فى هذه الحالة فأن المتغيرات المحفوظة داخل الخاصية [[scope]]  يتم مشاركتها بين تلك الوظائف التى تملك نفس سلسلة النطاق الام parent scope chain ، لذلك التغير فى أغلاق واحد سيؤثر على باقى الإغلاقات الاخرى ، المثال التالى سيوضح الموضوع :

function baz() {
  var x = 1;
  return {
    foo: function foo() { return ++x; },
    bar: function bar() { return --x; }
  };
}

var closures = baz();

console.log(
  closures.foo(), // 2
  closures.bar()  // 1
);

يمكن شرح الكود السابق بهذه الصورة :

 

ماذا عن إنشاء أكثر من وظيفة داخل for loop  ؟ هنا مشكلة اخرى تواجه المبرمجين المبتدئين ، إستخدام المتغير الذى يعمل كعداد فى ال for loop مثل i داخل الوظائف التى تنشأها داخل ال loop ، قد تحصل على نتيجة غير متوقعة عندما تجد ان كل الوظائف التى أنشأتها تستخدم اخر قيمة لهذا المتغير  i ، يحدث هذا لأن الوظائف كلها لها نفسى ال [[scope]] الذى يحتوى على قيمة واحدة ل i عند انتهاء حلقة التكرار ، انظر للمثال التالى حتى تتضح الامور أكثر

var data = [];

for (var i = 0; i < 3; k++) {
    data[i] = function () {
        alert(i);
    };
}

data[0](); // 3, but not 0
data[1](); // 3, but not 1
data[2](); // 3, but not 2

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

var data = [];

for (var k = 0; k < 3; k++) {
    data[k] = (function (x) {
        return function () {
            alert(x);
        };
    })(k); // pass "k" value
}

// now it is correct
data[0](); // 0
data[1](); // 1
data[2](); // 2

إذا كنت مهتم بنظرية الإغلاقات و تطبيقاتها العملية يمكنك الحصول على معلومات إضافية فى الفصل السادس Chapter 6. Closure ، او الحصول على المزيد من المعلومات عن سلسلة النطاق فى الفصل الرابع Chapter 4. Scope Chain.

سننتقل للحديث عن خاصية من خواص سياق التنفيذ ألا و هى القيمة this .

القيمة this

القيمة this عبارة عن كائن خاص مرتبط بسياق التنفيذ الحالى ، لذلك يمكن تسميته كائن السياق context object .

أى كائن يمكن إستخدامه لإستبدال القيمة this للسياق الحالى ، أود أن أوضح اختلاط المفاهيم الذي يظهر بعض الأحيان عند التحديث عن سياقات التنفيذ فى ال ECMAScript و عند التطرأ للحديث عن القيمة this ، غالبا ما يتم وصف القيمة this بالخطأ على أنها خاصية لكائن المتغير ، يمكن إيجاد هذا الخطأ فى كتاب High performance JavaScript بالرغم من أن الفصل المذكور فى الكتب جيد ، تذكر مرة أخرى

القيمة this خاصية من خصائص سياق التنفيذ و ليست خاصية من خواص كائن المتغير

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

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

فى السياق الشامل القيمة this تشير إلى السياق الشامل نفسه ، هذا يعنى أن القيمة this تساوى كائن المتغير :

var x = 10;

console.log(
    x, // 10
    this.x, // 10
    window.x // 10
);

فى حالة سياق الوظيفة ، قد تتغير قيمة this فى كل إستدعاء للوظيفة  ، هنا قيمة this تقدم من ال caller عن طريقة الاستدعاء – الطريقة التى تم تنشيط الوظيفة بها،  على سبيل الوظيفة foo بالأسفل تم إستدعائها من السياق الشامل الذى هو بمثابة ال caller ، هيا بنا نرى كيف تتغير قيمة this مع كل أستدعاء لها بطريقة مختلفة مع العلم ان الكود لا يتغير

// كود الوظيفة فوو لا يتغير على الاطلاق
// لكن قيمة this تتغير مع كل تنشيط للوظيفة

function foo() {
    alert(this);
}

// ال caller ينشط فوو
// و يقدم قيمة this لها التى ستكون الكائن الشامل

foo(); // global object
foo.prototype.constructor(); // foo.prototype

var bar = {
    baz: foo
};

bar.baz(); // bar

(bar.baz)(); // also bar
(bar.baz = bar.baz)(); // but here is global object
(bar.baz, bar.baz)(); // also global object
(false || bar.baz)(); // also global object

var otherFoo = bar.baz;
otherFoo(); // again global object

لكى تفهم أكثر عمقا كيف تتغير قيمة this مع كل تنشيط للوظيفة عليك بقراءة الفصل الثالث  Chapter 3. This حيث تناقش جميع الحالات التى تم ذكرها بالأعلى بالتفصيل الممل .

ختام

هنا انتهينا من هذه النظرة العامة على ال ECMAScript و لكن اتضح انها ليست نظرة عامل ;) بالرغم من ذلك الشرح الوافى قد يحتاج لكتاب كامل ، لم نتطرق لموضوعين رئيسين و هما : الوظائف ( الفرق بين نوعين الوظائف – الوظائف المعلنة function declarations و تعبير الوظائف  function expressions  ) و إستراتيجية التقييم evaluation strategy المستخدمة فى ECMAScript ، يمكنك أيجاد الموضوعين فى الفصلين الخاصين بهما Chapter 5. Function و  Chapter 8. Evaluation strategy .

حظ موفق فى دراسة ال  ECMAScript .

كتبها : ديمترى تشينكوف
نشرت فى : ٢-٩-٢٠١٠

محاضرة Changes to ECMAScript الجزء الثانى

09/05/2010

السلام عليكم و رحمة الله تعالى و بركاته

أعطىء Tom Van Cutsem محاضرة فى جوجل بعنوان Changes to ECMAScript ناقش فيها التغيرت التى طرأت على الجافاسكربت بعد تبنى مواصفات ECMASCript 5 و إستفاض فى نقاش ال Proxies و ال traits ، يمكنك مشاهدة المحاضرة على اليوتيوب من هنا ، هذه المحاضرة الجزء الثانى لمحاضرة upcoming changes to ECMAScript التى دونت عنها من قبل .

ترجمة ECMA-262-3 الفصل الأول : سياقات التنفيذ

21/04/2010

هذه المقالة ترجمة ECMA-262-3 in detail Chapter1. Executions Contexts للكاتب Dmitry A. Soshnikov

1- مقدمة
2- تعريفات
3- أنواع الكود القابل للتنفيذ
كود ال global
كود الوظائف
كود الوظيفة eval
4- إستنتاج
5- المزيد

مقدمة

فى هذا المقال سأقوم بذكر أنواع سياقات التنفيذ execution contexts ب ECMAScript و أنواع الكود القابل للتنفيذ المرتبط بهم .

تعريفات

فى كل مرة ينتقل فيها التحكم إلى كود قابل للتنفيذ فى ECMAScript فإن الكود يدخل فى سياق للتنفيذ execution context. سياق التنفيذ execution context إختصارا EC هو مفهوم تجريدى تستخدمه مواصفات ال ECMAScript لتصنيف وتمييز الأكواد القابلة للتنفيذ.

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

أنواع الكود القابل للتنفيذ

مع هذا المفهود التجريدى لسياق التنفيذ ، نوع الكود القابل للتنفيذ executable code مرتبط به . عند الحديث عن نوع الكود فإنه من الممكن فى بعض الأوقات ان نقصد به سياق التنفيذ.

فى الأمثلة سنعرف stack سياقات التنفيذ كمصفوفة :

ECStack = [];

يتم الإضافة إلى هذا ال stack فى كل مرة عند دخول وظيفة حتى إذا إستدعت هذه الوظيفة نفسها recursively أو كانت constructor ، و إيضا عند تنفيذ كود من خلال الوظيفة eval .

كود ال global

هذا النوع من الكود الذى يتم تنفيذه فى مستوى البرنامج مباشرة داخل ال script tag او الذى يتم تحميلة من ملف خارجى خارج أى وظيفة ، عندما يتم بدأ تطبيق البرنامج فإن ال ECStack يبدو هكذا :

ECStack = [
    globalContext
];

كود ال Function

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

(function foo(bar) {
if (bar) {
return;
}
foo(true);
})();

التعديلات التى تطرأ على ECStack :

// عند دخول foo لأول مرة لتنفيذ الكود
ECStack = [
  <foo> functionContext
  globalContext
];

// عندما نادت foo نفسها
ECStack = [
  <foo> functionContext – recursively
  <foo> functionContext
  globalContext
];

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

كود الوظيفة eval

الأمور أكثر إثارة عندما نتحدث عن كود ال eval ، ينشأ هنا مفهوم سياق الإستدعاء calling context السياق الذى يتم منه إستدعاء الوظيفة eval ، حيث ان التأثير الذى تحدثه الوظيفة eval مثل انشاء المتغيرات و الدوال يؤثر على سياق الإستدعاء :

eval('var x = 10');

(function foo() {
  eval('var y = 20');
})();

alert(x); // 10
alert(y); // "y" is not defined

التعديلات التى تطرأ على ECStack :


ECStack = [
  globalContext
];

// eval('var x = 10');
ECStack.push(
  evalContext,
  callingContext: globalContext
);

// eval خرجت من السياق
ECStack.pop();

// عند إستدعاء الوظيفة foo
ECStack.push(<foo> functionContext);

// eval('var y = 20');
ECStack.push(
  evalContext,
  callingContext: <foo> functionContext
);

// الإنتهاء من تنفيذ eval
ECStack.pop();

// الإنتهاء من تنفيذ foo
ECStack.pop();

فى SpiderMonkey محرك ترجمة الجافاسكربت الموجود فى فايرفوكس و سندربرد لغاية النسخة 1.7 من الممكن تمرير عبارة تانية للوظيفة eval لكى تمثل سياق الإستدعاء calling context ، ومن الممكن عن طرق ذلك التأثير على المتغيرات الخاصة كما يسميها البعض :

function foo() {
  var x = 1;
  return function () { alert(x); };
};

var bar = foo();

bar(); // 1

// تمرير سياق الإستدعاء للتأثير على المتغير الداخلى إكس
eval('x = 2', bar);

bar(); // 2

إستنتاج

هذه المعالجة النظرية مطلوبة لمزيد من التحليل لتفاصيل المواضيع الاخرى المرتبطة بسياقات التنفيذ مثل كائن المتغير Variable object و سلسلة المدى scope chain … الخ .

للمزيد

القسم المقابل فى مواصفات ECMA-262-3  Execution Contexts

شرح ECMA-262-3 بالتفصيل

13/04/2010

السلام عليكم و رحمة الله تعالى و بركاته

إنتهى Dmitry A. Soshnikov من شرح ECMAScript 262 3rd edition بلغة انجليزية واضحة لمبرمجين الجافاسكربت و هى مواصفات اللغة المبنية عليها جافاسكربت التى تعمل فى جميع المتصفحات و محركات الترجمة حاليا ، من سنحت له الفرصة و قرأ ECMA-262-3 سيجدها معقدة جدا لأنها موجهة لمصممين لغات البرمجة حتى ينتجوا مترجم يفهم لغة جافاسكربت ، أما السلسلة التى شرحها ديمترى فهى بلغة انجليزية مباشرة و سهلة الفهم للمبرمجين العادين الذين يريدون فهم أعمق لكيفية عمل الجافاسكربت خاصة الاجزاء الغامضة منها مثل ال Closure و ال Execution context و This … الخ ، السلسلة تم شرحها فى 9 فصول كالتالى بالترتيب :

Execution Contexts
Variable object
This
Scope chain
Functions
Closures
OOP: The general
OOP: ECMAScript
Evaluation strategy

و قد وعد أيضا ديميترى بشرح ECMAScript 5th edition قريبا .
أنصح بشدة قراءة هذه السلسلة لمن يريد فهم الجافاسكربت بعمق أكثر و من يريد أن ينتقل من المستوى المتوسط إلى مستوى الإحتراف.

محاضره upcoming changes to JavaScript

22/06/2009

السلام عليكم و رحمه الله تعالى و بركاته

ecmascript 5 changes upcoming to javascript

اعطى 3 من مهندسى جوجل محاضره بعنوان EcmaScript5 , Upcoming changes to JavaScript ، تناولت المحاضره التغيرات التى ستطرأ على اللغه فى خلال سنه ان شاء الله بعدما اتفق مصنعوا المتصفحات ادراج مواصفات EmaScript5 الجديده ، يمكنك تحميل المحاضره من هنا ، و يمكنك تحميل الشرائح من هنا .

ما هى قصه EcmaScript 5 ؟

بعدما تيقن مصنعوا انه يستحيل ادراج مواصفات EcmaScript 4 فى جافاسكربت حاليا ، لأنها تقلب اللغه رأسا على عقب مقارنه بالوضع الحالى كما انها ضخمه جداااا و ليست backward compatible ، قاموا بإنشاء مجموعه جديده لتحديد مواصفات EcmaScript 3.1 تحتاجها فعلا اللغه بوضعها الحالى بحيث تكون backward compatible و يسهل ادراجها فى وقت قصير بعدما انتهوا من المواصفات و بعد وقت كبير من المناقشات انتهوا إلى مواصفات اطلقوا عليها EcmaScript 5 ، اتفق صناع المتصفحات على ادراجها و هى حاليا فى مرحله التنقيح ، كتب john resig مطور مكتبه jQuery تدوينتين يناقش بهم بعض مزايا EcmaScript 5 الجديده هنا و هنا . او يمكنك قراءه المواصفات كامله من هنا .

خارج النص : يمكنك الان متابعه ما اقوم به يوميا من خلال حسابى على twitter .

E4X

09/05/2009

السلام عليكم و رحمه الله تعالى و بركاته

قد ذكرت فى موضوع المصفوفه فى جافاسكربت 1.6 انه تم اضافه مواصفات E4X للجافاسكربت ، ال E4x -اختصار ECMA for XML- هو امتداد للغه جافاسكربت يمكننها من برمجه ال XML بطريقه ابسط بكثيرررر من ال DOM ، متاحه حاليا -وقت كتابه التدوينه- فى فايرفوكس فقط ، تم معايره مواصفات E4X بناءا على مواصفات ECMA-357 standard .

قدمت E4x كائن جديد من نوع XML يمكنك من كتابه  xml literals داخل كود جافاسكربت و برمجتها بمنتهى السهوله ، عليك اولا احاطه كود جافاسكربت بتاج <script> لكن مع تغير الخاصيه type إلى text/javascript;e4x=1 كما يلى :

<script type="text/javascript;e4x=1">
    /* JavaScript code here */
</script>

إنشاء كائنات XML

يمكنك انشاء كائنات XML عن طريق E4X بطريقتين ، الطريقه الاولى عن طريق ال XML constructor كما يوضح الكود البسيط التالى :

var languages= new XML('<languages type="dynamic">\
<lang>JavaScript</lang><lang>Python</lang></languages>');

ملحوظه : ال \ التى فى اخر السطر الاول لأنى قسمت النص إلى سطرين حتى يستوعبه عرض المدونه و لا يخرج عن الاطار .
و الطريقه التانيه فى انشاء كائن XML هى استخدام XML literal كما يوضح الكود البسيط التالى :

var languages = <languages type="dynamic">
<lang>JavaScript</lang>
<lang>Python</lang>
</languages>;

فى كلتا الحالتين ينتج كائن من نوع XML كما يلى :

alert(typeof languages); //XML

إذا كان ال XML المكتوب غير صحيح -كأن تنسى غلق تاج- فسينتج عنه error .

التعامل مع المتغيرات

ال XML literal يتميز عن ال XML constructor فى انه يمكنك وضع متيغرات الجافاسكربت داخل كائن ال XML اثناء انشاءه من خلال وضع اسم المتغير داخل {  } كما يوضح الكود البسيط التالى :

var h = "html";
var text = "some text ya nas";
var doc = <{h}><body>{text}</body></{h}>;
alert(doc.toString());
// <html><body>here's some text ya nas</body></html>

يمكنك عمل attributes ديناميكا بنفس الطريقه كما يوضح الكود التالى :

var a = 2;
var b = <foo bar={a} ></foo>; //<foo bar="2" ></foo>

يتم استبدال علامات التنصيص “و’ و علامه الاصغر من < و علامه & بال entity المقابل لهم كما يوضح الكود التالى :

var b = 'he said " don\'t leave " ';
var el = <foo a={b} />;
alert(el.toString());
// <foo a="he said &quot; don't leave &quot; ">

يمكن ايضا استخدام متغيرات الجافاسكربت لعمل اسم ال attribute كما يلى :

var a = "att";
var el = <foo {a}="b" />;
alert(el.toString()); //<foo att="b">

لا يمكنك عمل التعبير كاملا عن طريق {} كما يلى :

var a = 'a="b"';
var el = <foo {a}/>;

التعامل مع ال atrributes

يمكنك تغير ال attributes و الحصول على قيمها عن طريق وضع @ قبل إسم ال attribute كما يوضح المثال البسيط التالى :

alert(languages.@type); // Alerts "dynamic"
languages.@type = "agile";
alert(languages.@type); // Alerts "agile"
alert(languages.toString());
<languages type="agile">
    <lang>JavaScript</lang>
    <lang>Python</lang>
</languages>

عليك ملاحظه انه لابد من تحويل قيمه ال attribute إلى نص عن طريق toString قبل مقارنته بنص اخر كما يلى :

if(languages.@type.toString() == "agile"){
    // do something
}

التعامل مع الكائن XML

الكائن XML الذى يتم انشاءه عن طريق E4X يحتوى على العديد من الوظائف التى تمكنك من التعامل معه و الحصول على المعلومات منه و تحديث قيمه ، هيا بنا ننشأ كائن XML جديد كما يوضح الكود التالى :

var person = <person>
    <name>Mostafa Farghaly</name>
    <likes>
        <os>Windows Vista</os>
        <browser>FireFox</browser>
        <language>JavaScript</language>
        <language>PHP</language>
    </likes>
</person>

يمكنك الحصول على معلومات الكائنات التى يحتويها الكائن person عن طريق dot notation و عن طريق معامله الكائنات كأنها داخل associative array كما يوضح الكود البسيط التالى :

alert(person.name); //Mostafa Farghaly
alert(person["name"]); //Mostafa Farghaly
alert(person.likes.browser); //FireFox
alert(person["likes"].browser); //FireFox

الكائن الذى يتكرر اكثر من مره يدخل داخل كائن من نوع XMLList يحتوى على مجموعه مرتبه من هذه الكائنات المتشابهه و له الوظيفه length التى تبلغ عن عدد تلك الكائنات كما يلى :

alert(person.likes.language);
<language>JavaScript</language>
<language>PHP</language>
alert(person.likes.length()); //2

عليك ملاحظه ان length وظيفه و ليست خاصيه كما فى حاله المصفوفه .

و كما فى ال DOM يمكنك استخدام ال * للحصول على كل ال Child nodes كما يوضح المثال البسيط التالى :

alert(person.likes.*.length()); //4

المعامل . يستخدم للتعامل مع الكائنات الفرعيه مباشرا من الكائن الحالى direct child nodes ، انا المعامل .. فيستخدم للتعامل مع الكائنات الفرعيه بالنسبه للكائن الحالى مهما كان عمقها او مهما كانت متفرعه من كائنات اخرى فرعيه كما يوضح المثال البسيط التالى :

alert(person..*.length()); //11

الكود السابق ارجع 11 لأنه ناتج ال element node و ال text node داخل الكائن person .

هناك ايضا وظائف اخرى للكائن XML كما يوضح الكود البسيط التالى :

alert(person.name.text()); //Mostafa Farghaly
var xml = person.name.toXMLString();
//<name><Mostafa Farghaly/name>
var personCopy = person.copy(); //new person XML object
var child1 = person.child(1); // <likes> ... </likes> element

التعامل مع الكائن XMLList

كما ذكرت من قبل الكائن XMLList الذى ينتج من الاستعلام عن العناصر المتشابهه له الوظيفه length الذى ترجع عدد هذه الكائنات ، كما يمكن عمل loop على هذه الكائنات من خلال for كما يوضح الكود البسيط التالى :

var languages = person.likes.language;
for(var i=0; i<languages.length(); i+=1){
    alert(languages[i]);
}

كما يمكنك ايضا استخدام الجمله for each in الجديده فى جافاسكربت 1.6 -و مدعومه فى فايرفوكس فقط- للإستعلام عن قيم الكائنات كما يوضح الكود البسيط التالى :

var languages = person.likes.language;
for each(var lang in languages){
    alert(lang);
}

عليك ملاحظه ان for in تقوم بالحصول على ال key -الخصائص و الوظائف- ، انا for each in تقوم بالحصول على القيم فقط value .

يمكنك عمل XMLList ببساطه بإستخدام طريقه ال XML literal عن طريق احاطه الكائنات الجديده ب <> و </> كما يوضح الكود البسيط التالى :

var xml_list = <>
<language></language>
<language></language>
</>;

و يمكنك الاضافه كائنات جديده على ال XMLList عن طريق المعامل =+ كما يوضح المثال البسيط التالى :

var languages = person.likes.language;
languages += <language>Ruby</language>;

ال XMLList الناتج يكون Static بمعنى انه اذا اضيف عليه كائنات جديده فإن الوظيفه length لن تبلغ عن زياده كائن جديد ، عليك اعاده انشاء ال XMLList كما يوضح المثال التالى :

var languages = person.likes.language;
alert(languages.length()); //2
languages += <language>Ruby</language>;
alert(languages.length()); //2
languages = person.likes.languages;
alert(languages.lenght()); //3

البحث و التنقيح

يمكنك إضافه فلتر بين قوسين () للحصول على كائن معين من بين عناصر ال XML ، يمكن اختبار ال attributes كما يوضح المثال البسيط التالى :

var html = <html>
    <p id="p1">the 1st paragraph</p>
    <p id="p2">the 2nd paragraph</p>
</html>;

alert(html.p.(@id=="p1")); // the 1st paragraph

و يمكنك عمل فلتر على العصنر كما يوضح المثال التالى :

var people = <people>
    <person>
        <name>mostafa</name>
        <age>21</age>
    </person>
    <person>
        <name>mohamed</name>
        <age>31</age>
    </person>
</people>;

alert(people.person.(name == "mohammed").age); //31

و يمكنك تمرير وظيفه للفلتر كما يوضح المثال البسيط التالى :

function overs30(age){
    return age>30;
}
alert(people.person.(over30(parseInt(age)).name); //mohammed

للمزيد :

Mozilla E4X
Mozille E4X tutorial

Mozilla Processing XML with E4X


تابع

Get every new post delivered to your Inbox.