เหตุใดจึงใช้ Object.prototype.hasOwnProperty.call (myObj, prop) แทน myObj.hasOwnProperty (prop)


107

ถ้าฉันเข้าใจอย่างถูกต้องแต่ละวัตถุใน JavaScript จะสืบทอดมาจาก Object ต้นแบบซึ่งหมายความว่าแต่ละวัตถุใน JavaScript สามารถเข้าถึงฟังก์ชัน hasOwnProperty ผ่านสายโซ่ต้นแบบได้

ในขณะที่อ่านซอร์สโค้ดของRequireJSฉันสะดุดกับฟังก์ชันนี้:

function hasProp(obj, prop) {
    return hasOwn.call(obj, prop);
}

hasOwnObject.prototype.hasOwnPropertyมีการอ้างอิงถึง มีความแตกต่างในทางปฏิบัติในการเขียนฟังก์ชันนี้เป็น

function hasProp(obj, prop) {
    return obj.hasOwnProperty(prop);
}

และเนื่องจากเราอยู่ที่นั่นทำไมเราถึงกำหนดฟังก์ชันนี้เลย? เป็นเพียงคำถามเกี่ยวกับทางลัดและการแคชการเข้าถึงคุณสมบัติในเครื่องเพื่อเพิ่มประสิทธิภาพการทำงาน (เล็กน้อย) หรือฉันพลาดกรณีใด ๆ ที่อาจใช้ hasOwnProperty กับวัตถุที่ไม่มีวิธีนี้หรือไม่?

คำตอบ:


113

มีความแตกต่างในทางปฏิบัติ [ระหว่างตัวอย่างของฉัน] หรือไม่?

ผู้ใช้อาจมีออบเจ็กต์ JavaScript ที่สร้างขึ้นด้วยObject.create(null)ซึ่งจะมีnull [[Prototype]]สายโซ่ดังนั้นจึงไม่มีhasOwnProperty()อยู่ในนั้น การใช้แบบฟอร์มที่สองของคุณจะล้มเหลวในการทำงานด้วยเหตุนี้

นอกจากนี้ยังเป็นการอ้างอิงที่ปลอดภัยกว่าObject.prototype.hasOwnProperty()(และสั้นกว่าด้วย)

คุณคงนึกออกว่าอาจมีคนทำ ...

var someObject = {
    hasOwnProperty: function(lol) {
        return true;
    }
};

ซึ่งจะทำให้hasProp(someObject)ล้มเหลวหากถูกนำไปใช้เช่นตัวอย่างที่สองของคุณ (จะพบวิธีการนั้นโดยตรงบนวัตถุและเรียกใช้สิ่งนั้นแทนที่จะถูกมอบหมายให้Object.prototype.hasOwnProperty)

แต่มีโอกาสน้อยที่จะมีคนลบล้างObject.prototype.hasOwnPropertyข้อมูลอ้างอิง

และเนื่องจากเราอยู่ที่นั่นทำไมเราถึงกำหนดฟังก์ชันนี้เลย?

ดูด้านบน.

เป็นเพียงคำถามเกี่ยวกับทางลัดและการแคชการเข้าถึงคุณสมบัติในเครื่องเพื่อเพิ่มประสิทธิภาพ (เล็กน้อย) ...

อาจทำให้เร็วขึ้นในทางทฤษฎีเนื่องจาก[[Prototype]]ไม่จำเป็นต้องปฏิบัติตามห่วงโซ่ แต่ฉันสงสัยว่าสิ่งนี้จะไม่สำคัญและไม่ใช่เหตุผลที่การนำไปใช้คือเหตุใดจึงเป็นเช่นนั้น

... หรือฉันพลาดกรณีใด ๆ ที่ hasOwnPropertyอาจใช้กับวัตถุที่ไม่มีวิธีนี้?

hasOwnProperty()มีอยู่Object.prototypeแต่สามารถลบล้างได้ ออบเจ็กต์ JavaScript ดั้งเดิมทุกชิ้น (แต่ไม่รับประกันว่าออบเจ็กต์โฮสต์จะทำตามสิ่งนี้โปรดดูคำอธิบายเชิงลึกของ RobG ) มีObject.prototypeเป็นอ็อบเจ็กต์สุดท้ายบนเชนก่อนหน้านี้null(ยกเว้นอ็อบเจ็กต์ที่ส่งคืนโดยObject.create(null))


ตรรกะของคุณน่าจะถูกต้อง แต่ฉันคิดว่าคุณเป็นคนใจดี หากผู้เขียน require.js คิดว่าhasOwnPropertyอาจถูกแทนที่ (ซึ่งไม่น่าเป็นไปได้อย่างยิ่ง) พวกเขาควรเรียกใช้เมธอดในตัวทั้งหมดด้วยวิธีนั้น (อาจทำได้)
RobG

@Periback จริงเหรอ? ฉันค่อนข้างมั่นใจว่ามันรองรับ
alex

ทางลัด ES6 หากใช้บ่อยๆ const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Richard Ayotte

