نصيحة
بادئ ذي بدء ، أعلم أن هذا المقال ممل ، إنه ليس أكثر من هذا في JS ، وكان هناك الآلاف من المقالات التي تكتب هذا الجزء ؛
ومع ذلك ، ما زلت أرغب في كتابة مقال حول هذا الموضوع في JS ، والذي يمكن اعتباره ملخصًا ؛ (يمكن للآلهة الالتفاف وقراءة مقالاتي الأخرى)
في JS ، لا يمكن التنبؤ دائمًا بأن سياق ذلك لا يمكن التنبؤ به ، وغالبًا ما يتم الخلط بين الحشرات دائمًا. في الواقع ، طالما أنك تميز بوضوح كيفية التنفيذ في ظل ظروف مختلفة ، سيكون الأمر على ما يرام.
التنفيذ العالمي
أولاً ، دعونا نلقي نظرة على ما هو في البيئة العالمية:
أولاً. المتصفح:
console.log (this) ؛ // window {pleasesynthesis: pleasesynthesis ، caches: cachestorage ، localstorage: storage ، sessionstorage: storage ، webkitstorageinfo: deprecatedstorageinfo ...}يمكنك أن ترى أن كائن النافذة مطبوع ؛
ثانية. العقدة:
console.log (هذا) ؛ // العالمية
يمكنك أن ترى أن الكائن العالمي مطبوع ؛
ملخص: في النطاق العالمي ، ينفذ هذا الكائن العالمي الحالي (النافذة على المتصفح ، Global in Node).
تنفيذ في الوظيفة
مكالمات الوظيفة النقية
هذه هي الطريقة الأكثر شيوعًا لاستخدام الوظيفة:
اختبار الوظيفة () {console.log (this) ؛} ؛ test () ؛ // window {pleasesynthesis: pleasesynthesis ، caches: cachestorage ، localstorage: storage ، sessionstorage: storage ، webkitstorageinfo: deprecatedStorageInfo ...}}يمكننا أن نرى أنه عندما يتم استدعاء وظيفة مباشرة ، فإنها تنتمي إلى مكالمة عالمية ، وفي هذا الوقت ، فإن هذا يشير إلى الكائن العالمي ؛
الوضع الصارم "استخدام صارم" ؛
إذا تم تنفيذ مكالمات الوظائف الخالصة في وضع صارم ، فإن هذا هنا لا يشير إلى العالم ، ولكنه غير محدد. هذا هو القضاء على بعض السلوك غير المتساقط في JS:
"استخدام صارم" ؛ اختبار الوظيفة () {console.log (this) ؛} ؛ test () ؛ // undefinedبالطبع ، سيكون وضعها في وظيفة تنفيذ فورية أفضل ، وتجنب تلويث الوضع العالمي:
(function () {"استخدام صارم" ؛ console.log (this) ؛}) () ؛ // undefinedاستدعاء الطريقة ككائن
عندما تسمى الوظيفة كوسيلة كائن:
var obj = {name: 'qiutc' ، foo: function () {console.log (this.name) ؛ }} obj.foo () ؛ // 'qiutc'في هذا الوقت ، يشير هذا إلى الكائن الحالي ؛
بالطبع ، يمكننا القيام بذلك:
دالة اختبار () {console.log (this.name) ؛} var obj = {name: 'qiutc' ، foo: test} obj.foo () ؛ // 'qiutc'لم يتغير أيضًا ، لأنه في JS كل شيء هو كائن ، والوظيفة هي أيضًا كائن. للاختبار ، إنه مجرد اسم وظيفة ، مرجع إلى الوظيفة ، والتي تشير إلى هذه الوظيفة. عند اختبار FOO = ، يشير FOO أيضًا إلى هذه الوظيفة.
ماذا لو قمت بتعيين طريقة الكائن إلى متغير ثم استدعاء هذا المتغير مباشرة:
var obj = {name: 'qiutc' ، foo: function () {console.log (this) ؛ }} var test = obj.foo ؛ test () ؛ // windowيمكن ملاحظة أن هذا ينفذ العالم العالمي في هذا الوقت. عندما نضع اختبار = obj.foo ، يشير الاختبار مباشرة إلى إشارة إلى وظيفة. في هذا الوقت ، في الواقع لا علاقة له بكائن OBJ ، لذلك يطلق عليه مباشرة كدالة عادية ، وبالتالي فإن هذا يشير إلى الكائن العالمي.
بعض المزالق
غالبًا ما نواجه بعض المزالق في وظائف رد الاتصال:
var obj = {name: 'qiutc' ، foo: function () {console.log (this) ؛ } ، foo2: function () {console.log (this) ؛ setTimeout (this.foo ، 1000) ؛ }} obj.foo2 () ؛بعد تنفيذ هذا الرمز ، سنجد أن المطبوعات تختلف عن بعضها البعض:
المرة الأولى هي طباعة هذا مباشرة في FOO2 ، مشيرًا إلى كائن OBJ هنا ، ليس لدينا شكوك ؛
ومع ذلك ، فإن هذا. أليست استخدامها كوسيلة دالة هنا؟ هذا في كثير من الأحيان الألغاز العديد من المبتدئين.
في الواقع ، SetTimeOut هي مجرد وظيفة ، وقد تحتاج الوظيفة إلى معلمات. نقوم بتمرير this.foo كمعلمة إلى وظيفة setTimeout ، تمامًا مثلها تتطلب معلمة ممتعة. عند المرور في المعلمة ، قمنا بالفعل بمثل هذه العملية متعة = this.foo. رؤية أن هناك لا ، نشير مباشرة إلى المرجع إلى هذا. عند التنفيذ ، يتم تنفيذ المرح () بالفعل ، لذلك لا علاقة له بـ OBJ. يطلق عليه مباشرة كدالة عادية ، لذلك هذا يشير إلى الكائن العالمي.
تتم مواجهة هذه المشكلة عادة في العديد من وظائف رد الاتصال غير المتزامن ؛
يحل
لحل هذه المشكلة ، يمكننا استخدام ميزة الإغلاق للتعامل معها:
var obj = {name: 'qiutc' ، foo: function () {console.log (this) ؛ } ، foo2: function () {console.log (this) ؛ var _ this = this ؛ setTimeOut (function () {console.log (this) ؛ // window console.log (_this) ؛ // object {name: "qiutc"}} ، 1000) ؛ }} obj.foo2 () ؛يمكنك أن ترى أن استخدام هذا مباشرة لا يزال نافذة ؛ نظرًا لأن هذا في FOO2 نقاط إلى OBJ ، يمكننا أولاً استخدام متغير - هذا لتخزينه ، ثم استخدام هذا في وظيفة رد الاتصال للإشارة إلى الكائن الحالي ؛
حفرة أخرى من settimeout
كما ذكرنا سابقًا ، إذا تم تنفيذ وظيفة رد الاتصال مباشرة دون نطاق الربط ، فإن هذه النقطة إلى الكائن العالمي (نافذة) ، والتي ستشير إلى غير محدد في الوضع الصارم. ومع ذلك ، فإن وظيفة رد الاتصال في setTimeout تظهر مختلفة في الوضع الصارم:
'use strict' ؛ function foo () {console.log (this) ؛} setTimeout (foo ، 1) ؛ // windowمن الناحية المنطقية ، أضفنا وضعًا صارمًا ، ولم تحدد مكالمة FOO هذا ، لذلك يجب أن تكون غير محددة ، ولكن لا يزال هناك كائن عالمي هنا. هل لأن الوضع الصارم قد فشل؟
لا ، حتى في الوضع الصارم ، عندما تستدعي طريقة setTimeout الوظيفة الواردة ، إذا لم تحدد الوظيفة ذلك ، فستقوم بإجراء عملية ضمنية - حقن السياق العالمي تلقائيًا ، وهو ما يعادل استدعاء foo.apply (نافذة) بدلاً من foo () ؛
بالطبع ، إذا حددنا هذا بالفعل عند المرور في الوظيفة ، فلن يتم حقننا في الكائن العالمي ، مثل: setTimeout (foo.bind (obj) ، 1) ؛؛
استخدم كمؤسس
في JS ، من أجل تنفيذ الفصل ، نحتاج إلى تحديد بعض المُنشئين ، ويحتاج إلى إضافة الكلمة الرئيسية الجديدة عند استدعاء مُنشئ:
وظيفة الشخص (الاسم) {this.name = name ؛ console.log (this) ؛} var p = new شخص ('qiutc') ؛ // person {name: "qiutc"}يمكننا أن نرى أنه عند استدعاء مُنشئ ، يشير هذا إلى الكائن الذي تم إنشاءه عند استدعاء هذا المُنشئ ؛
بالطبع ، المُنشئ هو في الواقع وظيفة. إذا قمنا بتنفيذها كدالة طبيعية ، فسيظل هذا يتم تنفيذه على مستوى العالم:
وظيفة الشخص (الاسم) {this.name = name ؛ console.log (this) ؛} var p = person ('qiutc') ؛ // windowالفرق هو كيفية استدعاء الوظيفة (جديدة).
وظيفة السهم
في مواصفات ES6 الجديدة ، تمت إضافة وظيفة السهم. الشيء الأكثر اختلافًا عن الوظائف العادية هو هذا الإشارة. هل تتذكر أننا نستخدم عمليات الإغلاق لحل مشكلة الإشارة هذه؟ إذا استخدمنا وظيفة السهم ، فيمكننا حلها بشكل أكثر مثالية:
var obj = {name: 'qiutc' ، foo: function () {console.log (this) ؛ } ، foo2: function () {console.log (this) ؛ setTimeOut (() => {console.log (this) ؛ // object {name: "qiutc"}} ، 1000) ؛ }} obj.foo2 () ؛كما ترون ، في الوظيفة التي تنفذها SetTimeOut ، كان ينبغي طباعتها على النافذة ، ولكن هذه تشير إلى OBJ هنا. والسبب هو أن الوظيفة (المعلمة) التي تم تمريرها إلى setTimeout هي وظيفة السهم:
هذا الكائن في جسم الوظيفة هو الكائن الذي يتم تعريفه ، وليس الكائن المستخدم.
بناءً على أمثلة ، دعونا نفهم هذه الجملة:
عندما يتم تنفيذ OBJ.FOO2 () ، فإن هذا التيار يشير إلى OBJ ؛ عند تنفيذ SetTimeOut ، نحدد أولاً وظيفة سهم مجهول ، والنقطة الأساسية موجودة هنا. الكائن الموجود في وظيفة السهم حيث يتم تنفيذ ذلك عند تحديد وظيفة السهم هذه ، يتم توجيهها إلى هذا النطاق عند تحديد وظيفة السهم هذه ، أي في OBJ.FOO2 ، أي OBJ ؛ لذلك عند تنفيذ وظيفة السهم ، فإن هذا -> OBJ في OBJ.FOO2 -> OBJ ؛
ببساطة ، هذا في وظيفة السهم يرتبط فقط بهذا في النطاق عند تحديده ، وليس له أي علاقة بمكان وكيف يطلق عليه. في الوقت نفسه ، فإن هذا الإشارة لا يمكن تغييره.
اتصل ، تطبيق ، ربط
في JS ، الوظائف هي أيضًا كائنات ، وهناك أيضًا بعض الطرق. هنا نقدم ثلاث طرق ، يمكنهم تغيير هذا المؤشر في الوظيفة:
يتصل
fun.call (thisarg [، arg1 [، arg2 [، ...]]])
سوف ينفذ الوظيفة على الفور. تحدد المعلمة الأولى سياق هذا في وظيفة التنفيذ ، والمعلمة اللاحقة هي المعلمات التي تحتاج إلى تمريرها في وظيفة التنفيذ ؛
يتقدم
fun.apply (thisarg [، [arg1 ، arg2 ، ...]])
سوف ينفذ الوظيفة على الفور. تحدد المعلمة الأولى سياق هذا في وظيفة التنفيذ ، والمعلمة الثانية هي صفيف ، وهي المعلمة التي تم تمريرها إلى وظيفة التنفيذ (الفرق من المكالمة) ؛
ربط
var foo = fun.bind (thisarg [، arg1 [، arg2 [، ...]]]) ؛
لا ينفذ الوظيفة ، ولكنه يعيد وظيفة جديدة. تحدد هذه الوظيفة الجديدة سياق هذا ، والمعلمات اللاحقة هي المعلمات التي تحتاج إلى تمريرها لتنفيذ الوظيفة ؛
هذه الوظائف الثلاث متشابهة في الواقع. والغرض العام هو تحديد سياق دالة (هذا). لنأخذ وظيفة المكالمة كمثال ؛
حدد هذا للحصول على وظيفة طبيعية
var obj = {name: 'qiutc'} ؛ function foo () {console.log (this) ؛} foo.call (obj) ؛ // object {name: "qiutc"}يمكن ملاحظة أنه عند تنفيذ foo.call (OBJ) ، يشير هذا في الوظيفة إلى كائن OBJ ، الذي ينجح ؛
حدد هذا للطريقة في الكائن
var obj = {name: 'qiutc' ، foo: function () {console.log (this) ؛ }} var obj2 = {name: 'tcqiu22222'} ؛ obj.foo.call (obj2) ؛ // object {name: "tcqiu22222"}يمكنك أن ترى أنه عند تنفيذ الوظيفة ، فإن هذه النقطة إلى OBJ2 ، والتي تنجح ؛
حدد هذا للمنشئ
وظيفة الشخص (الاسم) {this.name = name ؛ console.log (this) ؛} var obj = {name: 'qiutc222222'} ؛ var p = new person.call (obj ، 'qiutc') ؛ // uncaughter typeerror: person.Call ليس مُنشئًا (...)تم الإبلاغ عن خطأ هنا لأننا ذهبنا إلى شخص جديد. وظيفة ، وليس الشخص ، والوظيفة هنا ليست مُنشئًا ؛
التغيير لربط وحاول:
وظيفة الشخص (الاسم) {this.name = name ؛ console.log (this) ؛} var obj = {name: 'qiutc222222'} ؛ var person2 = person.bind (obj) ؛ var p = new person2 ('qiutc') ؛ // person {name: "qiutc"} console.log (obj) ؛ // object {name:ما تم طباعته هو الكائن الذي قام به الشخص ، والذي لا علاقة له بـ OBJ ، ولم يتغير OBJ ، مما يشير إلى أننا نحدد هذا السياق للشخص دون أن ينفذ ؛
لذلك ، يمكن أن نستنتج أن استخدام الربط لتحديد هذا لمؤسس. عندما يكون مُنشئًا جديدًا ، فإن هذا المحدد بواسطة دالة BIND لن يدخل حيز التنفيذ ؛
بالطبع ، لا يمكن لـ BIND تحديد ذلك فحسب ، بل أيضًا تمرير المعلمات. لنجرب هذه العملية:
وظيفة الشخص (الاسم) {this.name = name ؛ console.log (this) ؛} var obj = {name: 'qiutc222222222'} ؛ var person2 = person.bind (obj ، 'qiutc111111') ؛ var p = new person2 ('qiutc') ؛ // person {name:كما ترون ، على الرغم من أن تحديد هذا لا يعمل ، فإن المعلمات الواردة لا تزال تعمل ؛
حدد هذا لوظيفة السهم
دعونا نحدد وظيفة السهم تحت العالم ، لذلك سيشير هذا في وظيفة السهم هذه إلى الكائن العالمي. ماذا لو تم تغيير هذا باستخدام طريقة الاتصال:
var afoo = (a) => {console.log (a) ؛ console.log (this) ؛} afoo (1) ؛ // 1 // windowvar obj = {name: 'qiutc'} ؛ afoo.call (obj ، 2) ؛ // 2 // windowكما ترون ، لم يكن تشغيل المكالمة التي تشير إلى هذا هنا ناجحًا ، لذلك يمكن أن نستنتج أن هذا في وظيفة السهم قد قرر بالفعل عند تعريفها (تنفيذ هذا في النطاق الذي يحدده) ، وليس له أي علاقة بكيفية الاتصال به ومكان الاتصال به. لا توجد عمليات بما في ذلك (Call ، تطبيق ، BIND) والعمليات الأخرى لا يمكن تغيير هذا.
فقط تذكر أن وظيفة السهم جيدة ولا يزال هذا دون تغيير.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.