JavaScript でオブジェクトを作成するさまざまなモードの分析 (グラフィック チュートリアル)

亚连
リリース: 2018-05-21 14:15:04
オリジナル
1284 人が閲覧しました

ここで、JavaScript でオブジェクトを作成するためのさまざまなパターンの分析をお届けします。共有して参考にしてください

JavaScriptでのオブジェクトの作成

•ファクトリパターン

•コンストラクターパターン

•プロトタイプパターン

•コンストラクターとプロトタイプのパターンの組み合わせ

•プロトタイプの動的パターン

ほとんどのオブジェクト指向言語にはクラスの概念があり、それを通じて同じメソッドとプロパティを持つ複数のオブジェクトを作成できます。技術的に言えば、JavaScript はオブジェクト指向言語ですが、JavaScript にはクラスの概念がなく、すべてがオブジェクトです。すべてのオブジェクトは、既存の参照型を通じて作成される特定の参照型のインスタンスです。参照型はネイティブまたはカスタマイズできます。ネイティブ参照タイプには、オブジェクト、配列、データ、正規表現、および関数が含まれます。 !参照型は、データと機能を編成するデータ構造であり、多くの場合クラスと呼ばれます。 クラスの概念がない JavaScript では、オブジェクトをいかに効率的に作成するかが解決すべき問題となります。

1.1.0. オブジェクトを作成する一般的なメソッド

var person = {}; //对象字面量表示,等同于var person = new Objcect();
person.name = 'evansdiy';
person.age = '22';
person.friends = ['ajiao','tiantian','pangzi'];
person.logName = function() {
  console.log(this.name);
}
ログイン後にコピー

オブジェクト参照型に基づいて、4 つのプロパティを含むオブジェクトが作成され、そのうちの 1 つはメソッドです。人物のようなオブジェクトのインスタンスが多数必要な場合、重複するコードが大量に存在することになります。

1.1.1. ファクトリパターン

オブジェクトの詳細を含むことができる関数を通じてオブジェクトを作成し、そのオブジェクトを返します。

function person(name,age,friends) {

  var o = {
    name: name,
    age: age,
    friends: friends,
    logName: function() {
      console.log(this.name);
    }
  };

  return o;

}

var person1 = person('Evansdiy','22',['ajiao','tiantian','pangzi']);
ログイン後にコピー

person 関数が呼び出されるたびに、関数内のオブジェクト o を通じて新しいオブジェクトが作成され、返されます。これ以外に、新しいオブジェクトを作成するために存在するこの内部オブジェクト o には他の目的はありません。さらに、ファクトリ パターンによって作成されたオブジェクトの種類を判断することはできません。

1.1.2. コンストラクター パターン

function Person(name,age,job) {

  this.name = name;
  this.age = age;
  this.job = job;
  this.logName = function() {
    console.log(this.name);
  }

}

//通过new操作符创建Person的实例
var person1 = new Person('boy-a','22','worker');

var person2 = new Person('girl-b','23','teacher');

person1.logName(); //boy-a

person2.logName(); //girl-a
ログイン後にコピー

ファクトリ パターンを比較すると、ここでは中間オブジェクトを作成する必要がなく、リターンがないことがわかります。さらに、コンストラクターのインスタンスを特定の型として識別できるため、オブジェクト識別の問題が解決されます (インスタンスのコンストラクター プロパティを確認するか、instanceof 演算子を使用してインスタンスが特定のコンストラクターを通じて作成されたかどうかを確認することによって)。 。

console.log(person1.constructor == Person);//コンストラクターはコンストラクターのプロトタイプ内にあり、コンストラクターを指しており、結果は true です

console.log(person1instanceof Person);// instanceof 演算子 person1 がコンストラクター person のインスタンスであるかどうか。ただし、コンストラクター パターンにも独自の問題があります。インスタンス化によって作成されたメソッドは同じではないことに注意してください。コードは false になります:

console.log(person1.logName == person2.logName); //false この問題を解決するには、メソッドをコンストラクターの外に移動します (グローバル関数になります)。グローバル コンテキストの下で作成されたグローバル関数は、実際には Person によって作成されたインスタンスによってのみ呼び出すことができます。これはその名前に少しふさわしくありません。メソッドが多数ある場合、それらを 1 つずつ定義する必要があり、カプセル化が不足しています。

