ความแตกต่างของขอบเขตมรดกต้นแบบ / ต้นแบบใน AngularJS คืออะไร?


1028

อ้างอิง API หน้าขอบเขตพูดว่า:

ขอบเขตสามารถสืบทอดจากขอบเขตพาเรนต์

ผู้พัฒนาคู่มือหน้าขอบเขตพูดว่า:

ขอบเขต (ต้นแบบ) สืบทอดคุณสมบัติจากขอบเขตพาเรนต์

  • ดังนั้นขอบเขตของลูกจะสืบทอดต้นแบบจากขอบเขตหลักเสมอหรือไม่?
  • มีข้อยกเว้นหรือไม่
  • เมื่อมันรับช่วงมันจะสืบทอดมรดกต้นแบบ JavaScript ปกติหรือไม่

คำตอบ:


1740

คำตอบด่วน :
โดยปกติขอบเขตลูกมักจะสืบทอดจากแม่ลูก แต่ไม่เสมอไป ข้อยกเว้นประการหนึ่งสำหรับกฎนี้คือคำสั่งด้วยscope: { ... }- สิ่งนี้จะสร้างขอบเขต "แยก" ที่ไม่ได้รับช่วงต้นแบบ โครงสร้างนี้มักจะใช้เมื่อสร้างคำสั่ง "องค์ประกอบที่ใช้ซ้ำได้"

สำหรับความแตกต่างการสืบทอดขอบเขตมักจะตรงไปตรงมา ... จนกว่าคุณจะต้องการการผูกข้อมูลแบบสองทาง (เช่นองค์ประกอบของฟอร์ม, ng-model) ในขอบเขตลูก Ng-repeat, ng-switch และ ng-include สามารถเดินทางไปหาคุณได้ถ้าคุณพยายามที่จะผูกกับดั้งเดิม (เช่นตัวเลขสตริงบูลีน) ในขอบเขตหลักจากภายในขอบเขตลูก มันไม่ทำงานอย่างที่คนส่วนใหญ่คาดหวัง ขอบเขตลูกได้รับคุณสมบัติของตนเองที่ซ่อน / เงาคุณสมบัติหลักที่มีชื่อเดียวกัน วิธีแก้ไขปัญหาของคุณคือ

  1. กำหนดอ็อบเจ็กต์ในพาเรนต์สำหรับโมเดลของคุณจากนั้นอ้างอิงคุณสมบัติของอ็อบเจ็กต์นั้นใน child: parentObj.someProp
  2. ใช้ $ parent.parentScopeProperty (ไม่เสมอไปได้ แต่ง่ายกว่า 1. ที่เป็นไปได้)
  3. กำหนดฟังก์ชันบนขอบเขตพาเรนต์และเรียกใช้จาก child (ไม่สามารถทำได้)

นักพัฒนา AngularJS ใหม่มักจะไม่ทราบว่าng-repeat, ng-switch, ng-view, ng-includeและng-ifทุกคนสร้างขอบเขตเด็กใหม่ดังนั้นปัญหาที่เกิดขึ้นมักจะแสดงขึ้นเมื่อสั่งเหล่านี้มีส่วนร่วม (ดูตัวอย่างนี้สำหรับภาพประกอบอย่างรวดเร็วของปัญหา)

ปัญหานี้ด้วย primitives สามารถหลีกเลี่ยงได้อย่างง่ายดายโดยทำตาม "แนวปฏิบัติที่ดีที่สุด" ซึ่งมักจะมี '.' ใน ng-models ของคุณ - รับชมได้ 3 นาที Misko ng-switchแสดงให้เห็นถึงปัญหาที่มีผลผูกพันดั้งเดิมด้วย

มี '.' ในแบบจำลองของคุณจะทำให้มั่นใจได้ว่ามรดกต้นแบบนั้นกำลังเล่นอยู่ ดังนั้นใช้

<input type="text" ng-model="someObj.prop1">

