जावास्क्रिप्ट के प्रोटोटाइप-आधारित विरासत का अच्छा उदाहरण


89

मैं 10 वर्षों से OOP भाषाओं के साथ प्रोग्रामिंग कर रहा हूं लेकिन मैं अब जावास्क्रिप्ट सीख रहा हूं और यह पहली बार है जब मैंने प्रोटोटाइप आधारित विरासत का सामना किया है। मैं अच्छे कोड का अध्ययन करके सबसे तेजी से सीखना चाहता हूं। एक जावास्क्रिप्ट अनुप्रयोग (या पुस्तकालय) का एक अच्छी तरह से लिखा उदाहरण है जो प्रोटोटाइप के उत्तराधिकार का सही उपयोग करता है? और क्या आप (संक्षेप में) वर्णन कर सकते हैं कि कैसे / जहां प्रोटोटाइप विरासत का उपयोग किया जाता है, इसलिए मुझे पता है कि पढ़ना कहां शुरू करना है?


1
क्या आपको उस बेस लाइब्रेरी की जांच करने का मौका मिला? यह वास्तव में अच्छा है, और काफी छोटा है। यदि आप इसे पसंद करते हैं, तो मेरे उत्तर को उत्तर के रूप में चिह्नित करने पर विचार करें। टीआईए, रोलाण्ड।
रोलैंड बोमन

मुझे लगता है कि मैं आपके समान नाव में हूं। मैं चाहता हूं, साथ ही, इस प्रोटोटाइप भाषा के बारे में थोड़ा जानने के लिए, केवल ओओपी फ्रेमवर्क या इसी तरह तक सीमित नहीं होना चाहिए, यहां तक ​​कि वे महान और सभी होने के नाते, हमें सीखने की जरूरत है, है ना? न सिर्फ कुछ रूपरेखा मेरे लिए है, भले ही मैं इसका उपयोग करने जा रहा हूं। लेकिन सीखें कि कैसे नए तरीकों से नई चीजें बनाएं, नए तरीके से सोचें। मुझे तुम्हारी शैली पसंद है। मैं मेरी मदद करने की कोशिश कर रहा हूं और शायद आपकी मदद करूं। जैसे ही मुझे कुछ मिलेगा, मैं आपको बता दूंगा।
मार्सेलो-फ़राज़

जवाबों:


48

डगलस क्रॉकफोर्ड के पास जावास्क्रिप्ट प्रोटोटाइप पर एक अच्छा पृष्ठ है :

पाँच साल पहले मैंने जावास्क्रिप्ट में क्लासिकल इनहेरिटेंस लिखा था । इससे पता चला कि जावास्क्रिप्ट एक वर्ग-मुक्त, प्रोटोटाइप भाषा है, और यह एक शास्त्रीय प्रणाली को अनुकरण करने के लिए पर्याप्त अभिव्यंजक शक्ति है। मेरी प्रोग्रामिंग शैली तब से विकसित हुई है, जैसा कि किसी भी अच्छे प्रोग्रामर को करना चाहिए। मैंने पूरी तरह से प्रोटोटाइप को अपनाना सीख लिया है, और खुद को शास्त्रीय मॉडल के दायरे से मुक्त कर लिया है।

डीन एडवर्ड के बेस.जेएस , मुटूलस क्लास या जॉन रेजिग के सिंपल इनहेरिटेंस काम जावास्क्रिप्ट में शास्त्रीय विरासत करने के तरीके हैं ।


newObj = Object.create(oldObj);यदि आप इसे कक्षा-मुक्त चाहते हैं तो बस क्यों नहीं ? अन्यथा, oldObjनिर्माण कार्य के प्रोटोटाइप ऑब्जेक्ट के साथ काम करना चाहिए?
साइकर

76

जैसा कि उल्लेख किया गया है, डगलस क्रॉकफ़ोर्ड की फिल्में क्यों और कैसे कवर करती हैं, इसके बारे में अच्छी व्याख्या करती हैं। लेकिन इसे जावास्क्रिप्ट की कुछ पंक्तियों में रखना:

