JavaScript 상속에 대한 자세한 설명(3)_js 객체지향

WBOY
풀어 주다: 2016-05-16 18:50:02
원래의
715명이 탐색했습니다.

참고: 이 장의 jClass 구현은 간단한 JavaScript 상속의 실행을 의미합니다.

먼저 1장에 소개된 예제를 검토해 보겠습니다.

 function Person(name) {<br>
this.name = name;<br>
}<br>
Person.prototype = {<br>
getName: function() {<br>
return this.name;<br>
}<br>
}<br><br>
function Employee(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
}<br>
Employee.prototype = new Person();<br>
Employee.prototype.getEmployeeID = function() {<br>
return this.employeeID;<br>
};<br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "ZhangSan" <br>
로그인 후 복사

생성자 포인팅 오류 수정

이전 기사의 생성자 설명을 통해 Employee 인스턴스의 생성자에 아래와 같이 포인팅 오류가 있음을 알 수 있습니다.

 var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.constructor === Employee); // false<br>
console.log(zhang.constructor === Object); // true <br>
로그인 후 복사
간단한 수정이 필요합니다.
 function Employee(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
}<br>
Employee.prototype = new Person();<br>
Employee.prototype.constructor = Employee;<br>
Employee.prototype.getEmployeeID = function() {<br>
return this.employeeID;<br>
};<br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.constructor === Employee); // true<br>
console.log(zhang.constructor === Object); // false<br>
로그인 후 복사

Employee 클래스를 생성할 때 Person을 인스턴스화하는 것은 부적절합니다

그러나 반면에 상속을 구현하려면 이 메커니즘을 사용해야 합니다. 해결책은 생성자에서 데이터를 초기화하는 것이 아니라 데이터를 초기화하는 프로토타입 메서드(예: init)를 제공하는 것입니다.

 // 空的构造函数<br>
function Person() {<br>
}<br>
Person.prototype = {<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
}<br>
// 空的构造函数<br>
function Employee() {<br>
}<br>
// 创建类的阶段不会初始化父类的数据,因为Person是一个空的构造函数<br>
Employee.prototype = new Person();<br>
Employee.prototype.constructor = Employee;<br>
Employee.prototype.init = function(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
};<br>
Employee.prototype.getEmployeeID = function() {<br>
return this.employeeID;<br>
};<br>
로그인 후 복사
이 방법에서는 다음과 같이 개체를 인스턴스화한 후 init 함수를 수동으로 호출해야 합니다.
 var zhang = new Employee();<br>
