참고: 코드의 메서드, 상속, 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
개선을 위한 제안
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를 작성해 왔지만 우버 기능을 사용해야 할 필요성을 느낀 적이 없습니다. 슈퍼 아이디어는 클래식 패턴에서는 상당히 중요하지만 프로토타입 패턴과 기능적 패턴에서는 불필요해 보입니다. 이제 나는 JavaScript에서 클래식 모델을 지원하려는 나의 초기 시도를 실수로 봅니다.
Crockford는 JavaScript의 객체 지향 프로그래밍을 거부하고 JavaScript가 프로그래밍을 위해 프로토타입 및 기능적 패턴을 따라야 한다고 주장하는 것을 볼 수 있습니다.하지만 개인적으로는 복잡한 시나리오에서 객체지향 메커니즘이 있다면 훨씬 더 편리할 것 같습니다.