이전 두 글에서는 자바스크립트로 프로토타입의 메모리 모델과 프로토타입을 다시 작성하는 방법, 즉 주의사항에 대해 소개했습니다. 프로토타입을 이해한 후에는 프로토타입을 통해 JavaScript 개체를 만들 수 있습니다. 프로토타입 기반 생성 방법은 효과적으로 캡슐화를 완료할 수 있지만 여전히 몇 가지 문제가 있습니다.
프로토타입을 통해 객체를 생성하면 주로 두 가지 문제가 발생합니다.
1. 생성자를 통해 객체의 속성 값을 설정할 수 없습니다.
2. 속성에 참조형 변수가 있는 경우 변수값이 중복될 수 있습니다.
다음 예를 살펴보겠습니다.
function Person(){} Person.prototype = { constructor:Person, name:"Leon", age:22, friends:["Ada","Chris"], say:function(){ console.info(this.name+"["+this.friends+"]"); } }
위 코드에서는 Person 클래스를 생성하고 프로토타입 재정의를 통해 이를 설정합니다. 일부 속성과 메서드 중 하나 친구 속성인 참조 유형의 배열입니다. 다음으로, Person 클래스를 통해 객체를 생성합니다.
var p1 = new Person(); p1.name = "John"; p1.say(); //控制台输出:Jhon[Ada,Chris]
Person 클래스를 통해 객체 p1을 생성합니다. 프로토타입 메소드를 사용하여 객체를 생성하기만 하면 됩니다. p1의 name 속성은 생성 후에만 설정됩니다. 그런 다음 p1의 say() 메서드를 호출하면 콘솔에 Jhon[Ada, Chris]가 출력됩니다.
그런 다음 개체 p1에 새 친구를 추가하면 문제가 발생합니다. 코드는 다음과 같습니다.
p1.friends.push("Mike"); //为p1增加一个朋友(注意这里是在原型中添加) p1.say();
배열의 push 메소드를 통해 p1에 새로운 친구 "Mike"를 추가합니다. 이때 객체 p1의 자체 공간에는 friends 속성이 없으므로 "Mike"가 추가됩니다. "는 아래와 같이 Person의 프로토타입에 추가됩니다.
새로 추가된 배열 요소가 프로토타입에 배치되므로 나중에 생성되는 모든 객체는 이 속성을 공유합니다. 이때 객체 p2를 생성하면 그의 친구들 중에 "Mike"도 있을 것입니다. 이는 우리가 보고 싶지 않은 결과입니다.
var p2 = new Person(); //如果p1 push之后,原型中就多了一个人,p2也多了一个朋友 p2.say();
프로토타입과 생성자를 결합하여 객체를 생성합니다
위의 문제를 해결하기 위해 프로토타입과 생성자를 결합하여 객체를 생성할 수 있습니다. 즉, 속성은 생성자에서 정의되고 메서드는 프로토타입에서 정의됩니다. 이 방법은 두 가지 장점을 효과적으로 결합하며 JavaScript에서 객체를 생성하는 데 가장 일반적으로 사용되는 방법입니다.
function Person(name,age,friends){ //属性在构造函数中定义 this.name = name; this.age = age; this.friends = friends; } Person.prototype = { //方法在原型中定义 constructor:Person, say:function(){ console.info(this.name+"["+this.friends+"]"); } }
이렇게 생성된 객체의 모든 속성은 객체 고유의 공간에 저장됩니다. 이 시점에서 객체를 생성할 때 객체에 대한 고유한 속성을 설정할 수 있습니다.
var p1 = new Person("Leon",22,["Ada","Chris"]); p1.name = "John"; p1.say(); //控制台输出: John[Ada,Chris]
위 코드를 완성한 후 Person 클래스와 p1 객체의 메모리 모델은 아래와 같습니다.
이때 When을 추가합니다. 새로운 친구가 추가되면 p1 객체의 자체 메모리 공간에 있는 friends 속성에 추가됩니다. 이런 방식으로 각 개체의 속성은 독립적이며 서로 간섭하지 않습니다.
p1.friends.push("Mike"); //为p1增加一个朋友(注意这里是在p1自己的空间中添加) p1.say(); //控制台输出: John[Ada,Chris,Mike] var p2 = new Person(); p2.say(); //控制台输出: John[Ada,Chris]
따라서 지금 객체 p2를 생성하면 객체 p2의 친구는 "Ada"와 "Chris"만 되고 "Mike"는 아닙니다.
동적 프로토타입을 사용하여 객체 생성
프로토타입과 생성자의 조합을 기반으로 객체를 생성하는 것은 완벽하지만 순수 객체지향 언어에서 객체를 생성하는 방식과는 여전히 몇 가지 차이점이 있습니다. 메소드는 클래스 외부에서 정의됩니다. 정의 객체를 객체 지향 명세의 요구 사항에 더 부합하게 만들기 위해 정의 메소드의 프로토타입 코드를 Person 생성자에 배치할 수 있습니다. 이 방법을 동적 프로토타이핑이라고 하며 객체를 생성합니다.
// 动态原型方式 function Person(name,age,friends){ this.name = name; this.age = age; this.friends = friends; Person.prototype.say = function(){ console.info(this.name+"["+this.friends+"]"); } }
동적 프로토타입을 사용하여 객체를 생성할 때 메서드 정의 시 프로토타입 재작성을 사용할 수 없다는 점에 유의하세요. 예를 들어 다음 코드는 잘못되었습니다.
// 错误的动态原型方式 function Person(name,age,friends){ this.name = name; this.age = age; this.friends = friends; //不能使用原型重写的方式来设置方法 Person.prototype = { constructor:Person, say:function(){ console.info(this.name+"["+this.friends+"]"); } } }
동적 사용 생성에도 문제가 있습니다. 클래스의 메소드는 Person.prototype.say를 통해 생성되므로 객체가 생성될 때마다 새로운 say() 메소드가 메모리에 생성됩니다. 이 문제를 해결하는 방법은 먼저 Person.prototype.say 메소드가 존재하는지 판단하고, 존재하지 않는 경우에만 생성하고 그렇지 않으면 생성되지 않는 것이다.
// 动态原型方式 function Person(name,age,friends){ this.name = name; this.age = age; this.friends = friends; //判断Person.prototype.say是否存在,不存在就创建 if(!Person.prototype.say){ alert("创建say方法"); Person.prototype.say = function(){ console.info(this.name+"["+this.friends+"]"); } } }
为了验证判断条件是否起作用,我们在代码中的判断分支中添加了一个弹出对话框语句。我们可以创建2个对象,然后2个对象分别调用say()方法,在结果中,第一个对象在调用say()方法时会弹出对话框,而第二个对象在调用say()方法时就不会在弹出对话框了,也就是说创建第二个对象时不会再添加say()方法。
以上就是JavaScript面向对象-基于组合和动态原型创建对象的内容,更多相关内容请关注PHP中文网(www.php.cn)!