เคล็ดลับ
ก่อนอื่นฉันรู้ว่าบทความนี้น่าเบื่อมันไม่มีอะไรมากไปกว่านี้ใน JS และมีบทความหลายพันบทความที่เขียนส่วนนี้
อย่างไรก็ตามฉันยังต้องการเขียนบทความเกี่ยวกับเรื่องนี้ใน JS ซึ่งถือได้ว่าเป็นบทสรุป (เทพสามารถไปรอบ ๆ และอ่านบทความอื่น ๆ ของฉัน)
ใน JS บริบทของสิ่งนี้ไม่สามารถคาดเดาได้เสมอและบ่อยครั้งที่ข้อบกพร่องมักจะสับสน ในความเป็นจริงตราบใดที่คุณแยกแยะวิธีการดำเนินการภายใต้สถานการณ์ที่แตกต่างกันอย่างชัดเจนก็จะโอเค
การดำเนินการทั่วโลก
ก่อนอื่นมาดูกันว่าสิ่งนี้อยู่ในสภาพแวดล้อมระดับโลก:
อันดับแรก. เบราว์เซอร์:
console.log (this); // window {PeechSynThesis: PeechSynthesis, แคช: Cachestorage, LocalStorage: Storage, SessionStorage: Storage, WebKitStorageInfo: DEPRECATEDSTORAGEINFO …}คุณจะเห็นว่าวัตถุหน้าต่างถูกพิมพ์
ที่สอง. โหนด:
console.log (นี่); // ทั่วโลก
คุณจะเห็นว่าวัตถุทั่วโลกถูกพิมพ์
สรุป: ในขอบเขตทั่วโลกสิ่งนี้จะดำเนินการวัตถุทั่วโลกในปัจจุบัน (หน้าต่างบนเบราว์เซอร์ทั่วโลกในโหนด)
ดำเนินการในฟังก์ชัน
การเรียกใช้ฟังก์ชั่นบริสุทธิ์
นี่เป็นวิธีที่พบได้บ่อยที่สุดในการใช้ฟังก์ชัน:
การทดสอบฟังก์ชั่น () {console.log (this);}; test (); // window {peechingsynthesis: peechingsynthesis, caches: cachestorage, localstorage: การจัดเก็บ, sessionstorage: การจัดเก็บ, webkitstorageinfo: deprecatedstorageinfo …}}เราจะเห็นได้ว่าเมื่อมีการเรียกฟังก์ชั่นโดยตรงมันจะเป็นของการโทรทั่วโลกและในเวลานี้มันชี้ไปที่วัตถุทั่วโลก
โหมดเข้มงวด 'ใช้อย่างเข้มงวด';
หากการเรียกใช้ฟังก์ชั่นบริสุทธิ์ถูกดำเนินการในโหมดที่เข้มงวดสิ่งนี้ที่นี่ไม่ได้ชี้ไปที่ทั่วโลก แต่ไม่ได้กำหนด นี่คือการกำจัดพฤติกรรมการคลี่คลายบางอย่างใน JS:
'ใช้อย่างเข้มงวด'; การทดสอบฟังก์ชั่น () {console.log (นี่);}; test (); // undefinedแน่นอนว่าการวางไว้ในฟังก์ชั่นการดำเนินการทันทีจะดีกว่าการหลีกเลี่ยงการก่อมลพิษทั่วโลก:
(function () {"ใช้อย่างเข้มงวด"; console.log (this);}) (); // undefinedวิธีการเรียกเป็นวัตถุ
เมื่อฟังก์ชั่นถูกเรียกว่าเป็นวิธีการของวัตถุ:
var obj = {ชื่อ: 'qiutc', foo: function () {console.log (this.name); }} obj.foo (); // 'qiutc'ในเวลานี้ชี้ไปที่วัตถุปัจจุบัน
แน่นอนเราสามารถทำได้:
ฟังก์ชั่นทดสอบ () {console.log (this.name);} var obj = {ชื่อ: 'qiutc', foo: ทดสอบ} obj.foo (); // 'qiutc'ยังไม่เปลี่ยนแปลงเพราะใน JS ทุกอย่างเป็นวัตถุและฟังก์ชั่นก็เป็นวัตถุ สำหรับการทดสอบมันเป็นเพียงชื่อฟังก์ชั่นการอ้างอิงถึงฟังก์ชั่นซึ่งชี้ไปที่ฟังก์ชั่นนี้ เมื่อ foo = ทดสอบฟูก็ชี้ไปที่ฟังก์ชั่นนี้
จะเกิดอะไรขึ้นถ้าคุณกำหนดวิธีการของวัตถุให้กับตัวแปรแล้วเรียกตัวแปรนี้โดยตรง:
var obj = {ชื่อ: 'qiutc', foo: function () {console.log (นี่); }} var test = obj.foo; test (); // windowจะเห็นได้ว่าสิ่งนี้ดำเนินการทั่วโลกในเวลานี้ เมื่อเราทำการทดสอบ = obj.foo ให้ทดสอบชี้ไปที่การอ้างอิงโดยตรงไปยังฟังก์ชั่น ในเวลานี้จริง ๆ แล้วมันไม่มีส่วนเกี่ยวข้องกับวัตถุ OBJ ดังนั้นจึงเรียกว่าโดยตรงเป็นฟังก์ชันธรรมดาดังนั้นสิ่งนี้ชี้ไปที่วัตถุระดับโลก
ข้อผิดพลาดบางอย่าง
เรามักจะพบข้อผิดพลาดบางอย่างในฟังก์ชั่นการโทรกลับ:
var obj = {ชื่อ: 'qiutc', foo: function () {console.log (นี่); }, foo2: function () {console.log (นี่); settimeout (this.foo, 1,000); }} obj.foo2 ();หลังจากดำเนินการรหัสนี้เราจะพบว่างานพิมพ์แตกต่างจากกัน:
ครั้งแรกคือการพิมพ์สิ่งนี้โดยตรงใน FOO2 ชี้ไปที่วัตถุ OBJ ที่นี่เราไม่มีข้อสงสัย
อย่างไรก็ตาม this.foo ดำเนินการในการตั้งถิ่นฐานชี้ไปที่วัตถุทั่วโลก มันไม่ได้ใช้เป็นวิธีการฟังก์ชั่นที่นี่? สิ่งนี้มักจะปริศนาผู้เริ่มต้นจำนวนมาก;
ในความเป็นจริง Settimeout เป็นเพียงฟังก์ชั่นและฟังก์ชั่นอาจต้องใช้พารามิเตอร์ เราผ่านสิ่งนี้ foo เป็นพารามิเตอร์ไปยังฟังก์ชัน settimeout เช่นเดียวกับมันต้องใช้พารามิเตอร์ที่สนุก เมื่อผ่านในพารามิเตอร์เราได้ทำการดำเนินการสนุก = this.foo เมื่อเห็นว่าไม่มีเราชี้ไปที่การอ้างอิงของสิ่งนี้โดยตรง เมื่อดำเนินการ Fun () ถูกดำเนินการจริงดังนั้นจึงไม่มีส่วนเกี่ยวข้องกับ OBJ มันถูกเรียกโดยตรงว่าเป็นฟังก์ชั่นธรรมดาดังนั้นสิ่งนี้ชี้ไปที่วัตถุทั่วโลก
ปัญหานี้พบได้ทั่วไปในฟังก์ชั่นการโทรกลับแบบอะซิงโครนัสจำนวนมาก
แก้ปัญหา
เพื่อแก้ปัญหานี้เราสามารถใช้คุณสมบัติการปิดเพื่อจัดการกับมัน:
var obj = {ชื่อ: 'qiutc', foo: function () {console.log (นี่); }, foo2: function () {console.log (นี่); var _this = this; settimeout (function () {console.log (นี่); // window console.log (_this); // object {ชื่อ: "qiutc"}}, 1000); }} obj.foo2 ();คุณจะเห็นว่าการใช้สิ่งนี้ยังคงเป็นหน้าต่าง เนื่องจากสิ่งนี้ใน FOO2 ชี้ไปที่ OBJ เราจึงสามารถใช้ตัวแปร _ this เพื่อเก็บไว้ก่อนจากนั้นใช้ _ นี่ในฟังก์ชันการโทรกลับเพื่อชี้ไปที่วัตถุปัจจุบัน
อีกหลุมหนึ่งของ settimeout
ดังที่ได้กล่าวไว้ก่อนหน้านี้หากฟังก์ชั่นการโทรกลับถูกดำเนินการโดยตรงโดยไม่ต้องมีขอบเขตการเชื่อมโยงจุดนี้ไปยังวัตถุทั่วโลก (หน้าต่าง) ซึ่งจะชี้ไปที่ไม่ได้กำหนดไว้ในโหมดที่เข้มงวด อย่างไรก็ตามฟังก์ชั่นการโทรกลับใน SetTimeOut แสดงความแตกต่างในโหมดที่เข้มงวด:
'ใช้อย่างเข้มงวด'; ฟังก์ชั่น foo () {console.log (นี่);} settimeout (foo, 1); // windowการพูดอย่างมีเหตุผลเราเพิ่มโหมดที่เข้มงวดและการเรียก Foo ไม่ได้ระบุสิ่งนี้ดังนั้นจึงควรไม่ได้กำหนด แต่ยังมีวัตถุระดับโลกที่นี่ เป็นเพราะโหมดที่เข้มงวดล้มเหลว?
ไม่แม้ในโหมดที่เข้มงวดเมื่อวิธีการ SettimeOut เรียกฟังก์ชันที่เข้ามาหากฟังก์ชั่นไม่ได้ระบุสิ่งนี้มันจะทำการดำเนินการโดยนัย - ฉีดบริบททั่วโลกโดยอัตโนมัติซึ่งเทียบเท่ากับการเรียก foo.apply (หน้าต่าง) แทน foo ();
แน่นอนถ้าเราระบุสิ่งนี้แล้วเมื่อผ่านฟังก์ชั่นเราจะไม่ถูกฉีดเข้าไปในวัตถุระดับโลกเช่น: settimeout (foo.bind (OBJ), 1) ;;
ใช้เป็นตัวสร้าง
ใน JS เพื่อที่จะใช้คลาสเราจำเป็นต้องกำหนดตัวสร้างบางตัวและจำเป็นต้องเพิ่มคำหลักใหม่เมื่อเรียกตัวสร้าง:
ฟังก์ชันบุคคล (ชื่อ) {this.name = name; console.log (นี่);} var p = บุคคลใหม่ ('qiutc'); // บุคคล {ชื่อ: "qiutc"}เราจะเห็นได้ว่าเมื่อเรียกว่าเป็นตัวสร้างสิ่งนี้ชี้ไปที่วัตถุอินสแตนซ์เมื่อตัวสร้างนี้เรียกว่า;
แน่นอนตัวสร้างเป็นฟังก์ชั่นจริง หากเราดำเนินการเป็นฟังก์ชั่นปกติสิ่งนี้จะยังคงดำเนินการทั่วโลก:
ฟังก์ชันบุคคล (ชื่อ) {this.name = name; console.log (this);} var p = person ('qiutc'); // windowความแตกต่างคือวิธีเรียกฟังก์ชั่น (ใหม่)
ฟังก์ชันลูกศร
ในข้อกำหนด ES6 ใหม่มีการเพิ่มฟังก์ชั่นลูกศรแล้ว สิ่งที่แตกต่างที่สุดจากฟังก์ชั่นทั่วไปคือการชี้นี้ คุณจำได้ไหมว่าเราใช้การปิดเพื่อแก้ปัญหาการชี้นี้? หากเราใช้ฟังก์ชั่นลูกศรเราสามารถแก้ปัญหาได้อย่างสมบูรณ์แบบมากขึ้น:
var obj = {ชื่อ: 'qiutc', foo: function () {console.log (นี่); }, foo2: function () {console.log (นี่); settimeout (() => {console.log (นี่); // object {ชื่อ: "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 = {ชื่อ: 'qiutc'}; function foo () {console.log (นี่);} foo.call (obj); // object {ชื่อ: "qiutc"}จะเห็นได้ว่าเมื่อดำเนินการ foo.call (OBJ) สิ่งนี้ในฟังก์ชั่นชี้ไปที่วัตถุ OBJ ซึ่งประสบความสำเร็จ
ระบุสิ่งนี้สำหรับวิธีการในวัตถุ
var obj = {ชื่อ: 'qiutc', foo: function () {console.log (นี่); }}var obj2 = { name: 'tcqiu222222'};obj.foo.call(obj2);// Object {name: "tcqiu222222"}คุณจะเห็นว่าเมื่อดำเนินการฟังก์ชั่นนี้ชี้ไปที่ OBJ2 ซึ่งประสบความสำเร็จ
ระบุสิ่งนี้สำหรับตัวสร้าง
ฟังก์ชันบุคคล (ชื่อ) {this.name = name; console.log (this);} var obj = {ชื่อ: 'qiutc22222222222'}; var p = บุคคลใหม่ call (obj, 'qiutc');มีการรายงานข้อผิดพลาดที่นี่เพราะเราไปหาคนใหม่ฟังก์ชั่นโทรไม่ใช่บุคคลและฟังก์ชั่นที่นี่ไม่ใช่ตัวสร้าง
เปลี่ยนเป็นผูกและลอง:
ฟังก์ชันบุคคล (ชื่อ) {this.name = name; console.log (this);} var obj = {ชื่อ: 'qiutc222222222'}; var person2 = person.bind (obj); var p = person2 ('qiutc'); // person {ชื่อ: "qiutc"} console.log (obj);สิ่งที่พิมพ์ออกมาคือวัตถุที่มีการสร้างอินสแตนซ์โดยบุคคลซึ่งไม่มีส่วนเกี่ยวข้องกับ OBJ และ OBJ ไม่เปลี่ยนแปลงแสดงว่าเราระบุบริบทนี้ให้กับบุคคลโดยไม่ต้องมีผล
ดังนั้นจึงสามารถสรุปได้ว่าการใช้การผูกเพื่อระบุสิ่งนี้สำหรับตัวสร้าง เมื่อตัวสร้างใหม่สิ่งนี้ระบุโดยฟังก์ชั่นการผูกจะไม่มีผล
แน่นอนการผูกไม่เพียง แต่สามารถระบุสิ่งนี้ แต่ยังผ่านพารามิเตอร์ ลองดำเนินการนี้:
ฟังก์ชันบุคคล (ชื่อ) {this.name = name; console.log(this);}var obj = { name: 'qiutc2222222'};var Person2 = Person.bind(obj, 'qiutc111111');var p = new Person2('qiutc');// Person {name: "qiutc111111"}อย่างที่คุณเห็นแม้ว่าการระบุสิ่งนี้ไม่ทำงานพารามิเตอร์ที่เข้ามายังคงทำงานอยู่
ระบุสิ่งนี้สำหรับฟังก์ชันลูกศร
มากำหนดฟังก์ชั่นลูกศรภายใต้ทั่วโลกดังนั้นในฟังก์ชั่นลูกศรนี้จะชี้ไปที่วัตถุทั่วโลกอย่างหลีกเลี่ยงไม่ได้ ถ้าสิ่งนี้เปลี่ยนไปโดยใช้วิธีการโทร:
var afoo = (a) => {console.log (a); console.log (นี่);} afoo (1); // 1 // windowvar obj = {ชื่อ: 'qiutc'}; afoo.call (obj, 2); // 2 // หน้าต่างอย่างที่คุณเห็นการดำเนินการของการโทรที่ชี้ไปที่นี่ไม่ประสบความสำเร็จดังนั้นจึงสามารถสรุปได้ ว่าสิ่งนี้ในฟังก์ชั่นลูกศรได้ตัดสินใจแล้วเมื่อกำหนด (ดำเนินการสิ่งนี้ในขอบเขตที่กำหนด) และไม่มีส่วนเกี่ยวข้องกับวิธีการเรียกมัน ไม่มีการดำเนินการรวมถึง (การโทร, ใช้, ผูก) และการดำเนินการอื่น ๆ ไม่สามารถเปลี่ยนแปลงสิ่งนี้ได้
เพียงจำไว้ว่าฟังก์ชั่นลูกศรนั้นดีและสิ่งนี้ยังคงไม่เปลี่ยนแปลง
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น