// Declaring our Animal object
var Animal = function () {

    this.name = 'unknown';

    this.getName = function () {
        return this.name;
    }

    return this;
};

// Declaring our Dog object
var Dog = function () {

    // A private variable here        
    var private = 42;

    // overriding the name
    this.name = "Bello";

    // Implementing ".bark()"
    this.bark = function () {
        return 'MEOW';
    }  

    return this;
};


// Dog extends animal
Dog.prototype = new Animal();

// -- Done declaring --

// Creating an instance of Dog.
var dog = new Dog();

// Proving our case
console.log(
    "Is dog an instance of Dog? ", dog instanceof Dog, "\n",
    "Is dog an instance of Animal? ", dog instanceof Animal, "\n",
    dog.bark() +"\n", // Should be: "MEOW"
    dog.getName() +"\n", // Should be: "Bello"
    dog.private +"\n" // Should be: 'undefined'
);

हालांकि इस दृष्टिकोण के साथ समस्या यह है कि यह हर बार आपके द्वारा बनाई गई वस्तु को फिर से बनाएगा। एक अन्य तरीका यह है कि आप अपनी वस्तुओं को प्रोटोटाइप स्टैक पर घोषित करें, जैसे:

// Defining test one, prototypal
var testOne = function () {};
testOne.prototype = (function () {
    var me = {}, privateVariable = 42;
    me.someMethod = function () {
        return privateVariable;
    };

    me.publicVariable = "foo bar";
    me.anotherMethod = function () {
        return this.publicVariable;
    };

    return me;

}());


// Defining test two, function
var testTwo = function() {
    var me = {}, privateVariable = 42;
    me.someMethod = function () {
        return privateVariable;
    };

    me.publicVariable = "foo bar";
    me.anotherMethod = function () {
        return this.publicVariable;
    };

    return me;
};


// Proving that both techniques are functionally identical
var resultTestOne = new testOne(),
    resultTestTwo = new testTwo();

console.log(
    resultTestOne.someMethod(), // Should print 42
    resultTestOne.publicVariable // Should print "foo bar"
);

console.log(
    resultTestTwo.someMethod(), // Should print 42
    resultTestTwo.publicVariable // Should print "foo bar"
);



// Performance benchmark start
var stop, start, loopCount = 1000000;

// Running testOne
start = (new Date()).getTime(); 
for (var i = loopCount; i>0; i--) {
    new testOne();
}
stop = (new Date()).getTime();

console.log('Test one took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');



// Running testTwo
start = (new Date()).getTime(); 
for (var i = loopCount; i>0; i--) {
    new testTwo();
}
stop = (new Date()).getTime();

console.log('Test two took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');

आत्मनिरीक्षण की बात आती है तो थोड़ा सा उल्टा होता है। डंपिंग testOne, परिणाम कम उपयोगी जानकारी में होगा। इसके अलावा "testOne" में निजी संपत्ति "PrivateVariable" सभी उदाहरणों में साझा की जाती है, als shesek द्वारा उत्तर में उल्लेखित हैं।


3
टिप्पणी है कि testOne में privateVariableबस के दायरे में एक चर रहा है Iife , और सभी उदाहरणों भर में अपने साझा है, तो आप उस पर उदाहरण-विशिष्ट डेटा की दुकान नहीं करना चाहिए। (testTwo पर यह उदाहरण-विशिष्ट है, क्योंकि प्रत्येक कॉल testTwo () एक नया, प्रति-आवृत्ति, दायरा बनाती है)
shesek

मैंने अपवित्र किया क्योंकि आपने अन्य दृष्टिकोण दिखाया और इसका उपयोग क्यों नहीं किया क्योंकि यह प्रतियां बनाता है
मर्फी 316

हर बार वस्तु को फिर से बनाने की समस्या मुख्य रूप से हर नई वस्तु के लिए बनाए गए तरीकों के कारण होती है। हालाँकि, हम विधि को परिभाषित करके समस्या को कम कर सकते हैं Dog.prototype। इसलिए उपयोग करने के बजाय this.bark = function () {...}, हम फ़ंक्शन के Dot.prototype.bark = function () {...}बाहर कर सकते हैं Dog। ( इस उत्तर में अधिक विवरण देखें )
हुआंग चाओ

