基本原則
js では、変数には 5 つの基本型と複雑なデータ型のオブジェクトが含まれます。 もちろん、一般的に使用される関数と配列はすべてオブジェクトです。基本タイプと複合タイプには、スタック ストレージとヒープ ストレージという 2 つの異なるストレージ方法があります。 2 つの格納方法が実装されている理由は非常に単純です。基本型が初期化されるとメモリ サイズは固定され、変数にアクセスするということは、変数のメモリ内にある実際のデータにアクセスすることを意味し、これを値によるアクセスと呼びます。 。オブジェクト タイプはある時点でサイズが増加する可能性があり、メモリ サイズは固定されていません。たとえば、オブジェクトの属性を動的に追加したり、配列のサイズを動的に増加したりすると、変数のサイズが増加し、スタック上に維持できなくなります。したがって、js はオブジェクト型変数をヒープに配置し、インタプリタが必要に応じてメモリを割り当て、オブジェクトの参照ポインタを通じてアクセスできるようにします。ヒープ内のオブジェクトのメモリ アドレス サイズは固定されているため、 be メモリ アドレスはスタック メモリへの参照に格納されます。この方法は参照によるアクセスと呼ばれます。 これを理解することが重要です。そうすれば、将来のプログラミングで多くの問題を回避できます。 次のコードを見てみましょう:
var a = 'I am a string.'; //a,b,c的变量中保存的都是实际的值,因为他们是基本类型的变量 var b = 1010; var c = false; var d = a; //d中保存着和“a值一样的副本,它们互不影响” a = 'I am different from d'; alert(d); //输出'I am a string'
上記のコードは理解しやすいです。つまり、値によってアクセスされる変数のコピーです。「あなたのものはあなたのもの、私のものは私のもの、私たちは皆、コピーを持っていますが、持っていません」そして、値によってアクセスされる変数の場合、参照によるアクセスは少し異なります:
var e = { name : 'I am an object', setName : function(name){ this.name = name; } }; var f = e; //赋值操作,实际上的结果是e,f都是指向那个对象的引用指针 f.setName('I am different from e,I am object f.'); alert(e.name); //对f进行操作,e的值也改变了!
端的に言えば、オブジェクトのポインターは同じエンティティ オブジェクトを指します。はコピーではなく、元のオブジェクトは 1 つだけ残ります。良い。以上が、基本型と参照型の最大かつ最も基本的な違いです。それをわかりやすく表現するために画像を使用します。
* 基本型の変数とオブジェクト ポインタはスタック メモリに格納され、オブジェクト エンティティはヒープに格納されます
* 前後をコピーしますスタックとヒープ
参照型によって引き起こされる問題
の状況 1. プロトタイプ モデルを使用してオブジェクトを作成する問題
JavaScript OO (オブジェクト指向) において、プロトタイプ モデルを使用する最大の利点は、 create object は、オブジェクト インスタンスを作成し、プロトタイプに含まれるプロパティとメソッドを共有できることです。これにより、コンストラクター パターンの欠点が回避されます。つまり、各オブジェクトが各メソッドのコピーを持ち、各メソッドが各インスタンスで再作成されるため、メソッドの再利用が無意味になります。
さて、プロトタイプパターンを使用すると、すべてのインスタンスでメソッドが共有されますが、プロトタイプ内に参照型の値を持つプロパティがある場合、問題が発生します:
var Person = function(){ }; Person.prototype = { constructor : Person, name : 'Hanzongze', hobby : ['basketable', 'swiming', 'running'], //注意,这里包含着一个引用类型的属性 sayName : function(){ alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); person1.hobby.push('music'); alert(person2.hobby); //输出为'basketable', 'swiming', 'running','music' alert(person1.hobby === person2.hobby); //true
趣味のプロパティは参照型の値であるため、それは次のように使用されますPerson コンストラクター 作成されたインスタンスの趣味属性はすべてこの参照エンティティを指し、インスタンス オブジェクト間の属性は相互に干渉します。これは私たちが望む結果ではありません。この種の問題を回避するための解決策は、コンストラクター モデルとプロトタイプ モデルを組み合わせて使用することです:
var Person = function(){ this.name = 'Hanzongze'; this.hobby = ['basketable', 'swiming', 'running']; //对引用类型的值使用构造函数模式 }; Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); person1.hobby.push('music'); alert(person2.hobby); //输出 'basketable', 'swiming', 'running',说明对person1的修改没有影响到person2 alert(person1.hobby === person2.hobby); //false
2. プロトタイプ継承の問題
前の例はプロトタイプの継承のコンテキストで発生します。プロトタイプチェーンの継承問題を見てみましょう:
var Person = function(){ this.name = 'Hanzongze'; this.hobby = ['basketable', 'swiming', 'running']; }; Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } }; //子类型Student function Student(){ } Student.prototype = new Person(); //Student继承了Person var student1 = new Student(); var student2 = new Student(); student1.hobby.push('music'); //对子类实例student1的引用属性做了改动 var student3 = new Student(); alert(student2.hobby); //输出'basketable', 'swiming', 'running', 'music' alert(student3.hobby); //输出'basketable', 'swiming', 'running', 'music'
このコードでは、サブクラス Student が親クラス Person から継承していることがわかります。ただし、プロトタイプ継承が使用されているため、つまり、親クラスのインスタンスがサブクラスのプロトタイプとして機能するため、インスタンス内の参照型属性もサブクラスのプロトタイプ プロトタイプに継承されます。サブクラスのインスタンスは参照属性を共有し、相互に影響を与えます。
解決策は、借用したコンストラクター ソリューションを使用することです (ただし、これは理想的な解決策ではありません。理想的な解決策は、プロトタイプ チェーンと借用したコンストラクターを組み合わせて使用することです。これには多くの継承パターンが含まれます。ここで簡単に説明します。詳細記事は今後書きます):
rreee