15

ถ้าฉันเข้าใจถูกต้องแต่ละออบเจ็กต์ใน JavaScript จะสืบทอดมาจาก Object ต้นแบบ

อาจดูเหมือนการแบ่งเส้นขน แต่มีความแตกต่างระหว่างJavaScript (คำทั่วไปสำหรับการใช้งาน ECMAScript) และECMAScript (ภาษาที่ใช้สำหรับการใช้งาน JavaScript) เป็น ECMAScript ที่กำหนดโครงร่างการสืบทอดไม่ใช่ JavaScript ดังนั้นเฉพาะอ็อบเจ็กต์ ECMAScript ดั้งเดิมเท่านั้นที่ต้องใช้โครงร่างการสืบทอดนั้น

โปรแกรม JavaScript ที่รันอยู่ประกอบด้วยอ็อบเจ็กต์ ECMAScript ในตัว (Object, Function, Number เป็นต้น) เป็นอย่างน้อยและอาจเป็นอ็อบเจ็กต์ดั้งเดิม (เช่นฟังก์ชัน) นอกจากนี้ยังอาจมีวัตถุโฮสต์บางอย่าง (เช่นวัตถุ DOM ในเบราว์เซอร์หรือวัตถุอื่น ๆ ในสภาพแวดล้อมโฮสต์อื่น ๆ )

แม้ว่าออบเจ็กต์ในตัวและเนทีฟต้องใช้โครงร่างการสืบทอดที่กำหนดไว้ใน ECMA-262 แต่ออบเจ็กต์โฮสต์ไม่ ดังนั้นไม่ทั้งหมดวัตถุในสภาพแวดล้อม JavaScript ต้องรับมรดกจากObject.prototype ตัวอย่างเช่นโฮสต์อ็อบเจ็กต์ใน Internet Explorer ที่นำไปใช้เป็นอ็อบเจ็กต์ActiveXจะทำให้เกิดข้อผิดพลาดหากถือว่าเป็นเนทีฟอ็อบเจ็กต์ (ด้วยเหตุนี้จึงใช้try..catchเพื่อเริ่มต้นอ็อบเจ็กต์Microsoft XMLHttpRequest ) อ็อบเจ็กต์ DOM บางตัว (เช่น NodeLists ใน Internet Explorer ในโหมด quirks) หากส่งผ่านไปยังเมธอด Array จะทำให้เกิดข้อผิดพลาดอ็อบเจ็กต์ DOM ใน Internet Explorer 8 และต่ำกว่าไม่มีโครงร่างการสืบทอดเช่น ECMAScript เป็นต้น

ดังนั้นจึงไม่ควรสันนิษฐานว่าอ็อบเจ็กต์ทั้งหมดในสภาพแวดล้อม JavaScript สืบทอดมาจาก Object.prototype

ซึ่งหมายความว่าแต่ละอ็อบเจ็กต์ใน JavaScript สามารถเข้าถึงฟังก์ชัน hasOwnProperty ผ่านห่วงโซ่ต้นแบบ

ซึ่งไม่เป็นความจริงสำหรับวัตถุโฮสต์บางอย่างใน Internet Explorer ในโหมด quirks (และ Internet Explorer 8 และต่ำกว่าเสมอ) เป็นอย่างน้อย

จากที่กล่าวมาคุณควรพิจารณาว่าเหตุใดวัตถุจึงอาจมีเมธอดhasOwnPropertyของตัวเองและความเหมาะสมในการเรียกใช้เมธอดhasOwnPropertyอื่น ๆแทนโดยไม่ต้องทดสอบก่อนว่าเป็นความคิดที่ดีหรือไม่

ฉันสงสัยว่าสาเหตุของการใช้Object.prototype.hasOwnProperty.callคือในบางเบราว์เซอร์วัตถุโฮสต์ไม่มีเมธอดhasOwnPropertyการใช้การโทรและวิธีการในตัวเป็นอีกทางเลือกหนึ่ง อย่างไรก็ตามการทำเช่นนั้นโดยทั่วไปดูเหมือนจะไม่ใช่ความคิดที่ดีสำหรับเหตุผลที่ระบุไว้ข้างต้น

ในกรณีที่เกี่ยวข้องกับวัตถุโฮสต์สามารถใช้ตัวดำเนินการinเพื่อทดสอบคุณสมบัติโดยทั่วไปเช่น

var o = document.getElementsByTagName('foo');

// false in most browsers, throws an error in Internet Explorer 6, and probably 7 and 8
o.hasOwnProperty('bar');

// false in all browsers
('bar' in o);

// false (in all browsers? Do some throw errors?)
Object.prototype.hasOwnProperty.call(o, 'bar');

ทางเลือกอื่น (ทดสอบในInternet Explorer 6และอื่น ๆ ):

function ownProp(o, prop) {

  if ('hasOwnProperty' in o) {
    return o.hasOwnProperty(prop);

  } else {
    return Object.prototype.hasOwnProperty.call(o, prop);
  }
}

