JavaScript でオブジェクトを作成するパターンの概要
**JavaScript 作成オブジェクト モード:
オブジェクトリテラル
ファクトリーモード
コンストラクター パターン
プロトタイプモード
コンストラクターとプロトタイプのパターンを組み合わせる
プロトタイプダイナミックモード
**
ほとんどのオブジェクト指向言語にはクラスの概念があり、それを通じて同じメソッドとプロパティを持つ複数のオブジェクトを作成できます。技術的に言えば、JavaScript はオブジェクト指向言語ですが、JavaScript にはクラスの概念がなく、すべてがオブジェクトです。すべてのオブジェクトは、既存の参照型を通じて作成される特定の参照型のインスタンスです。参照型はネイティブまたはカスタマイズできます。
1. オブジェクト リテラル
var person = { name : 'Nicholas'; age : '22'; job :"software Engineer" sayName: function() { alter(this.name); } }
この例では、person という名前のオブジェクトが作成され、それに 3 つの属性 (名前、年齢、職業) とメソッド (sayName()) が追加されています。sayName() メソッドは this.name( is を表示するために使用されます。 person.name の値として解析されます)。
オブジェクト リテラルを使用して単一のオブジェクトを作成できますが、この方法には明らかな欠点があります。同じインターフェイスを使用して多数のオブジェクトを作成すると、大量の重複コードが生成されます。
2. ファクトリーモード
ファクトリ パターンは、ソフトウェア エンジニアリングの分野でよく知られている設計パターンであり、特定のオブジェクトを作成するプロセスを抽象化し、関数を使用して特定のインターフェイスを持つオブジェクトの作成の詳細をカプセル化します。
function createPerson(name,age,job){ var o = new object{}; o.name=name; o.age=age; o.job=job; o.sayName=function(){ alert(this.name); }; return o; } var person1=creatPerson("Nicholas",22,"software Engineer"); var person2=creatPerson("Greg",24,"student");
関数 creatperson{} は、受け入れられたパラメーターに基づいて、必要なすべての情報を含む Person オブジェクトを構築できます。この関数は何度でも呼び出すことができ、そのたびに 3 つのプロパティと 1 つのメソッドを含むオブジェクトが返されます。
ファクトリー モデルは、複数の同様のオブジェクトを作成する問題を解決しますが、オブジェクト認識 (つまり、オブジェクトの種類を知る方法) の問題は解決しません。
3. コンストラクター パターン
function Person(name,age,job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); } } //通过new操作符创建Person的实例 var person1 = new Person("Nicholas",22,"software Engineer"); var person2 = new Person("Greg",24,"student"); person1.sayName(); //Nicholas person2.sayName(); //Greg
ファクトリーモードとの違いは
です作成されたオブジェクトは表示されません
このオブジェクトにプロパティとメソッドを直接割り当てます
返品ステートメントはありません
Person の新しいインスタンスを作成するには、new 演算子を使用する必要があります。コンストラクターを呼び出すための 4 つのステップ:
新しいオブジェクトを作成します
コンストラクターのスコープを新しいオブジェクトに割り当てます (これはこの新しいオブジェクトを指します)
コンストラクターでコードを実行します
新しいオブジェクトを返す
この例で作成されたすべてのオブジェクトは、Object インスタンスと Person インスタンスの両方です。これは、instanceof 演算子を通じて検証できます。
alert(person1 instanceof Object);//true
コンストラクター パターンにも独自の問題があります。実際、sayName メソッドはインスタンスごとに 1 回再作成されます。次のコードでわかるように、インスタンス化によって作成されたメソッドは同等ではないことに注意してください。
alert(person1.sayName == person2.sayName);//false
function Person(name,age,job) { this.name = name; this.age = age; this.job = job; } function sayName() { alert(this.name); }
4. プロトタイプモード
JavaScript で作成されたすべての関数には、prototype 属性があります。これは、特定の型のすべてのインスタンスで共有できるプロパティとメソッドを含むオブジェクトへのポインターです (すべてのオブジェクト インスタンスでプロパティとメソッドを共有します)
function Person() {} Person.prototype.name ="Nicholas"; Person.prototype.age = 22; Person.prototype.job = "software Engineer"; Person.prototype.sayName(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); //Nicholas alert(person1.sayName == person2.sayName);//true
以上代码做了这几件事情:
定义了一个构造函数Person,Person函数自动获得一个prototype属性,该属性默认只包含一个指向Person的constructor属性
通过Person.prototype添加三个属性,和一个方法
创建一个Person的实例,随后在实例上调用了sayName()方法
JavaScript でオブジェクトを作成するパターンのまとめ_javascript ヒント
JavaScript でオブジェクトを作成するパターンのまとめ_javascript ヒント
图中展示了Person构造函数、Person的原型属性以及Person的两个实例,之间的关系。Person.prototype指向了原型对象,Person.prototype.constructor有指回了Person。原型对象中除了包含constructor属性,还包含后来添加的其他属性和方法,Person的两个实例person1和person2都包含一个内部属性,该属性仅指向Person.prototype。
sayName()方法的调用过程:
在person1实例上查找logName()方法,发现没有这个方法,于是追溯到person1的原型
在person1的原型上查找sayame()方法,有这个方法,于是调用该方法
基于这样一个查找过程,我们可以通过在实例上定义原型中的同名属性,来阻止该实例访问原型上的同名属性,需要注意的是,这样做并不会删除原型上的同名属性,仅仅是阻止实例访问。
function Person() {} Person.prototype.name ="Nicholas"; Person.prototype.age = 22; Person.prototype.job = "software Engineer"; Person.prototype.sayName(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name="Greg" alert(person1.name) //Greg 来自实例 alert(person2.name) //Nicholas 来自原型
使用delete操作符可以完全删除实例属性
delete person1.name; alert(person1.name) //Nicholas 来自原型
使用hasOwnProperty()方法可以检测一个属性是存在于实例还是原型中
function Person() {} Person.prototype.name ="Nicholas"; Person.prototype.age = 22; Person.prototype.job = "software Engineer"; Person.prototype.sayName(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); alert(person1,hasOwnProperty("name"));//false person1.name="Greg" alert(person1.name) //Greg 来自实例 alert(person1,hasOwnProperty("name"));//true alert(person2.name) //Nicholas 来自原型 alert(person2,hasOwnProperty("name"));//false delete person1.name; alert(person1.name) //Nicholas 来自原型 alert(person1,hasOwnProperty("name"));//false
下图展示了在不同情况下实例与原型之间的关系
简单的原型语法
function Person() {} Person.prototype={ name :"Nicholas", age : 22, job : "software Engineer", sayName:function(){ alert(this.name); } };
在上面的代码中constructor属性不再指向Person了,通过constructor无法确定对象的类型了。可以像下面这样特意将他设置回适当的值
function Person() {} Person.prototype={ constructor:Person, name :"Nicholas", age : 22, job : "software Engineer", sayName:function(){ alert(this.name); } };
重设constructor属性会导致它的[[Enumerable]]特性被设置为true,默认情况,原生的constructor属性是不可枚举的,可以使用Object.defineProperty()方法来改变
Object.defineProperty(Person.prototype,"constructor",{ enumerable:false, value:Person });
原型中查找值的过程是一次搜索,原型对象所做的任何修改都能从实例上立即反应出来
var friend=new Person(); Person.prototype.sayHi=function(){ alert("hi); } friend,sayHi();//"hi"(没有问题)
person实例是在添加新方法之前创建的,但仍可以访问新添加的方法,原因是实例与原型之间的松散连接关系
重写原型对象后的情况
function Person() {} var friend=new Person(); Person.prototype={ name :"Nicholas", age : 22, job : "software Engineer", sayName:function(){ alert(this.name); } }; friend.sayName();//error
调用friend.sayName()时发生错误的原因是,friend指向的原型中不包含以该字段命名的属性,如下图。
原型对象的问题
原型对象省略了为构造函数传递初始化参数这一环节,所有势力在默认情况下都取得相同的属性值。原型模型最大的问题是有其共享本性所导致的。当原型模型包含引用类型的属性来说,问题就比较严重了。来看下面的例子。
function Person() {} Person.prototype={ constructor:Person, name :"Nicholas", age : 22, job : "software Engineer", friends:["Shelby","Court"], sayName:function(){ alert(this.name); } }; var person1=new Person(); var person2=new Person(); person1.friend.push("Van"); alert(person1.friends);//"Shelby,Court,Van" alert(person2.friends);//"Shelby,Court,Van" alert(person1.friends==person2.friends);//true
5、组合使用构造函数模式和原型模式
组合使用构造函数模式和原型模式中,构造函数用于定义实例属性,原型模型用于定义方法和共享的属性。这样每个实例都会有自己的一份实例属性的副本,同时也可以共享对方法的引用,最大限度的节省了内存。
function Person(name,age,job) { this.name = name; this.age = age; this.job = job; this.friends=["Shelby","Court"]; } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } } var person1=new Person("Nicholas",22,"software Engineer"); var person2 = new Person("Greg",24,"student"); person1.friend.push("Van"); alert(person1.friends);//"Shelby,Court,Van" alert(person2.friends);//"Shelby,Court" alert(person1.friends==person2.friends);//false alert(person1.sayName==person2.sayName);//true
6、动态原型模式
原型动态模式将需要的所有信息都封装到构造函数中,通过if语句判断原型中的某个属性是否存在,若不存在(在第一次调用这个构造函数的时候),执行if语句内部的原型初始化代码。
function Person(name,age) { this.name = name; this.age = age; this.job =job; //方法 if(typeof this.sayName != 'function') { Person.prototype.sayName = function() { alert(this.name); }; } } var friend = new Person('Nicholas','22','Software Engineer');//初次调用构造函数,此时修改了原型 var person2 = new Person('amy','21');//此时sayName()方法已经存在,不会再修改原型
推奨読書:
JS オブジェクト指向の一般的なオブジェクト作成方法 (ファクトリー モード、コンストラクター モード、プロトタイプ モード)
上記はエディターが紹介したJavaScriptでのオブジェクト作成モードです。ご参考になれば幸いです。