まず、Crockford スタイルの継承を使用した呼び出しメソッドを見てみましょう。
注: コード内のメソッド、継承、および uber はすべてカスタム オブジェクトであり、これについては後続のコード分析で詳しく説明します。
// 定义Person类 function Person(name) { this.name = name; } // 定义Person的原型方法 Person.method("getName", function() { return this.name; }); // 定义Employee类 function Employee(name, employeeID) { this.name = name; this.employeeID = employeeID; } // 指定Employee类从Person类继承 Employee.inherits(Person); // 定义Employee的原型方法 Employee.method("getEmployeeID", function() { return this.employeeID; }); Employee.method("getName", function() { // 注意,可以在子类中调用父类的原型方法 return "Employee name: " + this.uber("getName"); }); // 实例化子类 var zhang = new Employee("ZhangSan", "1234"); console.log(zhang.getName()); // "Employee name: ZhangSan"
ここで言及しなければならない欠点がいくつかあります。
もちろん、Crockford の実装では、次の例に示すように、パラメータを使用して親クラスのメソッドを呼び出すサブクラスのメソッドもサポートされています。
function Person(name) { this.name = name; } Person.method("getName", function(prefix) { return prefix + this.name; }); function Employee(name, employeeID) { this.name = name; this.employeeID = employeeID; } Employee.inherits(Person); Employee.method("getName", function() { // 注意,uber的第一个参数是要调用父类的函数名称,后面的参数都是此函数的参数 // 个人觉得这样方式不如这样调用来的直观:this.uber("Employee name: ") return this.uber("getName", "Employee name: "); }); var zhang = new Employee("ZhangSan", "1234"); console.log(zhang.getName()); // "Employee name: ZhangSan"
まず第一に、メソッド関数の定義は非常に簡単です:
Function.prototype.method = function(name, func) { // this指向当前函数,也即是typeof(this) === "function" this.prototype[name] = func; return this; };
継承関数の定義は少し複雑です:
Function.method('inherits', function (parent) { // 关键是这一段:this.prototype = new parent(),这里实现了原型的引用 var d = {}, p = (this.prototype = new parent()); // 只为子类的原型增加uber方法,这里的Closure是为了在调用uber函数时知道当前类的父类的原型(也即是变量 - v) this.method('uber', function uber(name) { // 这里考虑到如果name是存在于Object.prototype中的函数名的情况 // 比如 "toString" in {} === true if (!(name in d)) { // 通过d[name]计数,不理解具体的含义 d[name] = 0; } var f, r, t = d[name], v = parent.prototype; if (t) { while (t) { v = v.constructor.prototype; t -= 1; } f = v[name]; } else { // 个人觉得这段代码有点繁琐,既然uber的含义就是父类的函数,那么f直接指向v[name]就可以了 f = p[name]; if (f == this[name]) { f = v[name]; } } d[name] += 1; // 执行父类中的函数name,但是函数中this指向当前对象 // 同时注意使用Array.prototype.slice.apply的方式对arguments进行截断(因为arguments不是标准的数组,没有slice方法) r = f.apply(this, Array.prototype.slice.apply(arguments, [1])); d[name] -= 1; return r; }); return this; });
var zhang = new Employee("ZhangSan", "1234"); console.log(zhang.getName()); // "Employee name: ZhangSan" console.log(zhang.constructor === Employee); // false console.log(zhang.constructor === Person); // true
前回の分析から、個人的にはメソッド関数は必要ないと感じていますが、視線が混乱しやすいです。 継承メソッドは、ある程度のスリム化を行うことができ (Crockford はより多くの状況を考慮する可能性があるためです。元の記事では継承の使用方法がいくつか紹介されていますが、そのうちの 1 つにのみ焦点を当てます)、コンストラクターのポインティング エラーを修正します。
Function.prototype.inherits = function(parent) { this.prototype = new parent(); this.prototype.constructor = this; this.prototype.uber = function(name) { f = parent.prototype[name]; return f.apply(this, Array.prototype.slice.call(arguments, 1)); }; };
function Person(name) { this.name = name; } Person.prototype.getName = function(prefix) { return prefix + this.name; }; function Employee(name, employeeID) { this.name = name; this.employeeID = employeeID; } Employee.inherits(Person); Employee.prototype.getName = function() { return this.uber("getName", "Employee name: "); }; var zhang = new Employee("ZhangSan", "1234"); console.log(zhang.getName()); // "Employee name: ZhangSan" console.log(zhang.constructor === Employee); // true
記事の最後で、クロックフォード氏は実際に次のように述べています。
私は 8 年間 JavaScript を書いてきましたが、uber 関数を使用する必要性を感じたことは一度もありません。スーパー アイデアは古典的なパターンではかなり重要ですが、プロトタイプおよび関数型パターンでは不要のようです。Crockford が JavaScript でのオブジェクト指向プログラミングを否定し、JavaScript はプログラミングのプロトタイプと関数のパターンに従うべきだと主張していることがわかります。