zhang.init("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "ZhangSan"<br>
로그인 후 복사

init 함수를 자동으로 호출하는 방법은 무엇입니까?

두 가지 효과를 달성해야 합니다. 클래스를 생성할 때 init 함수를 호출하지 않고 객체를 인스턴스화할 때 init 함수를 자동으로 호출합니다. 빈 생성자를 호출할 때 상태 표시기가 필요한 것 같습니다.

 // 创建一个全局的状态标示 - 当前是否处于类的构造阶段<br>
var initializing = false;<br>
function Person() {<br>
if (!initializing) {<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
Person.prototype = {<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
}<br>
function Employee() {<br>
if (!initializing) {<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
// 标示当前进入类的创建阶段,不会调用init函数<br>
initializing = true;<br>
Employee.prototype = new Person();<br>
Employee.prototype.constructor = Employee;<br>
initializing = false;<br>
Employee.prototype.init = function(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
};<br>
Employee.prototype.getEmployeeID = function() {<br>
return this.employeeID;<br>
};<br><br>
// 初始化类实例时,自动调用类的原型函数init,并向init中传递参数<br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "ZhangSan"<br>
로그인 후 복사
그러나 이를 위해서는 전역 변수를 도입해야 하는데 이는 나쁜 징조입니다.

전역 변수 초기화를 방지하는 방법은 무엇입니까?

클래스 생성 과정을 단순화하기 위해 전역 함수를 도입하는 동시에 전역 변수 도입을 피하기 위해 내부 세부 정보를 캡슐화해야 합니다.

 // 当前是否处于创建类的阶段<br>
var initializing = false;<br>
function jClass(baseClass, prop) {<br>
// 只接受一个参数的情况 - jClass(prop)<br>
if (typeof (baseClass) === "object") {<br>
prop = baseClass;<br>
baseClass = null;<br>
}<br>
// 本次调用所创建的类(构造函数)<br>
function F() {<br>
// 如果当前处于实例化类的阶段,则调用init原型函数<br>
if (!initializing) {<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
// 如果此类需要从其它类扩展<br>
if (baseClass) {<br>
initializing = true;<br>
F.prototype = new baseClass();<br>
F.prototype.constructor = F;<br>
initializing = false;<br>
}<br>
// 覆盖父类的同名函数<br>
for (var name in prop) {<br>
if (prop.hasOwnProperty(name)) {<br>
F.prototype[name] = prop[name];<br>
}<br>
}<br>
return F;<br>
};<br>
로그인 후 복사
jClass 함수를 사용하여 클래스를 생성하고 클래스를 상속하는 방법:
 var Person = jClass({<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
});<br>
var Employee = jClass(Person, {<br>
init: function(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
},<br>
getEmployeeID: function() {<br>
return this.employeeID;<br>
}<br>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "ZhangSan"<br>
로그인 후 복사
자, 이제 클래스를 생성하고 인스턴스화하는 방법이 훨씬 더 우아해 보입니다. 그러나 여기에는 여전히 명백한 결함이 있습니다. 직원의 초기화 함수 init는 상위 클래스와 동일한 이름의 메서드를 호출할 수 없습니다.

부모 클래스에서 같은 이름의 메소드를 어떻게 호출하나요?

다음과 같이 인스턴스화된 객체에 대한 기본 속성을 제공하여 상위 클래스(생성자)의 프로토타입을 가리킬 수 있습니다.

 // 当前是否处于创建类的阶段<br>
var initializing = false;<br>
function jClass(baseClass, prop) {<br>
// 只接受一个参数的情况 - jClass(prop)<br>
if (typeof (baseClass) === "object") {<br>
prop = baseClass;<br>
baseClass = null;<br>
}<br>
// 本次调用所创建的类(构造函数)<br>
function F() {<br>
// 如果当前处于实例化类的阶段,则调用init原型函数<br>
if (!initializing) {<br>
// 如果父类存在,则实例对象的base指向父类的原型<br>
// 这就提供了在实例对象中调用父类方法的途径<br>
if (baseClass) {<br>
this.base = baseClass.prototype;<br>
}<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
// 如果此类需要从其它类扩展<br>
if (baseClass) {<br>
initializing = true;<br>
F.prototype = new baseClass();<br>
F.prototype.constructor = F;<br>
initializing = false;<br>
}<br>
// 覆盖父类的同名函数<br>
for (var name in prop) {<br>
if (prop.hasOwnProperty(name)) {<br>
F.prototype[name] = prop[name];<br>
}<br>
}<br>
return F;<br>
};<br>
로그인 후 복사
호출 방법:
 var Person = jClass({<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
});<br>
var Employee = jClass(Person, {<br>
init: function(name, employeeID) {<br>
// 调用父类的原型函数init,注意使用apply函数修改init的this指向<br>
this.base.init.apply(this, [name]);<br>
this.employeeID = employeeID;<br>
},<br>
getEmployeeID: function() {<br>
return this.employeeID;<br>
},<br>
getName: function() {<br>
// 调用父类的原型函数getName<br>
return "Employee name: " + this.base.getName.apply(this);<br>
}<br>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "Employee name: ZhangSan"<br>
로그인 후 복사

지금까지 1장에서 상속 수동 구현의 단점을 수정했습니다. 사용자 정의 jClass 함수를 통해 클래스와 하위 클래스를 생성하고, 프로토타입 메소드 init를 통해 데이터를 초기화하고, 인스턴스 속성 베이스를 통해 상위 클래스의 프로토타입 함수를 호출합니다.

유일한 단점은 부모 클래스를 호출하는 코드가 너무 길고 이해하기 어렵다는 점입니다.

 var Employee = jClass(Person, {<br>
init: function(name, employeeID) {<br>
// 如果能够这样调用,就再好不过了<br>
this.base(name);<br>
this.employeeID = employeeID;<br>
}<br>
});<br>
로그인 후 복사

jClass 기능 최적화

 // 当前是否处于创建类的阶段<br>
var initializing = false;<br>
function jClass(baseClass, prop) {<br>
// 只接受一个参数的情况 - jClass(prop)<br>
if (typeof (baseClass) === "object") {<br>
prop = baseClass;<br>
baseClass = null;<br>
}<br>
// 本次调用所创建的类(构造函数)<br>
function F() {<br>
// 如果当前处于实例化类的阶段,则调用init原型函数<br>
if (!initializing) {<br>
// 如果父类存在,则实例对象的baseprototype指向父类的原型<br>
// 这就提供了在实例对象中调用父类方法的途径<br>
if (baseClass) {<br>
this.baseprototype = baseClass.prototype;<br>
}<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
// 如果此类需要从其它类扩展<br>
if (baseClass) {<br>
initializing = true;<br>
F.prototype = new baseClass();<br>
F.prototype.constructor = F;<br>
initializing = false;<br>
}<br>
// 覆盖父类的同名函数<br>
for (var name in prop) {<br>
if (prop.hasOwnProperty(name)) {<br>
// 如果此类继承自父类baseClass并且父类原型中存在同名函数name<br>
if (baseClass &&<br>
typeof (prop[name]) === "function" &&<br>
typeof (F.prototype[name]) === "function") {<br><br>
// 重定义函数name - <br>
// 首先在函数上下文设置this.base指向父类原型中的同名函数<br>
// 然后调用函数prop[name],返回函数结果<br><br>
// 注意:这里的自执行函数创建了一个上下文,这个上下文返回另一个函数,<br>
// 此函数中可以应用此上下文中的变量,这就是闭包(Closure)。<br>
// 这是JavaScript框架开发中常用的技巧。<br>
F.prototype[name] = (function(name, fn) {<br>
return function() {<br>
this.base = baseClass.prototype[name];<br>
return fn.apply(this, arguments);<br>
};<br>
})(name, prop[name]);<br><br>
} else {<br>
F.prototype[name] = prop[name];<br>
}<br>
}<br>
}<br>
return F;<br>
};<br>
로그인 후 복사
이 시점에서 클래스와 하위 클래스 생성 및 호출 방법이 매우 우아합니다. 다음을 참조하세요.
 var Person = jClass({<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
});<br>
var Employee = jClass(Person, {<br>
init: function(name, employeeID) {<br>
this.base(name);<br>
this.employeeID = employeeID;<br>
},<br>
getEmployeeID: function() {<br>
return this.employeeID;<br>
},<br>
getName: function() {<br>
return "Employee name: " + this.base();<br>
}<br>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "Employee name: ZhangSan"<br>
로그인 후 복사

지금까지 우리는 JavaScript에서 클래스와 상속을 보다 우아한 방식으로 구현하는 데 도움이 되는 완전한 함수 jClass를 만들었습니다.

다음 장에서는 인터넷에서 더 널리 사용되는 JavaScript 클래스의 구현과 상속을 연속적으로 분석합니다. 그러나 모든 것은 동일하게 유지되며 이러한 구현은 단지 보다 우아한 호출 방식을 위해 이 장에서 언급한 개념을 뒤흔드는 "과장"에 지나지 않습니다.

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!