<!--rather than
<input type="text" ng-model="prop1">`
-->


คำตอบยาว :

การสร้างต้นแบบ JavaScript

วางบน AngularJS wiki ด้วย: https://github.com/angular/angular.js/wiki/Understanding-Scopes

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

สมมติว่า parentScope มีคุณสมบัติ aString, aNumber, anArray, anObject และ aFunction ถ้า childScope prototypically สืบทอดมาจาก parentScope เรามี:

มรดกต้นแบบ

(โปรดทราบว่าเพื่อประหยัดพื้นที่ฉันแสดงanArrayวัตถุเป็นวัตถุสีน้ำเงินเดียวที่มีค่าสามค่าแทนที่จะเป็นวัตถุสีน้ำเงินเดียวที่มีตัวอักษรสีเทาสามตัวแยกกัน)

หากเราพยายามเข้าถึงคุณสมบัติที่กำหนดไว้ใน parentScope จากขอบเขตลูกก่อน JavaScript จะค้นหาในขอบเขตลูกไม่พบคุณสมบัติจากนั้นค้นหาในขอบเขตที่สืบทอดและค้นหาคุณสมบัติ (หากไม่พบคุณสมบัติใน parentScope มันจะดำเนินการต่อไปในห่วงโซ่ต้นแบบ ... ไปจนถึงขอบเขตรูท) ดังนั้นทั้งหมดนี้เป็นจริง:

childScope.aString === 'parent string'
childScope.anArray[1] === 20
childScope.anObject.property1 === 'parent prop1'
childScope.aFunction() === 'parent output'

สมมติว่าเราทำเช่นนี้:

childScope.aString = 'child string'

โซ่ต้นแบบไม่ได้รับการพิจารณาและคุณสมบัติ aString ใหม่จะถูกเพิ่มเข้าไปใน childScope คุณสมบัติใหม่นี้ซ่อน / เงาคุณสมบัติ parentScope ด้วยชื่อเดียวกัน สิ่งนี้จะสำคัญมากเมื่อเราพูดถึง ng-repeat และ ng-include ด้านล่าง

การซ่อนคุณสมบัติ

สมมติว่าเราทำเช่นนี้:

childScope.anArray[1] = '22'
childScope.anObject.property1 = 'child prop1'

มีการพิจารณาสายโซ่ต้นแบบเนื่องจากไม่พบวัตถุ (anArray และ anObject) ใน childScope พบวัตถุใน parentScope และมีการอัพเดทค่าคุณสมบัติบนวัตถุต้นฉบับ ไม่มีการเพิ่มคุณสมบัติใหม่ลงใน childScope ไม่มีการสร้างวัตถุใหม่ (โปรดทราบว่าในอาร์เรย์และฟังก์ชัน JavaScript ยังเป็นวัตถุด้วย)

ติดตามห่วงโซ่ต้นแบบ

สมมติว่าเราทำเช่นนี้:

childScope.anArray = [100, 555]
childScope.anObject = { name: 'Mark', country: 'USA' }

ไม่ได้ศึกษาห่วงโซ่ต้นแบบและขอบเขตลูกจะได้รับคุณสมบัติวัตถุใหม่สองรายการที่ซ่อน / เงาคุณสมบัติวัตถุ parentScope ด้วยชื่อเดียวกัน

การซ่อนคุณสมบัติมากขึ้น

ประเด็น:

  • หากเราอ่าน childScope.propertyX และ childScope มีคุณสมบัติ X ดังนั้นเชนต้นแบบจะไม่ได้รับการพิจารณา
  • ถ้าเราตั้งค่า childScope.propertyX เชนต้นแบบจะไม่ได้รับการพิจารณา

หนึ่งสถานการณ์สุดท้าย:

delete childScope.anArray
childScope.anArray[1] === 22  // true

เราลบคุณสมบัติ childScope ก่อนจากนั้นเมื่อเราพยายามเข้าถึงคุณสมบัติอีกครั้งเราจะพิจารณาถึงห่วงโซ่ต้นแบบ

หลังจากลบคุณสมบัติย่อย


การสืบทอดขอบเขตเชิงมุม

ผู้แข่งขัน:

  • ต่อไปนี้จะสร้างขอบเขตใหม่และสืบทอด prototypically: NG-ซ้ำ NG-รวมถึง ng สวิทช์, NG-ควบคุมสั่งกับสั่งด้วยscope: truetransclude: true
  • ต่อไปนี้จะสร้างขอบเขตใหม่ซึ่งไม่ได้รับมรดก prototypically: scope: { ... }สั่งด้วย สิ่งนี้จะสร้างขอบเขต "แยก" แทน

หมายเหตุโดยค่าเริ่มต้นสั่งไม่ได้สร้างขอบเขตใหม่ - scope: falseคือเริ่มต้นคือ

NG- ได้แก่

สมมติว่าเรามีตัวควบคุมของเรา:

$scope.myPrimitive = 50;
$scope.myObject    = {aNumber: 11};

และใน HTML ของเรา:

<script type="text/ng-template" id="/tpl1.html">
<input ng-model="myPrimitive">
</script>
<div ng-include src="'/tpl1.html'"></div>

<script type="text/ng-template" id="/tpl2.html">
<input ng-model="myObject.aNumber">
</script>
<div ng-include src="'/tpl2.html'"></div>

แต่ละ ng-include จะสร้างขอบเขตลูกใหม่ซึ่งแม่แบบสืบทอดมาจากขอบเขตแม่

ng- รวมขอบเขตเด็ก

การพิมพ์ (พูด "77") ลงในกล่องข้อความอินพุตแรกทำให้ขอบเขตลูกได้รับmyPrimitiveคุณสมบัติขอบเขตใหม่ที่ซ่อน / เงาคุณสมบัติขอบเขตของพาเรนต์ที่มีชื่อเดียวกัน นี่อาจไม่ใช่สิ่งที่คุณต้องการ / คาดหวัง

NG- รวมกับดั้งเดิม

การพิมพ์ (พูด "99") ลงในกล่องข้อความอินพุตที่สองไม่ส่งผลให้คุณสมบัติลูกใหม่ เนื่องจาก tpl2.html ผูกโมเดลเข้ากับคุณสมบัติของวัตถุการสืบทอดต้นแบบจะเริ่มขึ้นเมื่อ ngModel ค้นหา object myObject - พบในขอบเขตของพาเรนต์

NG- รวมกับวัตถุ

เราสามารถเขียนเทมเพลตแรกเพื่อใช้ $ parent ถ้าเราไม่ต้องการเปลี่ยนโมเดลจากแบบดั้งเดิมเป็นวัตถุ:

<input ng-model="$parent.myPrimitive">

การพิมพ์ (พูด "22") ลงในกล่องข้อความอินพุตนี้ไม่ได้ส่งผลให้คุณสมบัติลูกใหม่ ตอนนี้โมเดลถูกโยงกับคุณสมบัติของขอบเขตพาเรนต์ (เนื่องจาก $ parent เป็นคุณสมบัติขอบเขตลูกที่อ้างอิงขอบเขตพาเรนต์)

ng-include กับ $ parent

สำหรับขอบเขตทั้งหมด (ต้นแบบหรือไม่) Angular จะติดตามความสัมพันธ์พาเรนต์ - ลูก (เช่นลำดับชั้น) เสมอผ่านคุณสมบัติขอบเขต $ parent, $$ childHead และ $$ childTail ปกติแล้วฉันจะไม่แสดงคุณสมบัติขอบเขตเหล่านี้ในไดอะแกรม

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

// in the parent scope
$scope.setMyPrimitive = function(value) {
     $scope.myPrimitive = value;
}

นี่คือซอตัวอย่างที่ใช้วิธีการ "ฟังก์ชั่นผู้ปกครอง" นี้ (ซอเขียนขึ้นโดยเป็นส่วนหนึ่งของคำตอบนี้: https://stackoverflow.com/a/14104318/215945 )

ดูเพิ่มเติมhttps://stackoverflow.com/a/13782671/215945และhttps://github.com/angular/angular.js/issues/1267

NG-สวิทช์

การสืบทอดขอบเขตของ ng-switch ทำงานเหมือน ng-include ดังนั้นหากคุณต้องการผูกข้อมูลแบบ 2 ทางกับขอบเขตดั้งเดิมในขอบเขตพาเรนต์ให้ใช้ $ parent หรือเปลี่ยนรูปแบบเป็นวัตถุแล้วผูกเข้ากับคุณสมบัติของวัตถุนั้น สิ่งนี้จะหลีกเลี่ยงการซ่อนขอบเขตลูก / แชโดว์ของคุณสมบัติขอบเขตหลัก

ดูAngularJS ผูกขอบเขตของตัวเรือนสวิตช์หรือไม่

NG-ซ้ำ

Ng-repeat ทำงานแตกต่างกันเล็กน้อย สมมติว่าเรามีตัวควบคุมของเรา:

$scope.myArrayOfPrimitives = [ 11, 22 ];
$scope.myArrayOfObjects    = [{num: 101}, {num: 202}]

และใน HTML ของเรา:

<ul><li ng-repeat="num in myArrayOfPrimitives">
       <input ng-model="num">
    </li>
<ul>
<ul><li ng-repeat="obj in myArrayOfObjects">
       <input ng-model="obj.num">
    </li>
<ul>

สำหรับแต่ละรายการ / ซ้ำ ng ซ้ำสร้างขอบเขตใหม่ซึ่ง prototypically สืบทอดจากขอบเขตแม่แต่ก็ยังกำหนดมูลค่าของรายการไปยังสถานที่ให้บริการใหม่ในขอบเขตเด็กใหม่ (ชื่อของคุณสมบัติใหม่คือชื่อของตัวแปรลูป) นี่คือสิ่งที่ซอร์สโค้ด Angular สำหรับ ng-repeat จริงคือ:

childScope = scope.$new();  // child scope prototypically inherits from parent scope
...
childScope[valueIdent] = value;  // creates a new childScope property

หากรายการนั้นเป็นแบบดั้งเดิม (เช่นใน myArrayOfPrimitives) โดยพื้นฐานแล้วสำเนาของค่าจะถูกกำหนดให้กับคุณสมบัติขอบเขตลูกใหม่ การเปลี่ยนค่าของคุณสมบัติขอบเขตขอบเขตลูก (เช่นใช้ ng-model ขอบเขตขอบเขตลูกnum) จะไม่เปลี่ยนอาร์เรย์ที่อ้างอิงขอบเขตขอบเขตหลัก ดังนั้นในการทำซ้ำ ng แรกข้างต้นแต่ละขอบเขตของลูกจะได้รับnumคุณสมบัติที่ไม่ขึ้นอยู่กับอาร์เรย์ myArrayOfPrimitives:

ทำซ้ำกับดั้งเดิม

การทำซ้ำ ng นี้จะไม่ทำงาน (เช่นคุณต้องการ / คาดหวังว่าจะ) การพิมพ์ลงในกล่องข้อความจะเปลี่ยนค่าในกล่องสีเทาซึ่งจะปรากฏเฉพาะในขอบเขตลูก สิ่งที่เราต้องการสำหรับอินพุตจะมีผลกับอาร์เรย์ myArrayOfPrimitives ไม่ใช่คุณสมบัติดั้งเดิมของขอบเขตลูก เพื่อให้บรรลุสิ่งนี้เราจำเป็นต้องเปลี่ยนรูปแบบให้เป็นอาร์เรย์ของวัตถุ

ดังนั้นถ้ารายการเป็นวัตถุการอ้างอิงไปยังวัตถุต้นฉบับ (ไม่ใช่สำเนา) จะถูกกำหนดให้กับคุณสมบัติขอบเขตลูกใหม่ การเปลี่ยนค่าของคุณสมบัติขอบเขตลูก (เช่นการใช้ ng-model ดังนั้นobj.num) จะเปลี่ยนวัตถุที่การอ้างอิงขอบเขตหลัก ดังนั้นในซ้ำ ng ที่สองข้างต้นเรามี:

ทำซ้ำกับวัตถุ

(ฉันวาดสีเทาหนึ่งบรรทัดเพื่อให้ชัดเจนว่ากำลังจะเกิดอะไรขึ้น)

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

ดูความยากลำบากด้วย ng-model, ng-repeat และอินพุตและ https://stackoverflow.com/a/13782671/215945

NG-ควบคุม

ตัวควบคุมการซ้อนที่ใช้ ng-controller ส่งผลให้เกิดการสืบทอดต้นแบบปกติเช่นเดียวกับ ng-include และ ng-switch ดังนั้นจึงใช้เทคนิคเดียวกัน อย่างไรก็ตาม "มันถือว่าเป็นรูปแบบที่ไม่ดีสำหรับผู้ควบคุมสองคนที่จะแบ่งปันข้อมูลผ่านการสืบทอด $ scope" - http://onehungrymind.com/angularjs-sticky-notes-ptes-pt-1-architecture/ บริการควรใช้ข้อมูลร่วมกันระหว่าง ตัวควบคุมแทน

(หากคุณต้องการแบ่งปันข้อมูลผ่านการสืบทอดขอบเขตตัวควบคุมจริงๆคุณไม่จำเป็นต้องทำอะไรขอบเขตลูกจะสามารถเข้าถึงคุณสมบัติขอบเขตหลักทั้งหมดดูลำดับการโหลดตัวควบคุมต่างกันเมื่อโหลดหรือนำทาง )

สั่ง

  1. default ( scope: false) - คำสั่งไม่ได้สร้างขอบเขตใหม่ดังนั้นจึงไม่มีการสืบทอดที่นี่ สิ่งนี้เป็นเรื่องง่าย แต่ก็เป็นอันตรายเพราะเช่นคำสั่งอาจคิดว่ามันกำลังสร้างคุณสมบัติใหม่บนขอบเขตเมื่อในความเป็นจริงมันเป็นการอุดตันทรัพย์สินที่มีอยู่ นี่ไม่ใช่ตัวเลือกที่ดีสำหรับการเขียนคำสั่งที่มีวัตถุประสงค์เพื่อใช้เป็นส่วนประกอบ
  2. scope: true- คำสั่งสร้างขอบเขตลูกใหม่ที่ต้นแบบสืบทอดมาจากขอบเขตแม่ หากมีมากกว่าหนึ่งคำสั่ง (ในองค์ประกอบ DOM เดียวกัน) ร้องขอขอบเขตใหม่จะมีการสร้างขอบเขตลูกใหม่เพียงขอบเขตเดียว เนื่องจากเรามีการสืบทอดต้นแบบ "ปกติ" นี่เป็นเหมือน ng-include และ ng-switch ดังนั้นให้ระมัดระวังการเชื่อมโยงข้อมูลแบบสองทางกับขอบเขตหลักของขอบเขตหลักและซ่อนขอบเขตลูกของคุณสมบัติขอบเขตหลัก
  3. scope: { ... }- คำสั่งสร้างขอบเขตแยก / แยกใหม่ มันไม่ได้รับมรดกต้นแบบ นี่เป็นทางเลือกที่ดีที่สุดของคุณเมื่อสร้างส่วนประกอบที่ใช้ซ้ำได้เนื่องจากคำสั่งไม่สามารถอ่านหรือแก้ไขขอบเขตพาเรนต์ อย่างไรก็ตามคำสั่งดังกล่าวมักต้องการการเข้าถึงคุณสมบัติขอบเขตพาเรนต์ไม่กี่รายการ แฮชของวัตถุใช้เพื่อตั้งค่าการรวมสองทาง (โดยใช้ '=') หรือการเชื่อมโยงทางเดียว (โดยใช้ '@') ระหว่างขอบเขตหลักและขอบเขตแยก นอกจากนี้ยังมี '&' เพื่อเชื่อมโยงกับนิพจน์ขอบเขตพาเรนต์ ดังนั้นสิ่งเหล่านี้ทั้งหมดสร้างคุณสมบัติขอบเขตภายในที่ได้รับมาจากขอบเขตหลัก โปรดทราบว่ามีการใช้แอททริบิวเพื่อช่วยในการตั้งค่าการเชื่อมโยง - คุณไม่สามารถอ้างอิงชื่อคุณสมบัติของขอบเขตพาเรนต์ในแฮชของวัตถุได้คุณต้องใช้แอททริบิวต์ เช่นนี้จะไม่ทำงานหากคุณต้องการผูกเข้ากับคุณสมบัติผู้ปกครองparentPropในขอบเขตที่แยก: และ<div my-directive> scope: { localProp: '@parentProp' }แอตทริบิวต์จะต้องใช้ในการระบุแต่ละสถานที่ให้บริการผู้ปกครองที่สั่งต้องการที่จะผูกกับ: และ <div my-directive the-Parent-Prop=parentProp> แยกวัตถุอ้างอิงขอบเขต แยก $ parent ขอบเขตอ้างอิงถึงขอบเขตพาเรนต์ดังนั้นแม้ว่าจะแยกได้และไม่สืบทอดต้นแบบจากขอบเขตพาเรนต์ แต่ก็ยังคงเป็นขอบเขตลูก สำหรับภาพด้านล่างเรามี และ สมมติว่าคำสั่งทำเช่นนี้ในฟังก์ชั่นการเชื่อมโยง: สำหรับข้อมูลเพิ่มเติมเกี่ยวกับขอบเขตแยกดูได้ที่http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/scope: { localProp: '@theParentProp' }
    __proto__

    <my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2">
    scope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' }
    scope.someIsolateProp = "I'm isolated"
    ขอบเขตที่แยกได้
  4. transclude: true- คำสั่งจะสร้างขอบเขตย่อย "transcluded" ใหม่ซึ่งสืบทอดมาจากต้นแบบขอบเขต transcluded และ scope isolated (ถ้ามี) คือพี่น้อง - ทรัพย์สิน $ parent ของแต่ละขอบเขตอ้างอิงถึงขอบเขต parent เดียวกัน เมื่อทั้งสอง transcluded และ isolate scope อยู่แล้วคุณสมบัติ isolate ของขอบเขต $$ nextSibling จะอ้างอิงขอบเขตของ transcluded ฉันไม่ทราบถึงความแตกต่างใด ๆ กับขอบเขต transcluded
    สำหรับภาพด้านล่างให้ถือเป็นคำสั่งเดียวกันกับข้างต้นด้วยการเพิ่มนี้:transclude: true
    ขอบเขต transcluded

ซอนี้มีshowScope()ฟังก์ชั่นที่สามารถใช้ในการตรวจสอบขอบเขตไอโซเลตและ transcluded ดูคำแนะนำในข้อคิดเห็นในซอ


สรุป

ขอบเขตมีสี่ประเภท:

  1. การถ่ายทอดขอบเขตต้นแบบต้นแบบปกติ - ng-include, ng-switch, ng-controller, directive with scope: true
  2. การสืบทอดขอบเขตต้นแบบต้นแบบปกติพร้อมสำเนา / การมอบหมาย - ng-repeat การวนซ้ำของ ng-repeat แต่ละครั้งจะสร้างขอบเขตลูกใหม่และขอบเขตลูกใหม่นั้นจะได้รับคุณสมบัติใหม่เสมอ
  3. ขอบเขตแยก - scope: {...}สั่งด้วย อันนี้ไม่ใช่ต้นแบบ แต่ '=', '@' และ '&' ให้กลไกในการเข้าถึงคุณสมบัติขอบเขตหลักผ่านทางคุณสมบัติ
  4. ขอบเขตเรียก - transclude: trueสั่งด้วย อันนี้ก็คือการสืบทอดขอบเขตต้นแบบต้นแบบปกติ แต่ก็ยังเป็นพี่น้องของขอบเขตแยกใด ๆ

สำหรับขอบเขตทั้งหมด (ต้นแบบหรือไม่) Angular จะติดตามความสัมพันธ์ระหว่างผู้ปกครองกับลูก (เช่นลำดับชั้น) เสมอผ่านคุณสมบัติ $ parent และ $$ childHead และ $$ childTail

สร้างไดอะแกรมด้วย "* .dot ไฟล์" ซึ่งอยู่บนGitHub " การเรียนรู้จาวาสคริปต์ด้วยกราฟวัตถุ " ของ Tim Caswell เป็นแรงบันดาลใจในการใช้ GraphViz สำหรับไดอะแกรม


48
บทความดีเลิศยาวเกินไปสำหรับคำตอบ SO แต่มีประโยชน์มาก โปรดวางไว้บนบล็อกของคุณก่อนที่บรรณาธิการจะลดขนาด
iwein

43
ฉันใส่สำเนาบนAngularJS วิกิพีเดีย
Mark Rajcok

3
การแก้ไข: "แยก__proto__วัตถุอ้างอิงของขอบเขต" ควรเป็น "แยกการ__proto__อ้างอิงขอบเขตของวัตถุขอบเขต" แทน ดังนั้นในภาพสองภาพสุดท้ายกล่อง "วัตถุ" สีส้มควรเป็นกล่อง "ขอบเขต" แทน
Mark Rajcok

15
แอสเวิร์นี้ควรรวมอยู่ในคู่มือ angularjs นี่เป็นคำสอนที่มากกว่า ...
Marcelo De Zen

2
วิกิทำให้ฉันงงก่อนอื่นจึงอ่านว่า: "ห่วงโซ่ต้นแบบนั้นได้รับการพิจารณาเพราะไม่พบวัตถุใน childScope" และจากนั้นจะอ่านว่า: "ถ้าเราตั้งค่า childScope.propertyX โซ่ต้นแบบจะไม่ได้รับการพิจารณา" อันที่สองหมายถึงเงื่อนไขในขณะที่คนแรกไม่ได้
เตฟาน

140

ผมในทางที่ไม่ต้องการที่จะแข่งขันกับคำตอบของมาร์ค แต่เพียงต้องการที่จะเน้นชิ้นที่ในที่สุดก็ทำทุกอย่างคลิกเป็นคนใหม่ที่จะJavascript มรดกและโซ่ต้นแบบของมัน

คุณสมบัติเท่านั้นอ่านการค้นหาเชนลูกโซ่ต้นแบบไม่ใช่เขียน ดังนั้นเมื่อคุณตั้งค่า

myObject.prop = '123';

มันไม่ได้ค้นหาโซ่ แต่เมื่อคุณตั้งค่า

myObject.myThing.prop = '123';

มีการอ่านอย่างละเอียดเกิดขึ้นภายในการดำเนินการเขียนที่พยายามค้นหา myThing ก่อนที่จะเขียนไปที่เสา นั่นคือเหตุผลที่ว่าทำไมการเขียนถึง object.properties จากเด็กไปถึงวัตถุของผู้ปกครอง


12
ในขณะที่นี่เป็นแนวคิดที่เรียบง่ายมาก แต่ก็อาจจะไม่ชัดเจนมากนักตั้งแต่ฉันเชื่อว่าผู้คนจำนวนมากคิดถึงมัน ใส่กัน
moljac024

3
คำพูดที่ยอดเยี่ยม ฉันจะไปความละเอียดของคุณสมบัติวัตถุที่ไม่เกี่ยวข้องกับการอ่านในขณะที่ความละเอียดของคุณสมบัติวัตถุไม่
เตฟาน

1
ทำไม? อะไรคือแรงจูงใจในการเขียนอสังหาริมทรัพย์ที่ไม่ได้เกิดขึ้นในห่วงโซ่ต้นแบบ? ดูเหมือนว่าจะบ้า ...
โจนาธาน

1
มันจะดีถ้าคุณเพิ่มตัวอย่างง่าย ๆ จริง ๆ
tylik

2
สังเกตเห็นว่ามันไม่ค้นหาห่วงโซ่ต้นแบบสำหรับsetters หากไม่พบสิ่งใดสิ่งนั้นจะสร้างคุณสมบัติบนตัวรับ
Bergi

21

ฉันต้องการเพิ่มตัวอย่างของการสืบทอดต้นแบบด้วย javascript ใน @Scott Driscoll คำตอบ เราจะใช้รูปแบบการสืบทอดแบบคลาสสิกกับ Object.create () ซึ่งเป็นส่วนหนึ่งของข้อกำหนด EcmaScript 5

ก่อนอื่นเราสร้างฟังก์ชั่นวัตถุ "ผู้ปกครอง"

function Parent(){

}

จากนั้นเพิ่มต้นแบบในฟังก์ชันวัตถุ "แม่"

 Parent.prototype = {
 primitive : 1,
 object : {
    one : 1
   }
}

สร้างฟังก์ชั่นวัตถุ "เด็ก"

function Child(){

}

กำหนดลูกต้นแบบ (ทำให้ลูกต้นแบบสืบทอดจากแม่ลูกต้นแบบ)

Child.prototype = Object.create(Parent.prototype);

กำหนดตัวสร้างต้นแบบ "เด็ก" ที่เหมาะสม

Child.prototype.constructor = Child;

เพิ่มวิธีการ "changeProps" ให้กับลูกต้นแบบซึ่งจะเขียนค่าคุณสมบัติ "ดั้งเดิม" ในวัตถุลูกและเปลี่ยนค่า "object.one" ทั้งในลูกและวัตถุแม่

Child.prototype.changeProps = function(){
    this.primitive = 2;
    this.object.one = 2;
};

เริ่มต้นวัตถุพ่อแม่และลูก (ลูก)

var dad = new Parent();
var son = new Child();

เรียกเมธอด child (son) changeProps

son.changeProps();

ตรวจสอบผลลัพธ์

คุณสมบัติดั้งเดิมของผู้ปกครองไม่เปลี่ยนแปลง

console.log(dad.primitive); /* 1 */

เปลี่ยนแปลงคุณสมบัติดั้งเดิมของเด็กแล้ว (เขียนใหม่)

console.log(son.primitive); /* 2 */

parent และ Child คุณสมบัติ object.one เปลี่ยนไป

console.log(dad.object.one); /* 2 */
console.log(son.object.one); /* 2 */

ตัวอย่างการทำงานที่นี่ http://jsbin.com/xexurukiso/1/edit/

ข้อมูลเพิ่มเติมเกี่ยวกับ Object.create ที่นี่https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create

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