เพื่อทำความเข้าใจกับสิ่งนี้ตามที่ตั้งอยู่สถานการณ์สามารถแบ่งออกเป็นสามประเภท:
1. ในฟังก์ชั่น: นี่มักจะเป็นพารามิเตอร์โดยนัย
2. นอกฟังก์ชั่น (ในขอบเขตด้านบน): ในเบราว์เซอร์สิ่งนี้หมายถึงวัตถุทั่วโลก; ใน node.js หมายถึงการส่งออกของโมดูล
3. สตริงส่งผ่านไปยัง eval (): ถ้า eval () เรียกโดยตรงสิ่งนี้หมายถึงวัตถุปัจจุบัน; ถ้า evals () เรียกว่าทางอ้อมสิ่งนี้หมายถึงวัตถุทั่วโลก
เราได้ทำการทดสอบที่สอดคล้องกันสำหรับหมวดหมู่เหล่านี้:
1. สิ่งนี้ในฟังก์ชั่น
ฟังก์ชั่นสามารถแสดงถึงโครงสร้างที่เรียกทั้งหมดใน JS ดังนั้นนี่จึงเป็นสถานการณ์ที่พบบ่อยที่สุดที่ใช้และฟังก์ชั่นสามารถแบ่งย่อยเป็นสามบทบาทต่อไปนี้:
ฟังก์ชั่นจริง
ตัวสร้าง
วิธี
1.1 สิ่งนี้ในฟังก์ชั่นจริง
ในฟังก์ชั่นจริงค่าของสิ่งนี้เป็นรูปแบบที่ขึ้นอยู่กับบริบทที่ตั้งอยู่
โหมด Sloppy: นี่หมายถึงวัตถุทั่วโลก (หน้าต่างในเบราว์เซอร์)
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น sloppyfunc () {
console.log (หน้าต่างนี้ ===); // จริง
-
Sloppyfunc ();
โหมดที่เข้มงวด: ค่าของสิ่งนี้ไม่ได้กำหนด
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่นเข้มงวด func () {
'ใช้อย่างเข้มงวด';
console.log (สิ่งนี้ === ไม่ได้กำหนด); // จริง
-
เข้มงวด func ();
นี่คือพารามิเตอร์โดยนัยของฟังก์ชันดังนั้นค่าของมันจะเหมือนกันเสมอ อย่างไรก็ตามคุณสามารถกำหนดค่านี้ได้โดยใช้วิธีการโทร () หรือใช้ () เพื่อแสดงค่า
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น func (arg1, arg2) {
console.log (นี่); // 1
console.log (arg1); // 2
console.log (arg2); // 3
-
func.call (1, 2, 3); // (นี่, arg1, arg2)
func.apply (1, [2, 3]); // (นี่คือ arraywithargs)
1.2 สิ่งนี้ในตัวสร้าง
คุณสามารถใช้ฟังก์ชั่นเป็นตัวสร้างผ่านใหม่ การดำเนินการใหม่สร้างวัตถุใหม่และส่งผ่านวัตถุนี้ไปยังตัวสร้างผ่านสิ่งนี้
การคัดลอกรหัสมีดังนี้:
var บันทึกสิ่งนี้;
ฟังก์ชั่น cronst () {
บันทึกนี้ = สิ่งนี้;
-
var inst = new const ();
console.log (บันทึกนี้ === Inst); // จริง
หลักการการใช้งานของการดำเนินการใหม่ใน JS นั้นแสดงอย่างคร่าวๆในรหัสต่อไปนี้ (ดูที่นี่สำหรับการใช้งานที่แม่นยำยิ่งขึ้นการใช้งานนี้มีความซับซ้อนมากขึ้น):
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น newOperator (ข้อ จำกัด , arraywithargs) {
var thisvalue = object.create (constr.prototype);
constr.apply (thisvalue, arraywithargs);
คืนค่านี้;
-
1.3 สิ่งนี้ในวิธีการ
ในวิธีการการใช้งานนี้มีแนวโน้มที่จะเป็นภาษาที่มุ่งเน้นวัตถุแบบดั้งเดิมมากขึ้น: ตัวรับสัญญาณชี้ไปที่สิ่งนี้คือวัตถุที่มีวิธีนี้
การคัดลอกรหัสมีดังนี้:
var obj = {
วิธี: ฟังก์ชัน () {
console.log (this === obj); // จริง
-
-
obj.method ();
2. สิ่งนี้อยู่ในขอบเขต
ในเบราว์เซอร์ขอบเขตคือขอบเขตทั่วโลกและสิ่งนี้หมายถึงวัตถุระดับโลกนี้ (เช่นหน้าต่าง):
การคัดลอกรหัสมีดังนี้:
<script>
console.log (หน้าต่างนี้ ===); // จริง
</script>
ใน node.js คุณมักจะเรียกใช้ฟังก์ชั่นในโมดูล ดังนั้นขอบเขตระดับบนสุดจึงเป็นขอบเขตโมดูลที่พิเศษมาก:
การคัดลอกรหัสมีดังนี้:
// `Global` (ไม่ใช่` window ') อ้างถึงวัตถุทั่วโลก:
console.log (คณิตศาสตร์ === global.math); // จริง
// `สิ่งนี้ 'ไม่ได้อ้างถึงวัตถุทั่วโลก:
console.log (นี่! == ทั่วโลก); // จริง
// `นี่คือหมายถึงการส่งออกของโมดูล:
console.log (this === module.exports); // จริง
3. สิ่งนี้ใน eval ()
evaln () สามารถเรียกได้โดยตรง (โดยเรียกชื่อฟังก์ชั่น 'eval') หรือทางอ้อม (โดยวิธีอื่นเช่นการโทร ()) สำหรับรายละเอียดเพิ่มเติมโปรดดูที่นี่
การคัดลอกรหัสมีดังนี้:
// ฟังก์ชั่นจริง
ฟังก์ชั่น sloppyfunc () {
console.log (eval ('this') === หน้าต่าง); // จริง
-
Sloppyfunc ();
ฟังก์ชั่นเข้มงวด func () {
'ใช้อย่างเข้มงวด';
console.log (eval ('this') === ไม่ได้กำหนด); // จริง
-
เข้มงวด func ();
// ตัวสร้าง
var บันทึกสิ่งนี้;
ฟังก์ชั่น cronst () {
บันทึกนี้ = eval ('this');
-
var inst = new const ();
console.log (บันทึกนี้ === Inst); // จริง
// วิธีการ
var obj = {
วิธี: ฟังก์ชัน () {
console.log (eval ('this') === obj); // จริง
-
-
obj.method ();
4. กับดักที่เกี่ยวข้องกับสิ่งนี้
คุณควรระวังเกี่ยวกับกับดัก 3 ประการที่เกี่ยวข้องกับสิ่งนี้ที่จะนำเสนอด้านล่าง โปรดทราบว่าในตัวอย่างต่อไปนี้การใช้โหมดที่เข้มงวดสามารถปรับปรุงความปลอดภัยของรหัส เนื่องจากในฟังก์ชั่นจริงค่าของสิ่งนี้ไม่ได้กำหนดคุณจะได้รับคำเตือนเมื่อมีบางอย่างผิดปกติ
4.1 ลืมที่จะใช้ใหม่
หากคุณไม่ได้ใช้ใหม่เพื่อเรียกตัวสร้างคุณกำลังใช้ฟังก์ชั่นจริง ดังนั้นนี่จะไม่เป็นคุณค่าที่คุณคาดหวัง ในโหมดเลอะเทอะจุดนี้ไปที่หน้าต่างและคุณจะสร้างตัวแปรทั่วโลก:
การคัดลอกรหัสมีดังนี้:
จุดฟังก์ชัน (x, y) {
this.x = x;
this.y = y;
-
var p = จุด (7, 5); // เราลืมใหม่!
console.log (p === ไม่ได้กำหนด); // จริง
// ตัวแปรทั่วโลกได้ถูกสร้างขึ้น:
console.log (x); // 7
console.log (y); // 5
อย่างไรก็ตามหากคุณใช้โหมดที่เข้มงวดคุณจะยังคงได้รับคำเตือน (สิ่งนี้ === ไม่ได้กำหนด):
การคัดลอกรหัสมีดังนี้:
จุดฟังก์ชัน (x, y) {
'ใช้อย่างเข้มงวด';
this.x = x;
this.y = y;
-
var p = จุด (7, 5);
// typeError: ไม่สามารถตั้งค่าคุณสมบัติ 'x' ของ undefined
4.2 วิธีการใช้งานที่ไม่เหมาะสม
หากคุณได้รับค่าโดยตรงของวิธีการ (ไม่เรียกมัน) คุณกำลังใช้วิธีนี้เป็นฟังก์ชัน เมื่อคุณต้องการผ่านวิธีการเป็นพารามิเตอร์ลงในฟังก์ชั่นหรือวิธีการเรียกคุณมักจะทำเช่นนี้ นี่เป็นกรณีของ SetTimeOut () และตัวจัดการเหตุการณ์การลงทะเบียน ฉันจะใช้เมธอด callit () เพื่อจำลองสถานการณ์นี้:
การคัดลอกรหัสมีดังนี้:
/** คล้ายกับ settimeout () และ setimmediate ()*/
ฟังก์ชั่น callit (func) {
func ();
-
หากคุณเรียกวิธีการเป็นฟังก์ชั่นในโหมดเลอะเทอะ * สิ่งนี้ * จะชี้ไปที่วัตถุทั่วโลกดังนั้นสิ่งที่สร้างขึ้นในภายหลังจะเป็นตัวแปรทั่วโลก
การคัดลอกรหัสมีดังนี้:
var counter = {
นับ: 0,
// วิธีโหมดเลอะเทอะ
inc: function () {
this.count ++;
-
-
callit (counter.inc);
// ไม่ได้ผล:
console.log (counter.count); // 0
// แทนตัวแปรส่วนกลางถูกสร้างขึ้น
// (NAN เป็นผลมาจากการใช้ ++ กับ undefined):
console.log (นับ); // น่าน
หากคุณทำสิ่งนี้ในโหมดที่เข้มงวดสิ่งนี้จะไม่ได้กำหนดและคุณยังไม่ได้รับผลลัพธ์ที่ต้องการ แต่อย่างน้อยคุณจะได้รับคำเตือน:
การคัดลอกรหัสมีดังนี้:
var counter = {
นับ: 0,
// วิธีโหมดที่เข้มงวด
inc: function () {
'ใช้อย่างเข้มงวด';
this.count ++;
-
-
callit (counter.inc);
// typeError: ไม่สามารถอ่านคุณสมบัติ 'นับ' ของ undefined
console.log (counter.count);
เพื่อให้ได้ผลลัพธ์ที่คาดหวังคุณสามารถใช้ bind ():
การคัดลอกรหัสมีดังนี้:
var counter = {
นับ: 0,
inc: function () {
this.count ++;
-
-
callit (counter.inc.bind (เคาน์เตอร์));
// มันใช้งานได้!
console.log (counter.count); // 1
bind () สร้างฟังก์ชั่นอื่นที่สามารถตั้งค่านี้เพื่อตอบโต้
4.3 ซ่อนสิ่งนี้
เมื่อคุณใช้ฟังก์ชั่นในวิธีการคุณมักจะเพิกเฉยต่อฟังก์ชั่นนั้นมีสิ่งนี้ สิ่งนี้แตกต่างจากวิธีการดังนั้นคุณจึงไม่สามารถผสมสองสิ่งนี้เข้าด้วยกัน สำหรับรายละเอียดโปรดดูรหัสต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
var obj = {
ชื่อ: 'Jane',
เพื่อน: ['Tarzan', 'Cheeta'],
ลูป: ฟังก์ชัน () {
'ใช้อย่างเข้มงวด';
this.friends.foreach (
ฟังก์ชั่น (เพื่อน) {
console.log (this.name+'รู้'+เพื่อน);
-
-
-
-
obj.loop ();
// typeError: ไม่สามารถอ่านคุณสมบัติ 'ชื่อ' ของ undefined
this.name ในฟังก์ชั่นในตัวอย่างข้างต้นไม่สามารถใช้ได้เนื่องจากค่าของฟังก์ชันนี้ไม่ได้กำหนดซึ่งแตกต่างจากสิ่งนี้ในวิธีการวนซ้ำ () ต่อไปนี้เป็นสามแนวคิดในการแก้ปัญหานี้:
1. นั่น = สิ่งนี้กำหนดสิ่งนี้ให้กับตัวแปรดังนั้นสิ่งนี้จะปรากฏอย่างชัดเจน (ยกเว้นว่าตัวเองยังเป็นชื่อตัวแปรทั่วไปที่ใช้ในการจัดเก็บสิ่งนี้) จากนั้นใช้ตัวแปรนั้น:
การคัดลอกรหัสมีดังนี้:
ลูป: ฟังก์ชัน () {
'ใช้อย่างเข้มงวด';
var that = this;
this.friends.foreach (ฟังก์ชั่น (เพื่อน) {
console.log (that.name+'ความรู้'+เพื่อน);
-
-
2. ผูก () ใช้ bind () เพื่อสร้างฟังก์ชั่น ฟังก์ชั่นนี้มักจะมีค่าที่คุณต้องการผ่าน (ในตัวอย่างต่อไปนี้วิธีนี้):
การคัดลอกรหัสมีดังนี้:
ลูป: ฟังก์ชัน () {
'ใช้อย่างเข้มงวด';
this.friends.foreach (ฟังก์ชั่น (เพื่อน) {
console.log (this.name+'รู้'+เพื่อน);
}. -bind (นี่));
-
3. ใช้พารามิเตอร์ที่สองของ foreach พารามิเตอร์ที่สองของ foreach จะถูกส่งผ่านไปยังฟังก์ชั่นการโทรกลับและใช้เป็นฟังก์ชันการโทรกลับ
การคัดลอกรหัสมีดังนี้:
ลูป: ฟังก์ชัน () {
'ใช้อย่างเข้มงวด';
this.friends.foreach (ฟังก์ชั่น (เพื่อน) {
console.log (this.name+'รู้'+เพื่อน);
}, นี้);
-
5. แนวปฏิบัติที่ดีที่สุด
ในทางทฤษฎีฉันคิดว่าฟังก์ชั่นจริงไม่ได้เป็นของตัวเองและวิธีการแก้ปัญหาข้างต้นก็ขึ้นอยู่กับความคิดนี้ Ecmascript 6 ใช้ฟังก์ชันลูกศรเพื่อให้ได้เอฟเฟกต์นี้ซึ่งเป็นฟังก์ชั่นที่ไม่มีสิ่งนี้ ในฟังก์ชั่นดังกล่าวคุณสามารถใช้สิ่งนี้ได้ตามความประสงค์โดยไม่ต้องกังวลว่าจะมีอยู่โดยนัยหรือไม่
การคัดลอกรหัสมีดังนี้:
ลูป: ฟังก์ชัน () {
'ใช้อย่างเข้มงวด';
// พารามิเตอร์ของ foreach () เป็นฟังก์ชันลูกศร
this.friends.foreach (เพื่อน => {
// `นี่คือลูป '
console.log (this.name+'รู้'+เพื่อน);
-
-
ฉันไม่ชอบ API บางอย่างที่ถือว่านี่เป็นพารามิเตอร์เพิ่มเติมสำหรับฟังก์ชั่นจริง:
การคัดลอกรหัสมีดังนี้:
ก่อนหน้า (ฟังก์ชั่น () {
this.addmatchers ({
TobeInRange: ฟังก์ชั่น (เริ่มต้นจบ) {
-
-
-
-
การเขียนพารามิเตอร์โดยนัยตามที่ผ่านอย่างชัดเจนรหัสจะดูดีกว่าที่จะเข้าใจและนี่สอดคล้องกับข้อกำหนดของฟังก์ชันลูกศร:
การคัดลอกรหัสมีดังนี้:
ก่อนหน้า (api => {
api.addmatchers ({
TobeInRange (เริ่มต้นจบ) {
-
-
-
-