ด้วยวิธีนี้คุณจะเรียกใช้hasOwnPropertyในตัวโดยเฉพาะซึ่งอ็อบเจ็กต์ไม่มี (สืบทอดหรืออย่างอื่น)

อย่างไรก็ตามหากออบเจ็กต์ไม่มีhasOwnPropertyเมธอดก็น่าจะเหมาะสมที่จะใช้อินโอเปอเรเตอร์เนื่องจากอ็อบเจกต์น่าจะไม่มีโครงร่างการสืบทอดและคุณสมบัติทั้งหมดอยู่บนอ็อบเจกต์ (นั่นเป็นเพียงข้อสันนิษฐานเท่านั้น) เช่นในตัวดำเนินการเป็นวิธีทดสอบทั่วไป (และดูเหมือนจะประสบความสำเร็จ) สำหรับการสนับสนุนวัตถุ DOM สำหรับคุณสมบัติ


ขอบคุณ. Object.prototype.hasOwnProperty.call (o, 'bar') ไม่ทำงานใน FF 18.0 (อย่างน้อยก็ในกรณีของฉัน) ดังนั้นฉันจึงตัดสินใจใช้ ('bar' ใน o) - และมันช่วยได้
สูงสุด

@ แม็กซ์inไม่ทำการhasOwnProperty()ค้นหาฉันสงสัยว่าคุณสมบัติที่คุณกำลังมองหามีอยู่ในห่วงโซ่ต้นแบบ
alex

นี่เป็นตัวอย่างที่น่าสนใจจากeslint.org/docs/rules/no-prototype-builtins : ตัวอย่างเช่นเว็บเซิร์ฟเวอร์จะไม่ปลอดภัยในการแยกวิเคราะห์อินพุต JSON จากไคลเอนต์และเรียกhasOwnPropertyใช้อ็อบเจ็กต์ที่เป็นผลลัพธ์โดยตรงเนื่องจากไคลเอ็นต์ที่เป็นอันตรายอาจ ส่งค่า JSON เช่น{"hasOwnProperty": 1}และทำให้เซิร์ฟเวอร์หยุดทำงาน
naught101

แน่นอน แต่ก็เป็นการดีที่จะทดสอบหรือตรวจสอบความถูกต้องของ JSON ที่ไคลเอ็นต์จัดหาด้วยสคีมา JSON เพื่อป้องกันปัญหาดังกล่าวแม้ว่าข้อกังวลของคุณจะเป็นเพียงคุณภาพของข้อมูล และไม่ควรทำให้เซิร์ฟเวอร์พัง :-)
RobG

8

JavaScript ไม่ป้องกันชื่อคุณสมบัติhasOwnProperty

หากมีความเป็นไปได้ที่อ็อบเจ็กต์อาจมีคุณสมบัติที่มีชื่อนี้จำเป็นต้องใช้ hasOwnProperty ภายนอกเพื่อให้ได้ผลลัพธ์ที่ถูกต้อง:

คุณสามารถคัดลอกและวางข้อมูลโค้ดด้านล่างลงในคอนโซลเบราว์เซอร์ของคุณเพื่อทำความเข้าใจได้ดียิ่งขึ้น

var foo = {
  hasOwnProperty: function() {
    return false;
  },
  bar: 'I belong to foo'
};

ส่งคืนเท็จเสมอ

foo.hasOwnProperty('bar'); // false

ใช้ hasOwnProperty ของ Object อื่นและเรียกมันด้วยชุดนี้ว่า foo

({}).hasOwnProperty.call(foo, 'bar'); // true

นอกจากนี้ยังสามารถใช้คุณสมบัติ hasOwnProperty จากObjectต้นแบบเพื่อจุดประสงค์นี้

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true

1
จุดที่คุณจะทำให้ได้รับการทำในคำตอบที่ได้รับการยอมรับ , ยกเว้นว่ามีการแทนที่ของผลตอบแทนhasOwnProperty true
Louis

1

ข้อมูลที่ให้ไว้ในคำตอบสองข้อแรก (ตามวันที่) เป็นข้อมูลเฉพาะจุด อย่างไรก็ตามการใช้:

('propertyName' in obj)

ได้รับการกล่าวถึงสองสามครั้ง ควรสังเกตว่าhasOwnPropertyการนำไปใช้งานจะคืนค่าจริงก็ต่อเมื่อคุณสมบัตินั้นอยู่ในวัตถุที่กำลังทดสอบโดยตรง

inผู้ประกอบการจะตรวจสอบลงผ่านห่วงโซ่ต้นแบบเกินไป

ซึ่งหมายความว่าคุณสมบัติของอินสแตนซ์จะคืนค่าจริงเมื่อส่งผ่านไปยังhasOwnPropertyตำแหน่งที่คุณสมบัติต้นแบบจะส่งกลับเท็จ

การใช้ตัวinดำเนินการทั้งอินสแตนซ์และคุณสมบัติต้นแบบจะคืนค่าจริง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.