نحن نثق في JS - أفضل طريقة للتعلم هي البناء/البرمجة والتدريس. أقوم بإنشاء التحديات لمساعدة أصدقائي على تعلم JavaScript وفي المقابل يساعدني ذلك في احتضان اللغة على مستوى أعمق بكثير. لا تتردد في الاستنساخ والشوكة والسحب.
function a ( x ) {
x ++ ;
return function ( ) {
console . log ( ++ x ) ;
} ;
}
a ( 1 ) ( ) ;
a ( 1 ) ( ) ;
a ( 1 ) ( ) ;
let x = a ( 1 ) ;
x ( ) ;
x ( ) ;
x ( ) ;
1, 2, 3
و 1, 2, 3
3, 3, 3
و 3, 4, 5
3, 3, 3
و 1, 2, 3
1, 2, 3
و 3, 3, 3
يعيد هذا السؤال النظر في الإغلاق - وهو أحد المفاهيم الأكثر إرباكًا في JavaScript. يسمح لنا الإغلاق بإنشاء stateful function
ويمكن لمثل هذه الوظيفة الوصول إلى المتغير خارج نطاقها. باختصار، يمكن للإغلاق الوصول إلى المتغير global
(النطاق) ونطاق father function
ونطاقه its
.
لدينا هنا الإجابة الصحيحة الوحيدة، 3، 3، 3 و3، 4، 5 لأننا أولاً نسمي الدالة a()
. إنها تعمل كوظيفة عادية ولم نر أي شيء يسمى stateful
حتى الآن. في الكود التالي، نعلن عن المتغير x
ويقوم بتخزين قيمة الدالة a(1)
، ولهذا السبب حصلنا على 3.4.5 بدلاً من 3, 3, 3.
يمنحني هذا النوع من الأخطاء إحساسًا بوجود متغير static
في عالم PHP.
function Name ( a , b ) {
this . a = a ;
this . b = b ;
}
const me = Name ( "Vuong" , "Nguyen" ) ;
console . log ( ! ( a . length - window . a . length ) ) ;
undefined
NaN
true
false
لقد أصبحنا صادقين في وحدة التحكم. الجزء الصعب هو عندما نقوم بإنشاء كائن من اسم وظيفة المُنشئ ولكننا لا نستخدم عمل مفاتيح new
. وهذا يجعل المتغير a
ويحصل على القيمة "Vuong". تذكر أنها في الواقع خاصية window
الكائن العامة (في المتصفح) أو global
في العقدة.
نحصل بعد ذلك على a.length
~ 5 و window.a.length
~ 5 والتي تُرجع 0. !0 تُرجع صحيحًا.
تخيل ما سيحدث عندما نقوم بإنشاء المثيل me
باستخدام المفتاح new
. هذا استفسار مثير للاهتمام!
const x = function ( ... x ) {
let k = ( typeof x ) . length ;
let y = ( ) => "freetut" . length ;
let z = { y : y } ;
return k - z . y ( ) ;
} ;
console . log ( Boolean ( x ( ) ) ) ;
true
false
قد يساعدنا عامل الانتشار ...x
في الحصول على المعلمة في الوظيفة على شكل مصفوفة. ومع ذلك، في Javascript، يُرجع نوع المصفوفة "object" بدلاً من "array". إنه أمر غريب تمامًا إذا كنت قادمًا من PHP.
ومع ذلك، لدينا الآن طول object
السلسلة الذي يُرجع 6. zy() يُرجع ببساطة طول السلسلة 'freetut' (7).
انتبه إلى أن الدالة x() (في شكل function express
أو anonymous function
(إذا كنت قادمًا من PHP) تُرجع -1 عند استدعائها وعند تحويلها إلى منطقية باستخدام Boolean(-1)
تُرجع صحيحًا بدلاً من خطأ. أن Boolean(0)
يُرجع خطأ.
( function js ( x ) {
const y = ( j ) => j * x ;
console . log ( y ( s ( ) ) ) ;
function s ( ) {
return j ( ) ;
}
function j ( ) {
return x ** x ;
}
} ) ( 3 ) ;
undefined
يمكن تنفيذ الدالة js()
تلقائيًا دون استدعائها وتعرف باسم IIFE (تعبير الدالة الذي يتم استدعاؤه فورًا). لاحظ أنه تم تمرير المعلمة x
للدالة js
فعليًا بالقيمة 3.
قيمة إرجاع الدالة هي y(s()))، مما يعني استدعاء ثلاث وظائف أخرى y()
و s()
و j()
لأن الدالة s()
ترجع j()
.
تقوم j() بإرجاع 3^3 = 27 بحيث تقوم s() بإرجاع 27.
y(s()) تعني y(27) والتي تُرجع 27*3 = 81.
لاحظ أنه يمكننا استدعاء declare function
قبل أن يتم التصريح عن الوظيفة فعليًا ولكن ليس باستخدام expression function
.
var tip = 100 ;
( function ( ) {
console . log ( "I have $" + husband ( ) ) ;
function wife ( ) {
return tip * 2 ;
}
function husband ( ) {
return wife ( ) / 2 ;
}
var tip = 10 ;
} ) ( ) ;
لدينا هنا IIFE (تعبير دالة يتم استدعاؤه فورًا). هذا يعني أنه ليس من الضروري أن نستدعيه ولكن سيتم تنفيذه تلقائيًا عند الإعلان عنه. يكون التدفق كما يلي: الزوج () يُرجع الزوجة ()/2 والزوجة () تُرجع الطرف * 2.
قد نعتقد أن هذه النصيحة = 100 لأنها متغير عام عند الإعلان باستخدام الكلمة الأساسية var
. ومع ذلك، فهو في الواقع undefined
لأن لدينا أيضًا var tip = 10
داخل الدالة. نظرًا لأنه تم رفع tip
المتغير بالقيمة الافتراضية undefined
، فإن النتيجة النهائية ستكون D. ونحن نعلم أن undefined
يُرجع NaN عندما نحاول القسمة على 2 أو مضاعف بـ 2.
إذا لم نعيد الإعلان عن var tip = 10;
في نهاية الدالة، سنحصل بالتأكيد على B.
JS ممتع، أليس كذلك؟
const js = { language : "loosely type" , label : "difficult" } ;
const edu = { ... js , level : "PhD" } ;
const newbie = edu ;
delete edu . language ;
console . log ( Object . keys ( newbie ) . length ) ;
يقوم هذا التحدي بمراجعة ميزة ES6 فيما يتعلق spread operator ...
يعد عامل الانتشار مفيدًا جدًا لاسترداد المعلمة في الوظيفة، unite
أو combine
الكائن والمصفوفة في JavaScript. PHP لديها هذه الميزة أيضًا.
في المتغير edu
، نستخدم ...js
(عامل الانتشار هنا) لدمج كلا الكائنين في كائن واحد. إنه يعمل بنفس الطريقة مع المصفوفة.
ثم نعلن عن متغير آخر اسمه newbie
. ملاحظة هامة: من خلال الإعلان عن المتغير بهذه الطريقة، يشير كلا المتغيرين إلى نفس الموضع في الذاكرة. ربما عرفنا شيئًا مثل $a = &$b
في PHP، والذي يسمح لكلا المتغيرات بالعمل بنفس الطريقة. ربما علمنا pass by reference
في هذه القضية.
ثم لدينا 2 حيث يتم حذف edu.language
. يحتوي كلا الكائنين الآن على عنصرين فقط.
لقد حان الوقت للتفكير في التعامل مع كائن ما في JS سواء كان سطحيًا أو عميقًا.
var candidate = {
name : "Vuong" ,
age : 30 ,
} ;
var job = {
frontend : "Vuejs or Reactjs" ,
backend : "PHP and Laravel" ,
city : "Auckland" ,
} ;
class Combine {
static get ( ) {
return Object . assign ( candidate , job ) ;
}
static count ( ) {
return Object . keys ( this . get ( ) ) . length ;
}
}
console . log ( Combine . count ( ) ) ;
تقوم طريقة buit-in Object.assign(candidate, job)
بدمج الكائنين candidate
job
في كائن واحد. ثم تقوم الطريقة Object.keys
بحساب عدد key
في الكائن.
لاحظ أن الطريقتين get()
و count()
تم تعريفهما على أنهما static
، لذا يجب استدعاؤهما بشكل ثابت باستخدام بناء جملة Class.staticmethod()
. ثم يحصل الكائن النهائي على 5 عناصر.
var x = 1 ;
( ( ) => {
x += 1 ;
++ x ;
} ) ( ) ;
( ( y ) => {
x += y ;
x = x % y ;
} ) ( 2 ) ;
( ( ) => ( x += x ) ) ( ) ;
( ( ) => ( x *= x ) ) ( ) ;
console . log ( x ) ;
في البداية يتم تعريف x
بالقيمة 1. في دالة IIFE الأولى، هناك عمليتان. أول x
يصبح 2 ثم 3.
في الدالة IIFE الثانية، x = x + y
فإن القيمة الحالية هي 5. في العملية الثانية، تُرجع 1 فقط لأنها تخضع لـ 5%2
.
في الدالتين الثالثة والرابعة IIFE، نحصل على 2 x = x + x
ثم 4 x = x * x
. إنها أكثر من بسيطة.
$ var = 10 ;
$ f = function ( $ let ) use ( $ var ) {
return ++ $ let + $ var ;
};
$ var = 15 ;
echo $ f ( 10 );
var x = 10 ;
const f = ( l ) => ++ l + x ;
x = 15 ;
console . log ( f ( 10 ) ) ;
يوضح هذا السؤال الاختلافات بين PHP وJavaScript عند التعامل مع الإغلاق. في المقتطف الأول، نعلن عن الإغلاق باستخدام الكلمة الرئيسية use
. الإغلاق في PHP هو ببساطة وظيفة مجهولة ويتم تمرير البيانات إلى الوظيفة باستخدام الكلمة الأساسية use
. بخلاف ذلك، يطلق عليه اسم lambda
عندما لا نستخدم الكلمة الأساسية use
. يمكنك التحقق من نتيجة المقتطف هنا https://3v4l.org/PSeMY. لا يقبل closure
PHP سوى قيمة المتغير قبل تحديد الإغلاق، بغض النظر عن مكان استدعائه. على هذا النحو، $var
هو 10 بدلاً من 15.
على العكس من ذلك، تتعامل JavaScript مع المتغير بشكل مختلف قليلاً عند تمريره إلى دالة مجهولة. ليس علينا استخدام الكلمة الأساسية use
هنا لتمرير المتغير إلى الإغلاق. يتم تحديث المتغير x
في المقتطف الثاني قبل استدعاء الإغلاق، فنحصل على 26.
لاحظ أنه في PHP 7.4، لدينا دالة سهمية، ومن ثم لا يتعين علينا استخدام الكلمة الأساسية use
لتمرير المتغير إلى الوظيفة. هناك طريقة أخرى لاستدعاء ariable global
داخل دالة في PHP وهي استخدام الكلمة الأساسية global
أو استخدام المتغير GLOBAL المدمج $GLOBALS.
let x = { } ;
let y = { } ;
let z = x ;
console . log ( x == y ) ;
console . log ( x === y ) ;
console . log ( x == z ) ;
console . log ( x === z ) ;
من الناحية الفنية، x
و y
لهما نفس القيمة. كلاهما كائنات فارغة. ومع ذلك، فإننا لا نستخدم القيمة لمقارنة الكائنات.
z
is x
هما كائنان يشيران إلى نفس موضع الذاكرة. في JavaScript، يتم تمرير المصفوفة والكائن حسب reference
. وبالتالي فإن x
و z
يعودان صحيحًا عند مقارنتهما.
console . log ( "hello" ) ;
setTimeout ( ( ) => console . log ( "world" ) , 0 ) ;
console . log ( "hi" ) ;
نظرًا لأن الدالة setTimeout() سيتم الاحتفاظ بها في task queue
قبل العودة إلى stack,
فستتم طباعة "hello" و"hi" أولاً، ثم A غير صحيح. وهذا هو الحال أيضًا بالنسبة للإجابات C وD.
بغض النظر عن عدد الثواني التي قمت بتعيينها على الدالة setTimeout()
، فسيتم تشغيلها بعد التعليمات البرمجية المتزامنة. لذلك سوف نحصل على "مرحبًا" أولاً حيث يتم وضعها في مكدس الاستدعاءات أولاً. على الرغم من أنه يتم بعد ذلك وضع setTimeout()
في مكدس الاستدعاءات، إلا أنه سيتم إلغاء تحميله لاحقًا إلى Web API (أو Node API) ثم يتم استدعاؤه عند مسح الرموز المتزامنة الأخرى. وهذا يعني أننا نحصل بعد ذلك على "مرحبًا" وأخيرًا "العالم".
لذا فإن B هي الإجابة الصحيحة.
الائتمان: @kaitoubg (voz) لاقتراحك فيما يتعلق timeout throttled
التي قررت تغيير السؤال قليلاً. سيضمن عدم إرباك القراء لأن الكود السابق قد يُظهر نتائج مختلفة عند اختباره على متصفحات أو بيئات أخرى. النقطة الرئيسية في السؤال هي حول التناقض بين الكود المتزامن والرمز غير المتزامن عند استخدام setTimeout.
.
String . prototype . lengthy = ( ) => {
console . log ( "hello" ) ;
} ;
let x = { name : "Vuong" } ;
delete x ;
x . name . lengthy ( ) ;
String.prototype.someThing = function () {}
هي الطريقة الشائعة لتعريف طريقة مدمجة جديدة لـ String
. يمكننا أن نفعل الشيء نفسه مع Array
أو Object
أو FunctionName
حيث تكون FunctionName هي الوظيفة التي صممناها بأنفسنا.
ليس من الصعب أن ندرك أن "string".lengthy()
تُرجع hello
دائمًا. ومع ذلك، فإن الجزء الصعب يكمن في delete object
حيث قد نعتقد أن هذا التعبير سيحذف الكائن بالكامل. ليس هذا هو الحال حيث يتم استخدام delete
لحذف خاصية الكائن فقط. ولا يحذف الكائن. ثم نحصل على hello
بدلاً من ReferenceError
.
لاحظ أنه إذا أعلنا عن كائن بدون let, const
أو var
، فسيكون لدينا كائن عام. delete objectName
ثم ارجع true
. وبخلاف ذلك، فإنه يُرجع دائمًا false
.
let x = { } ;
x . __proto__ . hi = 10 ;
Object . prototype . hi = ++ x . hi ;
console . log ( x . hi + Object . keys ( x ) . length ) ;
أولًا لدينا كائن فارغ x
، ثم نضيف خاصية أخرى hi
لـ x مع x.__proto__.hi
. لاحظ أن هذا يعادل Object.prototype.hi = 10
ونحن نضيف إلى الكائن father
Object
Object hi
. وهذا يعني أن كل كائن سوف يرث هذه الخاصية. الخاصية hi
تصبح مشتركة. لنفترض الآن أننا نعلن عن كائن جديد مثل let y = {}
، y
الآن لديه hi
موروثة من father
Object
. ببساطة x.__proto__ === Object.prototype
true
.
ثم نستبدل الخاصية hi
بقيمة جديدة 11. آخر مرة لدينا 11 + 1 = 12. x
لها خاصية واحدة و x.hi
تُرجع 11.
تم التحديث (27 يوليو 2021). إذا كتبت Object.prototype.hi = 11;
بدلاً من Object.prototype.hi = ++x.hi;
كما هو مكتوب في الكود أعلاه، فإن Object.keys(x)
سيُرجع مصفوفة فارغة لأن Object.keys(object)
يُرجع فقط خاصية الكائن نفسه، وليس الخاصية الموروثة. وهذا يعني أن النتيجة النهائية ستكون 11 بدلاً من 12. لسبب ما، الكود ``Object.prototype.hi = ++x.hi; will create a property for the object
x` نفسه ثم `Object.keys(x)` سيعطينا المصفوفة `["hi"]`.
ومع ذلك، إذا قمت بتشغيل console.log(x.hasOwnProperty("hi"))
فسيظل يُرجع false
. بالمناسبة، عندما تقوم بإضافة خاصية لـ x عن عمد مثل x.test = "testing"
، فإن console.log(x.hasOwnProperty("test"))
يُرجع true
.
const array = ( a ) => {
let length = a . length ;
delete a [ length - 1 ] ;
return a . length ;
} ;
console . log ( array ( [ 1 , 2 , 3 , 4 ] ) ) ;
const object = ( obj ) => {
let key = Object . keys ( obj ) ;
let length = key . length ;
delete obj [ key [ length - 1 ] ] ;
return Object . keys ( obj ) . length ;
} ;
console . log ( object ( { 1 : 2 , 2 : 3 , 3 : 4 , 4 : 5 } ) ) ;
const setPropNull = ( obj ) => {
let key = Object . keys ( obj ) ;
let length = key . length ;
obj [ key [ length - 1 ] ] = null ;
return Object . keys ( obj ) . length ;
} ;
console . log ( setPropNull ( { 1 : 2 , 2 : 3 , 3 : 4 , 4 : 5 } ) ) ;
يفحص هذا السؤال كيفية عمل عامل delete
في JavaScript. باختصار، لا يفعل شيئًا عندما نكتب delete someObject
أو delete someArray
. ومع ذلك، فإنه يحذف ويزيل خاصية كائن ما تمامًا عند كتابة شيء مثل delete someObject.someProperty
. في حالة المصفوفة، عندما نكتب delete someArray[keyNumber]
، فإنه يزيل فقط value
index
، ويحافظ على index
سليمًا ويتم الآن تعيين value
الجديدة على undefined
. لهذا السبب، في المقتطف الأول من الكود، نحصل على (الطول) 4 عناصر كما هو الحال في المصفوفة الأصلية ولكن تبقى 3 خصائص فقط في الكائن الذي تم تمريره عند استدعاء الدالة object()، كما في المقتطف الثاني.
يعطينا المقتطف الثالث 4 حيث أن الإعلان عن ملكية الكائن إما null
أو undefined
لا يؤدي إلى إزالة الخاصية بالكامل. المفتاح سليم. وبالتالي فإن طول الكائن غير قابل للتغيير.
بالنسبة لأولئك الذين هم على دراية بـ PHP، لدينا unset($someArray[index])
الذي يزيل عنصر المصفوفة، المفتاح والقيمة. عند print_r
المصفوفة، قد لا نرى المفتاح والقيمة التي تم إلغاء تعيينها. ومع ذلك، عندما ندفع (باستخدام array_push($someArray, $someValue)
) عنصرًا جديدًا في تلك المصفوفة، قد نرى أن المفتاح السابق لا يزال محفوظًا، ولكن بدون قيمة ولا يتم عرضه. وهذا شيء يجب أن تكون على علم به. ألقِ نظرة على https://3v4l.org/7C3Nf
var a = [ 1 , 2 , 3 ] ;
var b = [ 1 , 2 , 3 ] ;
var c = [ 1 , 2 , 3 ] ;
var d = c ;
var e = [ 1 , 2 , 3 ] ;
var f = e . slice ( ) ;
console . log ( a === b ) ;
console . log ( c === d ) ;
console . log ( e === f ) ;
تُرجع a
و b
false لأنها تشير إلى موقع ذاكرة مختلف على الرغم من أن القيمتين متماثلتان. إذا كنت قادمًا من عالم PHP، فمن الواضح أنه سيعود صحيحًا عندما نقارن القيمة أو القيمة + النوع. التحقق من ذلك: https://3v4l.org/IjaOs.
في JavaScript، يتم تمرير القيمة حسب المرجع في حالة array
object
. وبالتالي في الحالة الثانية، d
هي نسخة من c
لكن كلاهما يشير إلى نفس موضع الذاكرة. كل شيء يتغير في c
سيؤدي إلى التغيير في d
. في PHP، قد يكون لدينا $a = &$b;
، تعمل بنفس الطريقة.
يعطينا الخيار الثالث تلميحًا لنسخ مصفوفة في JavaScript باستخدام طريقة slice()
. الآن لدينا f
، وهي نسخة من e
ولكنها تشير إلى مواقع مختلفة في الذاكرة، وبالتالي لديهم "حياة" مختلفة. وفقًا لذلك، نحصل على false
عند مقارنتها.
var languages = {
name : [ "elixir" , "golang" , "js" , "php" , { name : "feature" } ] ,
feature : "awesome" ,
} ;
let flag = languages . hasOwnProperty ( Object . values ( languages ) [ 0 ] [ 4 ] . name ) ;
( ( ) => {
if ( flag !== false ) {
console . log (
Object . getOwnPropertyNames ( languages ) [ 0 ] . length <<
Object . keys ( languages ) [ 0 ] . length
) ;
} else {
console . log (
Object . getOwnPropertyNames ( languages ) [ 1 ] . length <<
Object . keys ( languages ) [ 1 ] . length
) ;
}
} ) ( ) ;
يعد مقتطف التعليمات البرمجية أمرًا صعبًا للغاية لأنه يحتوي على طريقتين مختلفتين مدمجتين للتعامل مع الكائن في JavaScript
. على سبيل المثال، يتم استخدام كل من Object.keys
و Object.getOwnPropertyNames
على الرغم من أنهما متشابهان تمامًا باستثناء أن الأخير يمكنه إرجاع خصائص غير قابلة للإحصاء. قد ترغب في إلقاء نظرة على هذا المرجع المكتوب بدقة https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
تقوم Object.values
و Object.keys
بإرجاع قيمة الخاصية واسم الخاصية للكائن، على التوالي. هذا ليس شيئا جديدا. يُرجع object.hasOwnProperty('propertyName')
boolean
تؤكد ما إذا كانت الخاصية موجودة أم لا.
لقد وضعنا flag
صحيح لأن Object.values(languages)[0][4].name
يُرجع feature
، وهو أيضًا اسم الخاصية.
ثم لدينا 4 << 4 في تدفق if-else
الذي يُرجع قيمة البت، أي ما يعادل 4*2^4
~ 4*16
~ 64.
var player = {
name : "Ronaldo" ,
age : 34 ,
getAge : function ( ) {
return ++ this . age - this . name . length ;
} ,
} ;
function score ( greeting , year ) {
console . log (
greeting + " " + this . name + `! You were born in ${ year - this . getAge ( ) } `
) ;
}
window . window . window . score . call ( window .