1.1.3. プロトタイプ モード

JavaScript のすべての関数には、プロトタイプ属性へのポインターが含まれています (ほとんどのブラウザーは、内部属性 __proto__ を通じてアクセスできます)。プロトタイプ属性は、すべての関数で共有されるプロパティとメソッドを含むオブジェクトです。参照型によって作成されたインスタンス。

function logName() {
  console.log(this.name);
}

function logAge() {
  console.log(this.age);
}
ログイン後にコピー

上記のコードは次のことを行います:

1. コンストラクター関数 person を定義します。これには、デフォルトでは

を指すコンストラクター属性のみが含まれます。 3 つのプロパティを追加し、そのうちの 1 つはメソッドとして機能します。

3. Person のインスタンスを作成し、そのインスタンスで logName() メソッドを呼び出します。 !

ここで注意する必要があるのは、logName() メソッドの呼び出しプロセスです:

1. person1 インスタンスで logName() メソッドを検索すると、そのようなメソッドがないことがわかり、プロトタイプまでトレースバックします。 person1 のプロトタイプを検索する logame() メソッドにはこのメソッドがあり、このメソッドを呼び出すことで、プロトタイプの同じ名前の属性にインスタンスがアクセスすることを防ぐことができます。インスタンス上のプロトタイプ内の同じ名前の属性。この際、プロトタイプ上の同じ名前のプロパティは削除されず、インスタンスへのアクセスが禁止されるだけであることに注意してください。

var person2 = new Person();

person2.name = 'laocai'; インスタンスの属性が不要になった場合は、delete 演算子を使用して削除できます。

delete person2.name; for-in ループを使用して、インスタンスによってアクセスできるすべてのプロパティ (プロパティがインスタンスに存在するかプロトタイプに存在するかに関係なく) を列挙します。 hasOwnProperty() メソッドは、属性がインスタンス上に存在するか、プロトタイプ内に存在するかを判断します。属性がインスタンス内に存在する場合にのみ true が返されます。

console.log(person1.hasOwnProperty('name'));//true!hasOwnProperty来自Object的原型,是javascript中唯一一个在处理属性时不查找原型链的方法。[via javascript秘密花园] 另外,也可以通过同时使用in操作符和hasOwnProperty()方法来判断某个属性存在于实例中还是存在于原型中:

console.log(('friends' in person1) && !person1.hasOwnProperty('friends'));先判断person1是否可以访问到friends属性,如果可以,再判断这个属性是否存在于实例当中(注意前面的!),如果不存在于实例中,就说明这个属性存在于原型中。 前面提到,原型也是对象,所以我们可以用对象字面量表示法书写原型,之前为原型添加代码的写法可以修改为:

Person.prototype = {

  name: 'evansdiy',
  friends: ['ajiao','jianjian','pangzi'],
  logName: function() {
    console.log(this.name);
  }

}
ログイン後にコピー

由于对象字面量语法重写了整个prototype原型,原先创建构造函数时默认取得的constructor属性会指向Object构造函数:

//对象字面量重写原型之后
console.log(person1.constructor);//Object不过,instanceof操作符仍会返回希望的结果:

//对象字面量重写原型之后
console.log(person1 instanceof Person);//true当然,可以在原型中手动设置constructor的值来解决这个问题。

Person.prototype = {

  constructor: Person,
  ......

}
ログイン後にコピー

如果在创建对象实例之后修改原型对象,那么对原型的修改会立即在所有对象实例中反映出来:

function Person() {};

var person1 = new Person();

Person.prototype.name = 'evansdiy';

console.log(person1.name);//'evansdiy'
ログイン後にコピー

实例和原型之间的连接仅仅是一个指针,而不是一个原型的拷贝,在原型实际上是一次搜索过程,对原型对象的所做的任何修改都会在所有对象实例中反映出来,就算在创建实例之后修改原型,也是如此。 如果在创建对象实例之后重写原型对象,情况又会如何?

function Person() {};