26
function Shape(x, y) {
    this.x = x;
    this.y = y;
}

// 1. Explicitly call base (Shape) constructor from subclass (Circle) constructor passing this as the explicit receiver
function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r = r;
}

// 2. Use Object.create to construct the subclass prototype object to avoid calling the base constructor
Circle.prototype = Object.create(Shape.prototype);

3
शायद आपके जवाब के साथ इस लिंक को जोड़ने से तस्वीर और भी अधिक हो सकती है: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
डायनम

14

मैं YUI पर और डीन एडवर्ड की Baseलाइब्रेरी पर एक नज़र डालूंगा : http://dean.edwards.name/weblog/2006/03/base/

YUI के लिए आप लैंग मॉड्यूल , एस्प पर एक त्वरित नज़र डाल सकते हैं । YAHOO.lang.extend विधि। और फिर, आप कुछ विजेट या उपयोगिताओं के स्रोत को ब्राउज़ कर सकते हैं और देख सकते हैं कि वे उस पद्धति का उपयोग कैसे करते हैं।


YUI 2 को 2011 के रूप में पदावनत किया गया है, इसलिए लिंक langअर्ध-टूटा हुआ है। किसी को भी YUI 3 के लिए इसे ठीक करने की परवाह है?
एक

3 yui में लंग को एक विस्तार विधि नहीं लगती है। लेकिन जब से उत्तर कार्यान्वयन का उपयोग करने का इरादा रखता है एक उदाहरण के रूप में संस्करण कोई फर्क नहीं पड़ता।
ईएमबीपी


4

मिक्सू की नोड बुक ( http://book.mixu.net/node/ch6.html ) से यह मुझे मिला सबसे स्पष्ट उदाहरण है :

मैं विरासत पर रचना का पक्ष लेता हूं:

रचना - किसी वस्तु की कार्यक्षमता अन्य वस्तुओं के उदाहरणों को मिलाकर विभिन्न वर्गों के समूह से बनी होती है। वंशानुक्रम - किसी वस्तु की कार्यक्षमता उसके मूल वर्गों से स्वयं की कार्यक्षमता और कार्यक्षमता से बनी होती है। यदि आपके पास विरासत है, तो सादे पुराने जेएस का उपयोग करें

यदि आपको विरासत को लागू करना चाहिए, तो कम से कम अभी तक एक और गैर-मानक कार्यान्वयन / जादू फ़ंक्शन का उपयोग करने से बचें। यहां बताया गया है कि आप शुद्ध ES3 में वंशानुक्रम की एक उचित कार्यप्रणाली को कैसे लागू कर सकते हैं (जब तक आप प्रोटोटाइप पर गुणों को परिभाषित नहीं करने के नियम का पालन करते हैं):

function Animal(name) {
  this.name = name;
};
Animal.prototype.move = function(meters) {
  console.log(this.name+" moved "+meters+"m.");
};

function Snake() {
  Animal.apply(this, Array.prototype.slice.call(arguments));
};
Snake.prototype = new Animal();
Snake.prototype.move = function() {
  console.log("Slithering...");
  Animal.prototype.move.call(this, 5);
};

var sam = new Snake("Sammy the Python");
sam.move();

यह शास्त्रीय विरासत के रूप में एक ही बात नहीं है - लेकिन यह मानक, समझने योग्य जावास्क्रिप्ट है और इसकी कार्यक्षमता है जो लोग ज्यादातर चाहते हैं: चेनेबल कंस्ट्रक्टर और सुपरक्लास के तरीकों को कॉल करने की क्षमता।


4

ईएस 6 classऔरextends

ईएस 6 classऔरextends पहले से संभव प्रोटोटाइप श्रृंखला हेरफेर के लिए सिंटैक्स चीनी हैं, और इसलिए यकीनन सबसे विहित सेटअप।

पहले प्रोटोटाइप चेन और .प्रॉपर्टी लुकअप के बारे में और जानें : https://stackoverflow.com/a/23877420/895245

अब क्या होता है का पुनर्निर्माण करते हैं:

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// /programming/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

सभी पूर्वनिर्धारित वस्तुओं के बिना सरलीकृत आरेख:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype


1

मैंने जो सबसे अच्छे उदाहरण देखे हैं, वे डगलस क्रॉकफोर्ड के जावास्क्रिप्ट: द गुड पार्ट्स में हैं । यह निश्चित रूप से भाषा पर संतुलित दृष्टिकोण प्राप्त करने में आपकी सहायता करने के लिए खरीदने लायक है।

डगलस क्रॉकफोर्ड JSON प्रारूप के लिए जिम्मेदार है और याहू पर जावास्क्रिप्ट गुरु के रूप में काम करता है।


7
उत्तरदायी? यह लगभग ":) का दोषी लगता है"
रोलाण्ड बोमन

@ रोलैंड मुझे लगता है कि JSON डेटा संग्रहीत करने के लिए काफी अच्छा गैर-क्रियात्मक प्रारूप है। उन्होंने निश्चित रूप से इसका आविष्कार नहीं किया था, लेकिन प्रारूप 2002 में स्टीम बैक में कॉन्फिग सेटिंग्स के लिए था
क्रिस एस

क्रिस एस, मुझे ऐसा भी लगता है - अधिक से अधिक बार मैं चाहता हूं कि हम सभी एक्सएमएल को एक्सचेंज प्रारूप के रूप में छोड़ दें और तुरंत जेएसएन पर चले गए।
रोलैंड बोमन

3
आविष्कार करने के लिए बहुत कुछ नहीं: JSON जावास्क्रिप्ट की अपनी वस्तु शाब्दिक वाक्य रचना का एक उपसमूह है, जो कि 1997 के बाद से भाषा में है।
टिम डाउन

@ समय अच्छा बिंदु - मुझे यह महसूस नहीं हुआ कि यह शुरुआत से ही है
क्रिस एस

0

ECMAScript संस्करण विशिष्ट कार्यान्वयन के साथ एक स्निपेट जावास्क्रिप्ट प्रोटोटाइप-आधारित विरासत है। यह स्वचालित रूप से चुनेगा जो वर्तमान रनटाइम के अनुसार ईएस 6, ईएस 5 और ईएस 3 कार्यान्वयन के बीच उपयोग करना है।


0

जावास्क्रिप्ट में प्रोटोटाइप आधारित विरासत का एक उदाहरण जोड़ना।

// Animal Class
function Animal (name, energy) {
  this.name = name;
  this.energy = energy;
}

Animal.prototype.eat = function (amount) {
  console.log(this.name, "eating. Energy level: ", this.energy);
  this.energy += amount;
  console.log(this.name, "completed eating. Energy level: ", this.energy);
}

Animal.prototype.sleep = function (length) {
  console.log(this.name, "sleeping. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "completed sleeping. Energy level: ", this.energy);
}

Animal.prototype.play = function (length) {
  console.log(this.name, " playing. Energy level: ", this.energy);
  this.energy -= length;
  console.log(this.name, "completed playing. Energy level: ", this.energy);
}

// Dog Class
function Dog (name, energy, breed) {
  Animal.call(this, name, energy);
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function () {
  console.log(this.name, "barking. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "done barking. Energy level: ", this.energy);
}

Dog.prototype.showBreed = function () {
  console.log(this.name,"'s breed is ", this.breed);
}

// Cat Class
function Cat (name, energy, male) {
  Animal.call(this, name, energy);
  this.male = male;
}

Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;

Cat.prototype.meow = function () {
  console.log(this.name, "meowing. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "done meowing. Energy level: ", this.energy);
}

Cat.prototype.showGender = function () {
  if (this.male) {
    console.log(this.name, "is male.");
  } else {
    console.log(this.name, "is female.");
  }
}

// Instances
const charlie = new Dog("Charlie", 10, "Labrador");
charlie.bark();
charlie.showBreed();

const penny = new Cat("Penny", 8, false);
penny.meow();
penny.showGender();

ES6 कंस्ट्रक्शन और सुपर कीवर्ड के उपयोग से वंशानुक्रम के सुगम कार्यान्वयन का उपयोग करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.