ホームページ  >  記事  >  ウェブフロントエンド  >  JS オブジェクトを作成する一般的な方法は何ですか?

JS オブジェクトを作成する一般的な方法は何ですか?

零下一度
零下一度オリジナル
2017-06-28 09:55:261220ブラウズ

はじめに

「js言語ではすべてがオブジェクトである」という言葉があるように、オブジェクトを作成する方法はたくさんありますので、今日は最も簡単な方法を整理します

JavaScript はい: オブジェクト リテラル形式、またはオブジェクト コンストラクターを使用します

オブジェクト リテラル形式

1 var person = new Object();2   person.name = "jack";3   person.sayName = function () {4   alert(this.name)5 }

オブジェクト コンストラクターを使用します

1 var person = {2   name: "jack";3   sayName: function () {4     alert(this.name)5   }6 }

明らかな欠点: 複数のオブジェクトを作成する場合、コードの重複が発生するため、「ファクトリー パターン」が登場しました

ファクトリーパターン

ファクトリーパターンを簡単に理解すると、ファクトリー: 「私はオブジェクトを作成し、作成プロセス全体に責任を負いますが、タスクが完了した後は、私はそれとは何の関係もありません。O ( ∩_∩)おはは〜”

 1 function createPerson (name) { 2   var o = new Object(); 3   o.name = name; 4   o.sayName = function () { 5     alert(this.name) 6   } 7   return o 8 } 9 10 var p1 = new createPerson("jack");
明らかな欠点: すべてのオブジェクト インスタンスは `Object` 型であり、型の区別はほとんどありません。タイプを区別できないと言うなら、私は信じません。次に、コードを見てみましょう:

1 var p1 = new createPerson("jack");2 var p2 = new createPerson("lucy");3 4 console.log(p1 instanceof Object); //true5 console.log(p2 instanceof Object); //true
ほら、これは事実ですか? この問題を解決するには、「コンストラクター パターン」を使用します

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

これは関数です。私は特定の型のオブジェクト インスタンスのみを作成し、それ以外のことは気にしません (ここにはすでに型の概念があることに気づきましたか、小さなグループのように感じられます)

 1 function Person (name) { 2   this.name = name; 3   this.sayName = function () { 4     alert(this.name) 5   } 6 } 7  8 function Animal (name) { 9   this.name = name;10   this.sayName = function () {11     alert(this.name)12   }13 }14 15 var p1 = new Person("jack")16 p1.sayName() //"jack"17 18 var a1 = new Animal("doudou")19 a1.sayName() //"doudou"20 21 console.log(p1 instanceof Person) //true22 console.log(a1 instanceof Animal) //true23 console.log(p1 instanceof Animal) //false(p1显然不是Animal类型,所以是false)24 console.log(a1 instanceof Person) //false(a1也显然不是Person类型,所以同样是false)
上記のコードは次のことを証明します。コンストラクター パターンは実際にオブジェクトの種類を区別できます。では、このモデルは完璧なのでしょうか? しかし、そうではありません。次のコードを見てみましょう:

1 //接着上面的代码2 console.log(p1.sayName === a1.sayName) //false
問題は見つかりましたか? 「p1」の「sayName」は「a1」の「sayName」と同じではありません。これは何を意味しますか? 「コンストラクター パターン」には「パブリック」という概念がまったくなく、作成された各オブジェクト インスタンスには独自のプロパティとメソッドのセットがあることは理解できますが、メソッドを実行する必要があります。明らかな欠点: 上で説明したように、この問題を解決するために、新しいモード「プロトタイプ モード」が登場しました。このモードは単にステージをジャンプするだけです。以下で「プロトタイプ パターン」を見てみましょう

プロトタイプ パターン

ここで覚えておくべきことが 1 つあります。コンストラクター内のプロパティとメソッドは各オブジェクト インスタンス間で共有されず、すべて個別に設定されます。共有を実現したい場合は、コンストラクターのプロトタイプにプロパティやメソッドを格納する必要があります。この文はどういう意味ですか?以下、詳しく説明していきます

コンストラクタを作成すると(通常の関数も同様です)`prototype`(プロトタイプ)が自動生成されます コンストラクターと`prototype`は1対1の関係にあります。今回は `prototype` にあります `constructor` 属性は 1 つだけです (明らかに別の `__proto__` があります。これについてはここでは説明しません。後で説明します)


この `constructor` とは何ですか?これは、`プロトタイプ` のコンストラクターを指す、ポインターに似た参照であり、ポインターはデフォルトで存在する必要があります

1 console.log(Person.prototype.constructor === Person) //true

先ほど、`プロトタイプ` は `自動生成` だと言いましたが、実際には存在しません。これは、`prototype` を手動で生成するもう 1 つの方法です:
1 function Person (name) {2   this.name = name3 }4 Person.prototype = {5   //constructor: Person,6   age: 307 }8 console.log(Person.prototype) //Object {age: 30}9 console.log(Person.prototype.constructor === Person) //false

ヒント: `prototype` がコンストラクターに対して手動で作成できることを証明するために、ここでは `name` 属性が `prototype` に追加されています。
次のコード行に問題があることに気づいたかもしれません:

1 console.log(Person.prototype.constructor === Person) //false

結果が `false` なのはなぜですか?兄さん、先ほどの「プロトタイプ」はデフォルトで生成されたものですが、別の方法、つまり手動設定を使用しました。手動設定の原理を詳しく分析してみましょう:
1. コンストラクターの `prototype` は実際にはオブジェクトです


2. `prototype` をこのように設定すると、元の `person.prototype` は実際には次のようになります。その後、別のオブジェクトが再度参照されます

3. このとき、コンストラクターは `prototype` を見つけることができますが、 `prototype` はコンストラクターを見つけることができません

1 Person.prototype = {2   //constructor: Person, // 因为constructor属性,我没声明啊,prototype就是利用它来找到构造函数的,你竟然忘了声明3   age: 304 }

4.コンストラクターの手動設定 関数間の接続を失わずに関数のプロトタイプを設定するには、次のようにします:
1 function Person (name) {2   this.name = name3 }4 Person.prototype = {5   constructor: Person, //constructor一定不要忘了!!6   age: 307 }

画外音:“说到这里,你还没有讲原型模式是如何实现属性与方法的共享啊”,不要急,马上开始:

对象实例-构造函数-原型,三者是什么样的关系呢?

看明白这张图的意思吗?
1.当对象实例访问一个属性时(方法依然),如果它自身没有该属性,那么它就会通过`__proto__`这条链去构造函数的`prototype`上寻找
2.构造函数与原型是一对一的关系,与对象实例是一对多的关系,而并不是每创建一个对象实例,就相应的生成一个`prototype`
这就是原型模式的核心所在,结论:在原型上声明属性或方法,可以让对象实例之间共用它们

然后原型模式就是完美的吗?并不是,它有以下两个主要问题:
问题1:如果对象实例有与原型上重名的属性或方法,那么,当访问该属性或方法时,实例上的会屏蔽原型上的

1 function Person (name) {2   this.name = name3 }4 Person.prototype = {5   constructor: Person,6   name: 'lucy'7 }8 var p1 = new Person('jack');9 console.log(p1.name); //jack

问题2:由于实例间是共享原型上的属性和方法的,所以当其中一个对象实例修改原型上的属性(基本值,非引用类型值或方法时,其他实例也会受到影响


原因就是,当实例自身的基本值属性与原型上的重名时,实例就会创建该属性,留着今后自己使用,而原型上的属性不会被修改;但如果属性是引用类型值,如:`Array`、`Object`,当发生重名时,实例是不会拷贝一份新的留给自己使用的,还是坚持实例间共享,所以就会出现上图中的情况

以上两个问题就是原型模式的明显缺点,为了改掉这些缺点,我们一般会采用一种组合模式“组合使用构造函数模式和原型模式”,其实在原型模式这一节,该模式已经有所应用了

组合使用构造函数模式和原型模式

这种模式可谓是集构造函数模式和原型模式之所长,用构造函数模式来定义对象实例的属性或方法,而共享的属性或方法就交给原型模式

 1 function Person (name) { 2   this.name = name //实例的属性,在构造函数中声明 3 } 4  5 Person.prototype = { 6   constructor: Person, 7   sayName: function () { //共享的方法存在原型中 8     alert(this.name) 9   }10 }

注:此模式目前是ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法

-----------------

下面要介绍的几个模式是针对不同场景的,而不是说`组合使用构造函数模式和原型模式`有什么缺点,又用这几个模式来弥补,不是这样的

动态原型模式

特点:共享的方法是在构造函数中检测并声明的,原型并没有被显示创建

 1 function Person (name) { 2   this.name = name; 3   if (typeof this.sayName !== 'function') { //检查方法是否存在 4     console.log('sayName方法不存在') 5     Person.prototype.sayName = function () { 6       alert(this.name) 7     } 8   } else { 9     console.log('sayName方法已存在')10   }11 }12 13 var p1 = new Person('jack'); //'sayName方法不存在'14 p1.sayName(); //因为sayName不存在,我们来创建它,所以这里输出'jack'15 var p2 = new Person('lucy'); //'sayName方法已存在'16 p2.sayName(); //这时sayName已存在,所以输出'lucy'

当`Person`构造函数第一次被调用时,`Person.prototype`上就会被添加`sayName`方法;《Javascript高级程序设计》一书说到:使用动态原型模式时,不能使用对象字面量重写原型。我们来理解一下:

 

分析:
1.`p1`实例创建,此时原型没有`sayName`方法,那我们就为原型添加一个
2.随后,我们以字面量的形式重写了原型,这时旧的原型并没有被销毁,而且它和`p1`还保持着联系
3.之后的实例,也就是这里的`p2`,都是与新原型保持联系;所以`p1`、`p2`有各自的构造器原型,即使它们的构造器是同一个

所以切记:当我们采用动态原型模式时,千万不要以字面量的形式重写原型

寄生构造函数模式

了解此模式之前,我们先来想一个问题:构造函数为什么要用`new`关键字调用?代码说话:

我们发现什么?如果不是`new`方法调用构造函数,那么就要显式的`return`,否则构造函数就不会有返回值;但如果使用`new`,那就没有这个问题了

下面我们再来看寄生构造函数模式:

 1 function Person (name) { 2   var o = new Object(); 3   o.name = name; 4   o.sayName = function () { 5     alert(this.name) 6   }; 7   return o 8 } 9 10 var p1 = new Person('jack'); //与工厂模式唯一不同之处:使用new调用11 p1.sayName(); //jack

其实new不new都无所谓,因为我们已经显式的return o

那么寄生构造函数模式到底有什么应用场景呢?据《javascript高级程序设计》一书记载,举例:如果我们想创建一个具有额外方法的特殊数组,那么我们可以这样做:

 1 function SpecialArray () { 2   var values = new Array(); 3   Array.prototype.push.apply(values,arguments); 4     values.toPipedString = function () { 5     return this.join('|') 6   } 7   return values 8 } 9 10 var colors = new SpecialArray('red','blue','green');11 alert(colors.toPipedString()) //'red|blue|green'

最后重要的一点:该模式和构造函数和原型无缘,也就是不能区分实例类型,因为该模式生成的实例,它的构造函数都是Object,原型都是Object.prototype

稳妥构造函数模式

该模式与寄生构造函数相比,主要有两点不同:
1.创建对象实例的方法不引用this
2.不使用new操作符调用构造函数
按照稳妥构造函数的要求,可以将前面的Person构造函数重写如下:

1 function Person (name) {2   var o = new Object();3   o.sayName = function () {4     alert(name) //这里其实涉及到了闭包的知识,因此产生了私有属性的概念5   }6   return o7 }

此模式最适合在一些安全的环境中(这些环境中会禁止使用this和new),同理,此模式与构造函数和原型也无缘

结语

以上就是对js中创建对象的方式的总结,希望对大家有所帮助

以上がJS オブジェクトを作成する一般的な方法は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。