John Resig (จากชื่อเสียงของ jQuery) นำเสนอ การสืบทอด JavaScript อย่างง่าย อย่างกระชับโดยย่อ แนวทางของเขาเป็นแรงบันดาลใจให้ฉันพยายามปรับปรุงสิ่งต่างๆ ให้ดียิ่งขึ้นไปอีก ฉันได้เขียนฟังก์ชัน Class.extend
ดั้งเดิมของ Resig ใหม่เพื่อให้มีข้อดีดังต่อไปนี้:
ประสิทธิภาพ – โอเวอร์เฮดน้อยลงระหว่างการกำหนดคลาส การสร้างออบเจ็กต์ และการเรียกเมธอดคลาสพื้นฐาน
ความยืดหยุ่น – ปรับให้เหมาะสมสำหรับเบราว์เซอร์รุ่นใหม่ที่เข้ากันได้กับ ECMAScript 5 (เช่น Chrome) แต่มี "shim" ที่เทียบเท่าสำหรับเบราว์เซอร์รุ่นเก่า (เช่น IE6)
ความเข้ากันได้ – ตรวจสอบในโหมดเข้มงวดและให้ความเข้ากันได้ของเครื่องมือที่ดีขึ้น (เช่น ความคิดเห็น VSDoc/JSDoc, Visual Studio IntelliSense ฯลฯ)
ความเรียบง่าย – คุณไม่จำเป็นต้องเป็น "นินจา" เพื่อทำความเข้าใจซอร์สโค้ด (และจะง่ายกว่านั้นอีกหากคุณสูญเสียฟีเจอร์ ECMAScript 5)
ความทนทาน – ผ่านการทดสอบหน่วย "ตัวพิมพ์เล็ก" มากขึ้น (เช่น การแทนที่ toString ใน IE)
เพราะมันเกือบจะดูดีเกินกว่าที่จะเป็นจริงได้ ฉันต้องการให้แน่ใจว่าตรรกะของฉันไม่มีข้อบกพร่องหรือข้อบกพร่องพื้นฐานใดๆ และดูว่ามีใครสามารถแนะนำการปรับปรุงหรือหักล้างโค้ดได้หรือไม่ ด้วยเหตุนี้ ฉันจึงนำเสนอฟังก์ชัน classify
:
function classify(base, properties)
{
/// <summary>Creates a type (i.e. class) that supports prototype-chaining (i.e. inheritance).</summary>
/// <param name="base" type="Function" optional="true">The base class to extend.</param>
/// <param name="properties" type="Object" optional="true">The properties of the class, including its constructor and members.</param>
/// <returns type="Function">The class.</returns>
// quick-and-dirty method overloading
properties = (typeof(base) === "object") ? base : properties || {};
base = (typeof(base) === "function") ? base : Object;
var basePrototype = base.prototype;
var derivedPrototype;
if (Object.create)
{
// allow newer browsers to leverage ECMAScript 5 features
var propertyNames = Object.getOwnPropertyNames(properties);
var propertyDescriptors = {};
for (var i = 0, p; p = propertyNames[i]; i++)
propertyDescriptors[p] = Object.getOwnPropertyDescriptor(properties, p);
derivedPrototype = Object.create(basePrototype, propertyDescriptors);
}
else
{
// provide "shim" for older browsers
var baseType = function() {};
baseType.prototype = basePrototype;
derivedPrototype = new baseType;
// add enumerable properties
for (var p in properties)
if (properties.hasOwnProperty(p))
derivedPrototype[p] = properties[p];
// add non-enumerable properties (see https://developer.mozilla.org/en/ECMAScript_DontEnum_attribute)
if (!{ constructor: true }.propertyIsEnumerable("constructor"))
for (var i = 0, a = [ "constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf" ], p; p = a[i]; i++)
if (properties.hasOwnProperty(p))
derivedPrototype[p] = properties[p];
}
// build the class
var derived = properties.hasOwnProperty("constructor") ? properties.constructor : function() { base.apply(this, arguments); };
derived.prototype = derivedPrototype;
derived.prototype.constructor = derived;
derived.prototype.base = derived.base = basePrototype;
return derived;
}
และการใช้งานเกือบจะเหมือนกับ Resig ยกเว้นชื่อ Constructor (constructor
vs. init
) และไวยากรณ์สำหรับการเรียกเมธอดคลาสพื้นฐาน
/* Example 1: Define a minimal class */
var Minimal = classify();
/* Example 2a: Define a "plain old" class (without using the classify function) */
var Class = function()
{
this.name = "John";
};
Class.prototype.count = function()
{
return this.name + ": One. Two. Three.";
};
/* Example 2b: Define a derived class that extends a "plain old" base class */
var SpanishClass = classify(Class,
{
constructor: function()
{
this.name = "Juan";
},
count: function()
{
return this.name + ": Uno. Dos. Tres.";
}
});
/* Example 3: Define a Person class that extends Object by default */
var Person = classify(
{
constructor: function(name, isQuiet)
{
this.name = name;
this.isQuiet = isQuiet;
},
canSing: function()
{
return !this.isQuiet;
},
sing: function()
{
return this.canSing() ? "Figaro!" : "Shh!";
},
toString: function()
{
return "Hello, " + this.name + "!";
}
});
/* Example 4: Define a Ninja class that extends Person */
var Ninja = classify(Person,
{
constructor: function(name, skillLevel)
{
Ninja.base.constructor.call(this, name, true);
this.skillLevel = skillLevel;
},
canSing: function()
{
return Ninja.base.canSing.call(this) || this.skillLevel > 200;
},
attack: function()
{
return "Chop!";
}
});
/* Example 4: Define an ExtremeNinja class that extends Ninja that extends Person */
var ExtremeNinja = classify(Ninja,
{
attack: function()
{
return "Chop! Chop!";
},
backflip: function()
{
this.skillLevel++;
return "Woosh!";
}
});
var m = new Minimal();
var c = new Class();
var s = new SpanishClass();
var p = new Person("Mary", false);
var n = new Ninja("John", 100);
var e = new ExtremeNinja("World", 200);
และนี่คือการทดสอบ QUnit ของฉันที่ผ่านทั้งหมด:
equals(m instanceof Object && m instanceof Minimal && m.constructor === Minimal, true);
equals(c instanceof Object && c instanceof Class && c.constructor === Class, true);
equals(s instanceof Object && s instanceof Class && s instanceof SpanishClass && s.constructor === SpanishClass, true);
equals(p instanceof Object && p instanceof Person && p.constructor === Person, true);
equals(n instanceof Object && n instanceof Person && n instanceof Ninja && n.constructor === Ninja, true);
equals(e instanceof Object && e instanceof Person && e instanceof Ninja && e instanceof ExtremeNinja && e.constructor === ExtremeNinja, true);
equals(c.count(), "John: One. Two. Three.");
equals(s.count(), "Juan: Uno. Dos. Tres.");
equals(p.isQuiet, false);
equals(p.canSing(), true);
equals(p.sing(), "Figaro!");
equals(n.isQuiet, true);
equals(n.skillLevel, 100);
equals(n.canSing(), false);
equals(n.sing(), "Shh!");
equals(n.attack(), "Chop!");
equals(e.isQuiet, true);
equals(e.skillLevel, 200);
equals(e.canSing(), false);
equals(e.sing(), "Shh!");
equals(e.attack(), "Chop! Chop!");
equals(e.backflip(), "Woosh!");
equals(e.skillLevel, 201);
equals(e.canSing(), true);
equals(e.sing(), "Figaro!");
equals(e.toString(), "Hello, World!");
มีใครเห็นสิ่งผิดปกติในแนวทางของฉันเทียบกับแนวทางดั้งเดิมของ John Resig หรือไม่ ยินดีรับข้อเสนอแนะและข้อเสนอแนะ!
หมายเหตุ: โค้ดด้านบนได้รับการแก้ไขอย่างมากตั้งแต่ฉันโพสต์คำถามนี้ครั้งแรก ด้านบนแสดงถึงเวอร์ชันล่าสุด หากต้องการดูว่ามีการพัฒนาอย่างไร โปรดตรวจสอบประวัติการแก้ไข
Object.create
และ traitsjs การสืบทอดไม่ดีในจาวาสคริปต์ ใช้องค์ประกอบของวัตถุ - person Raynos   schedule 28.07.2011