var person1 = new Person1();//创建的实例引用的是最初的原型

//重写了原型
Person.prototype = {
  friends: ['ajiao','jianjian','pangzi']
}

var person2 = new Person();//这个实例引用新的原型

console.log(person2.friends);

console.log(person1.friends);
ログイン後にコピー

以上代码在执行到最后一行时会出现未定义错误,如果我们用for-in循环枚举person1中的可访问属性时,会发现,里头空无一物,但是person2却可以访问到原型上的friends属性。 !重写原型切断了现有原型与之前创建的所有对象实例的联系,之前创建的对象实例的原型还在,只不过是旧的。

//创建person1时,原型对象还未被重写,因此,原型对象中的constructor还是默认的Person()
console.log(person1.constructor);//Person()

//但是person2的constructor指向Object()
console.log(person2.constructor);//Object()
ログイン後にコピー

需要注意的是,原型模式忽略了为构造函数传递参数的过程,所有的实例都取得相同的属性值。同时,原型模式还存在着一个很大的问题,就是原型对象中的引用类型值会被所有实例共享,对引用类型值的修改,也会反映到所有对象实例当中。

function Person() {};

Person.prototype = {
  friends: ['ajiao','tiantian','pangzi']
}

var person1 = new Person();

var person2 = new Person();

person1.friends.push('laocai');

console.log(person2.friends);//['ajiao','tiantian','pangzi','laocai']
ログイン後にコピー

修改person1的引用类型值friends,意味着person2中的friends也会发生变化,实际上,原型中保存的friends实际上只是一个指向堆中friends值的指针(这个指针的长度是固定的,保存在栈中),实例通过原型访问引用类型值时,也是按指针访问,而不是访问各自实例上的副本(这样的副本并不存在)。

1.1.4.结合构造函数和原型模式创建对象

结合构造函数和原型模式的优点,弥补各自的不足,利用构造函数传递初始化参数,在其中定义实例属性,利用原型定义公用方法和公共属性,该模式应用最为广泛。

function Person(name,age) {

  this.name = name;
  this.age = age;
  this.friends = ['ajiao','jianjian','pangzi'];

}

Person.prototype = {

  constructor: Person,
  logName: function() {
    console.log(this.name);
  }

}

var person1 = new Person('evansdiy','22');

var person2 = new Person('amy','21');

person1.logName();//'evansdiy'

person1.friends.push('haixao');

console.log(person2.friends.length);//3
ログイン後にコピー

1.1.5.原型动态模式

原型动态模式将需要的所有信息都封装到构造函数中,通过if语句判断原型中的某个属性是否存在,若不存在(在第一次调用这个构造函数的时候),执行if语句内部的原型初始化代码。

function Person(name,age) {

  this.name = name;
  this.age = age;

  if(typeof this.logName != 'function') {
    Person.prototype.logName = function() {
      console.log(this.name);
    };
    Person.prototype.logAge = function() {
      console.log(this.age);
    };
  };

}

var person1 = new Person('evansdiy','22');//初次调用构造函数,此时修改了原型

var person2 = new Person('amy','21');//此时logName()方法已经存在,不会再修改原型
ログイン後にコピー

需要注意的是,该模式不能使用对象字面量语法书写原型对象(这样会重写原型对象)。若重写原型,那么通过构造函数创建的第一实例可以访问的原型对象不会包含if语句中的原型对象属性。

function Person(name,age) {

  this.name = name;
  this.age = age;

  if(typeof this.logName != 'function') {
    Person.prototype = {
      logName: function() {
        console.log(this.name);
      },
      logAge: function() {
        console.log(this.Age);
      }
    }
  };

}

var person1 = new Person('evansdiy','22');

var person2 = new Person('amy','21');

person2.logName();//'amy'

person1.logName();//logName()方法不存在
ログイン後にコピー

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

详细解读JavaScript设计模式开发中的桥接模式(高级篇)

详细解读在JavaScript中实现设计模式中的适配器模式的方法(图文教程)

设计模式中的facade外观模式在JavaScript开发中的运用(高级篇)

以上がJavaScript でオブジェクトを作成するさまざまなモードの分析 (グラフィック チュートリアル)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート