ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript オブジェクト指向技術の基本チュートリアル_JavaScript スキル
JavaScript のオブジェクト指向テクノロジを紹介する記事をたくさん読んで混乱します。なぜなら、文章が下手だからではありません。
JavaScript のオブジェクトが理解できていないからです。まだ明確に説明されていませんが、読み始めるとすぐに、クラス/継承/プロトタイプ/プライベート変数のトピックに進みました。
その結果、長い間読んだ後、全体的な理解を得ることができました。よく考えてみると、何も理解できないようでした...
この記事は、 I の第 7 章、第 8 章、および第 9 章を参照して書かれています。
では、元の本の構造に従って JavaScript を説明します (オブジェクト/配列->関数-->クラス/コンストラクター/プロトタイプ)。参考までに英語の原文を添付します。
記事中の英語文(プログラム本体を除く)は、特に説明がない場合、から引用しています。 ;.
---------- ----------------------------------- ----
オブジェクトと配列
オブジェクトとは何ですか? JavaScript の
のオブジェクトは、「名前と属性」の組み合わせを 1 つの単位にまとめたものであることがわかります。 「キーと値」のペア (オブジェクトは名前付きの値のコレクションです。これらの名前付きの値は通常、オブジェクトのプロパティとして参照されます。--セクション 3.5)。他の型ではなく文字列型であり、属性の型は
であり (数値/文字列/その他のオブジェクト...) new Object() を使用して空のオブジェクトを作成することも、単に " を使用することもできます。 {}" を使用して
空のオブジェクトを作成します。オブジェクト、2 つの関数は同等です。
Js コード
var emptyObject1 = {}; //空のオブジェクトを作成します
var person = {"name":"sdcyst",
"age":18,
"sex":"male"};初期値を含むオブジェクト person
alert(person.name); //sdcyst
alert(person["age"]) //18
Js コード
var person = {};
person.name = "sdcyst" = 18; (person.name person.age); //sdcyst__18
var _person = {name:"balala","age":23}; // オブジェクトを構築するときに、属性の名前をマークできます (name)引用符なしの場合、
alert(_person["name"] person.age) //balala__23 alert(_person[name]); //未定義
一般的に、「.」演算子を使用してオブジェクトのプロパティを取得するには、プロパティの名前を知っている必要があります。 "[]" 演算子は、オブジェクトのプロパティを取得する場合により強力です。
は [] に配置できます。たとえば、
はループ内で使用できます。制御ステートメントを使用できますが、「.」演算子にはこの柔軟性がありません。
Js コード
for(var props in name) { //名前オブジェクト内のプロパティ名をループします
namestring = name[props]}
alert(namestring) ; / /NAME1NAME2NAME3NAME4
namestring = "";
for(var i=0; inamestring = name["name" (i 1)]; 🎜>alert(namestring); //NAME1NAME2NAME3NAME4
delete 演算子はオブジェクト内の特定の属性を削除でき、「in」演算子は属性が存在するかどうかを判断するために使用できます。
Js コード
var name = {"name1":"NAME1","name2":"NAME2","name3":"NAME3","name4":"NAME4"}; >var namestring = "";
for(var props in name) { //名前オブジェクト内のプロパティ名をループします
namestring = name[props];
alert(namestring); //NAME1NAME2NAME3NAME4
delete name.name1; //name1 属性を削除
delete name["name3"] //name3 属性を削除
namestring = name[props]>}
alert(namestring) //NAME2NAME4
alert(name1); //false
オブジェクト内の属性は順序どおりではないことに注意してください。
object
すべての JavaScript オブジェクトにはコンストラクター属性があります。この属性は、オブジェクトが初期化されるときのコンストラクターに対応します (関数もオブジェクトです)。
var date = new Date();
alert(date.constructor); //日付
alert(date.constructor == "日付"); //false
alert(date.constructor) == Date); //true
Array
オブジェクトは順序付けされていないデータのコレクションであり、配列は順序付けされたデータのコレクションであるとすでに述べました。 pass (0 から始まる) インデックスによってアクセスするには、
配列内のデータは任意のデータ型にすることができますが、配列自体は依然としてオブジェクトですが、配列の多くの特性により、通常、配列とオブジェクトは区別されます。 > 処理 (本書全体を通じて、オブジェクトと配列は別個のデータ型として扱われることがよくあります。
これは便利で合理的な単純化です。JavaScript プログラミングのほとんどで、オブジェクトと配列を別個の型として扱うことができます。
完全に理解するにはオブジェクトと配列の動作については、
ただし、真実を知っておく必要があります。配列は、追加の
機能の薄い層を備えたオブジェクトにすぎません。これは、typeof 演算子を使用して確認できます。配列値の場合、
文字列 "object" を返します。 --section7.5)
配列を作成するには、"[]" 演算子を使用するか、Array() コンストラクターを使用して新しい配列を作成します。 one.
var array2 = new Array(); //空の配列を作成します
array1 = [1,"s",[ 3,4],{"name1 ":"NAME1"}]; //
alert(array1[2][1]); //4 配列内の配列要素にアクセスします
alert(array1[3]) .name1); //NAME1 配列内のオブジェクトにアクセスします
alert(array1[8]); //未定義
array2 = [,,]; //カンマのみの場合は、その要素が対応するインデックスは未定義です
alert( array2.length); //3
alert(array2[1]) //未定義
new Array() を使用する場合配列を作成するには、デフォルトのサイズを指定できます。この時点では値は未定義であり、後で値を割り当てることができます。ただし、
JavaScript 内の配列の長さは任意に変更できます。 、配列の内容は任意に変更することもできます。初期の長さは実際には です。上記の
は、配列の最大長を超えるインデックスに値が割り当てられている場合、配列に拘束力を持ちません。 、配列の長さが変更され、値が割り当てられていないインデックスには未定義が割り当てられます。
var array = new Array(10); alter(array.length); //10
array[100 ] = "100th";インデックス 10 ~ 99 に対応する値
alray.length) //101
alray[87] ); //未定義
delete 演算子を使用して配列の要素を削除できます。この削除では、その位置にある配列の要素が未定義に設定されるだけであり、配列の長さは変更されないことに注意してください。
var array = new Array("n1","n2","n3","n4") ,"n5"); //5 つの要素の配列
var astring = "";
for( var i=0; i
alert(astring); //n1n2n3n4n5
delete array[3]; //配列要素の値を削除します
alert(array.length "_" array [3]) //5_未定義
array.length = 3; //配列の長さを減らす
alert(array [3]) //未定義
array.length = 8;配列の長さ
alert(array[4]); //未定義
については、結合/反転などの他のメソッドはここでは指定しません。
上記の説明により、オブジェクトの属性値は属性の名前 (文字列型) によって取得され、配列の要素は
(整数) のインデックスによって取得されることがわかりました。 type 0~~2**32-1)。配列自体もオブジェクトであるため、オブジェクト属性の操作は配列に完全に適しています。 ");
alert(array.length); //2
//配列の場合、たとえば、array[0] は array[ と同じ効果があります。 "0"] (? よくわかりませんが、テスト中はこれが当てはまります)
alert(array[0] "_" array["1"] "_" array.po );//no1_no2_props1
配列もオブジェクトであるため、配列は独自の属性を持つことができますが、属性と値は同じ概念ではありません。「no1」と「no2」は両方とも配列の値であり、array[ "po"] が配列に追加されます。属性が追加された場合、その長さは変更されません。
関数
皆さんは JavaScript 関数をたくさん書いていると思いますので、ここでは簡単に紹介します。
関数を作成します:
function f(x) {.. . .....}
var f = function(x) {......}
上記の 2 つの形式はどちらも f() という名前の関数を作成できますが、後者の形式では Anonymous を作成できます。関数
は関数定義時にパラメータを設定できますが、関数に渡されるパラメータの数が足りない場合は左から順に対応し、それ以上のパラメータが渡される場合は未定義のものが割り当てられます。 function
関数定義パラメータ番号よりも余分なパラメータは無視されます。
function myprint(s1,s2,s3) {
alert(s1 "_" s2 "_" s3); 🎜>}
myprint(); //unknown_unknown_unknown
myprint("string1","string2") //string1_string2_unknown
myprint("string1","string2","string4") ); //string1_string2_string3
したがって、定義された関数の場合、呼び出し元がすべてのパラメーターを渡すことは期待できません。使用する必要があるパラメーターは関数本体で検出される必要があります。 🎜> (! 演算子を使用)、またはデフォルト値を設定し、パラメーターを使用して or (||) 演算を実行してパラメーターを取得します。 { //デフォルトの人物 Object
"name":"name1",
"age":18,
}; //s1 は許可されません Empty
alert("s1 を入力する必要があります!");
return false;
person = person || person オブジェクトのパラメータを受け入れます
alert(s1 "_ " person.name ":" person.age ":" person.sex
}; //s1 を入力する必要があります。
); //s1_name1 :18:女性
myprint("s1",{"名前":"sdcyst","年齢":23,"性別":"男性"});
関数の引数属性
各関数本体の中に、Arguments オブジェクトを表す引数識別子があります。Arguments オブジェクトは Array (たとえば、オブジェクトにはすべて length 属性があります。その値にアクセスするには、「[]」演算子を使用してインデックスを使用してパラメーター値にアクセスします。表面上の類似点 (たとえば、Arguments オブジェクトの length プロパティを変更しても、その長さは変わりません)。 0]);
}
myargs(); //0 --- 未定義
myargs("1",[1,2]); //2 --- 1
Arguments オブジェクトには、現在の Arguments オブジェクトが配置されているメソッドを示す callee 属性があります。これを使用して、
function(x) の内部再帰呼び出しを実装できます。 ) {
if (x return x * argument.callee(x-1)
} (セクション8.2)
メソッド -- メソッド
メソッドは関数です。すべてのオブジェクトには 0 個以上の属性が含まれており、その属性はオブジェクトを含む任意のタイプであることがわかります。したがって、関数自体は
オブジェクトです。関数をオブジェクトに入れると、この関数がオブジェクトのメソッドになります。
function f1() {alert("f1");}
obj.f1 = f1; //オブジェクトにメソッドを追加します
obj.f1() のメソッドです //f1 f1 は obj
f1 のメソッドです(); //f1 f1 も関数です。
f0() は直接呼び出すことができます。 //f0 はオブジェクトを通じてのみ呼び出すことができます。 >
メソッドにはオブジェクトのサポートが必要ですが、オブジェクトのプロパティを取得するにはどうすればよいでしょうか? これは JavaScript メソッドの 、これを使用してメソッド呼び出し元 (オブジェクト) への参照を取得し、それによってメソッド呼び出し元のさまざまな属性を取得できます。
var obj = {"name":"NAME","sex ":"女性"};
obj.print = function() { //オブジェクトにメソッドを追加します
alert(this.name "_" this["sex"]);
};
obj.print(); //NAME_女性
obj.print (); //NAME_男性
オブジェクト指向の例。
var person = {name:"defaultname",
setName:function (s){
"printName ":function(){
alert(this.name);
}}
person.setName("newName");
person. printName(); //新しい名前
上記の例では、person.name=.. を使用して person の name 属性を直接変更できます。
person 属性を変更する別の方法を示します。メソッドは次のようになります。1 つは人物、もう 1 つは名前の値です。どちらのメソッドが優れているかは明らかです。例のメソッドはより鮮明で直感的で、
オブジェクト指向の影が少しあるように見えます
もう一度強調しますが、メソッド (メソッド) 自体は関数 (関数) です。メソッドの使用はさらに制限されています。以下のページでは、関数が言及されている場合、
で言及されている内容はメソッドにも適用されますが、その逆は当てはまりません。
関数のプロトタイプ属性
各関数には、プロトタイプ (プロトタイプ) 属性。この属性はオブジェクト指向 JavaScript の中心的な基盤を形成します。
クラス、コンストラクター、プロトタイプ
の 1 つを説明しましょう。 point first :上記の内容で述べたように、各関数にはプロトタイプ オブジェクトを指すプロトタイプ プロパティが含まれています (すべての
関数には、事前定義されたプロトタイプ オブジェクトを参照するプロトタイプ プロパティがあります --section8.6.2)。
を混乱させるためです。
コンストラクター:
new 演算子は、新しいオブジェクトを生成するために使用されます。これは、よくコンストラクターと呼ばれるものです。 ?
まず例を見てみましょう。
var per = new Person("sdcyst","male");
alert("name:" per.name "_sex:" per.sex); //name :sdcyst_sex:male
以下では、この作業の手順を説明します。
関数 (メソッドではなく、単なる通常の関数) を作成することから始めます。前に説明した this キーワードはオブジェクトを示します。これはメソッドを呼び出します。つまり、「メソッド」がオブジェクトを通じて呼び出された場合、 this キーワードはオブジェクトを指します (関数を直接呼び出すためにオブジェクトを使用せずに、これはスクリプト ドメイン全体を指します)。または関数が配置されているドメイン (ここでは詳しく説明しません
)。 new 演算子を使用する場合、JavaScript は最初に空のオブジェクトを作成し、次にこのオブジェクトがメソッドの this によって使用されます。 (関数) new Keyword 参照後! 次に、メソッド
で、これを操作することで、新しく作成されたオブジェクトに対応する属性が割り当てられます。このように、上記の例は非常に明確です。空のオブジェクト。次に、
が Person メソッドを呼び出してそれを割り当て、最後にオブジェクトを返します。すると、オブジェクトごとの
JavaScript では、各オブジェクトにはプロトタイプ オブジェクトを指すプロトタイプ属性があります。
new を使用してオブジェクトを作成するプロセスについては上で説明しました。実際、このプロセスでは、空のオブジェクトが作成されると、new は生成されたばかりのオブジェクトのプロトタイプ属性を操作します。
各メソッドにはプロトタイプ属性があり (メソッド自体もオブジェクトであるため)、new はそのオブジェクトのプロトタイプ属性を操作します。新しいオブジェクトのプロトタイプ属性値は、コンストラクター
のプロトタイプ属性値と一致します。このプロトタイプ オブジェクトは、最初は 1 つの属性コンストラクターのみを持ち、このコンストラクター属性はプロトタイプ オブジェクトを指します。前のセクションでは、new 演算子が新しい空のオブジェクトを作成し、そのオブジェクトのメソッドとしてコンストラクター関数を呼び出すことを示しました。ただし、空のオブジェクトを作成した後は、
new は、そのオブジェクトのプロトタイプを設定します。オブジェクトのプロトタイプは、
コンストラクター関数のプロトタイプ プロパティの値です。すべての関数には、
関数が定義されるときに自動的に作成され初期化されます。プロトタイプ プロパティの初期値は、単一のプロパティを持つオブジェクトです。このプロパティはコンストラクターと呼ばれ、プロトタイプが関連付けられているコンストラクター関数を参照します。これが、すべての
オブジェクトにコンストラクター プロパティがある理由です。このプロトタイプ オブジェクトに追加するプロパティは、コンストラクターによって初期化された
オブジェクトのプロパティのように見えます。-----セクション 9.2)
少しわかりにくいので、次の図を見てください。
このように、コンストラクターを使用して新しいオブジェクトが作成されると、コンストラクターに対応するプロトタイプ オブジェクトについて、コンストラクターのプロトタイプ属性が指すプロトタイプ オブジェクトのすべての属性が取得されます。 >実行された操作はすべて、生成されるオブジェクトに反映されます。これらのオブジェクトはすべて、コンストラクターに対応するプロトタイプ オブジェクトのプロパティ (メソッドを含む) を共有します。
具体的な例を見てみましょう。
function person(name,sex) { //コンストラクター
this.name = name;
this.sex = sex;
person.prototype.age = 12;プロトタイプ属性に対応するプロトタイプ オブジェクトの属性に値を追加
person.prototype.print = function() { //メソッドを追加
alert(this.name "_" this.sex "_" this .age);
};
var p1 = 新しい人 ("名前 1","男性");
var p2 = 新しい人 ("名前 2","男性"); print(); / /name1_male_12
p2.print(); //name2_male_12
person.prototype.age = 18; //コンストラクターのプロトタイプ属性を変更することに注意してください。操作
p1.print() ; //name1_male_18
p2.print(); //name2_male_18
次の記事では、コンストラクター メソッドを置き換えるために「クラス」という名前を使用しますが、これは単なるシミュレーションであり、実際にはそうではありません。
次の紹介の前に、まずオブジェクトのプロトタイプ属性の変更とプロトタイプ属性の設定についての注意事項を見てみましょう:
あまり適切ではない説明をします。理解するのに役立ちます: オブジェクトを新規作成すると、そのオブジェクトはコンストラクター (関数と変数を含む) のプロトタイプ属性を取得します。コンストラクター (クラス) は、そのプロトタイプ属性関数に対応するプロトタイプ オブジェクトを継承すると考えることができます。つまり、
プロトタイプ オブジェクトはスーパー クラスの効果をシミュレートします。少しわかりにくいように思えます。例を直接見てみましょう。
function Person(name,sex) { / /パーソン クラス コンストラクター
this.name = name;
Person.prototype.age = 12; class 属性の割り当て、
// PERSON の親クラスに属性を追加するのと同じ class
person.prototype.print = function() { // PERSON の親クラスにメソッドを追加します class
alert(this .name " _" this.sex "_" this.age);
} var
= new Person("name1","male"); // p1 の age 属性は親クラスを継承します。子の Person クラス (つまりプロトタイプ オブジェクト)
var p2 = new Person("name2","male");
p1.print() //name1_male_12
p2.print(); //name2_male_12
p1 .age = 34; //p1 インスタンスの年齢属性を変更します
p1.print() //name1_male_34
p2.print(); //name2_male_12
.prototype.age = 22; // Person クラスのスーパークラスの age 属性を変更します
p1.print(); //name1_male_34 (プロトタイプ属性の変更によって p1 の age 属性は変更されません)
p2.print(); //name2_male_22( p2 の年齢属性が変更されました)
p1.print = function() { //p1 オブジェクトの print メソッドを変更します
alert("i am p1");
}
p1.print( ); //私は p1 (p1 のメソッドは変更されています)
p2.print(); //name2_male_22 (p2 のメソッドは変更されていません)
Person.prototype.print = function() { // Person スーパークラスの print メソッドを変更します
alert("new print method!")
}
p1.print(); p1 (p1 の print メソッドは依然として独自のメソッドです)
p2.print(); //新しい print メソッド! (スーパークラスのメソッドの変更に伴い、p2 の print メソッドも変更されます)
JavaScript のオブジェクトのプロトタイプ属性は Java の静的変数に相当し、このクラス内のすべてのオブジェクト
で共有できるという記事を読みました。上記の例は、実際の属性を示しているようです。
JS では、new 演算子を使用してクラスのインスタンス オブジェクトを作成すると、そのメソッドと属性はクラスのプロトタイプ属性、およびプロトタイプ属性で定義されたメソッドと属性を継承します。クラス
の は、確かにこれらのインスタンス オブジェクトによって直接参照できますが、これらのインスタンス オブジェクトの属性とメソッドを再割り当てまたは定義すると、
インスタンス オブジェクトの属性またはメソッドは、このとき、クラスのprototype属性内の該当メソッドや
属性を変更しても、インスタンスオブジェクトには反映されません。例:
最初に、 new を使用します。オペレーターは 2 つのオブジェクト p1 と p2 を生成しました。それらの age 属性と print メソッドはすべて、Person クラスのプロトタイプ属性から取得されます (継承されます)。次に、
の age 属性を変更しました。 p1 にアクセスし、次に Person クラスのプロトタイプに直面します。属性の年齢が再割り当てされます (person.prototype.age = 22)。それに応じて p1 の age 属性は
変更されませんが、p2 の age 属性はそれに応じて変更されます。 、p2 の age 属性は引き続き Person クラスのプロトタイプ属性から引用されているため、同じ状況が後の
print メソッドにも反映されます。
上記の導入を通じて、プロトタイプ属性が をシミュレートしていることがわかります。 JavaScript における親クラス (スーパークラス) の役割。js のオブジェクト指向の考え方を反映して、
クラス変数/クラスメソッド/インスタンス変数/インスタンスメソッド は非常に重要です。 > まず、以前に作成したメソッドを追加します。
JavaScript では、すべてのメソッドに call メソッドと apply メソッドがあり、その最初のパラメータはオブジェクトであり、次のパラメータがオブジェクトです。
パラメータは、オブジェクトがこのメソッドを呼び出すタイミングを示します。ECMAScript の引数は、すべての関数に対して定義されている 2 つのメソッド、call()
および apply() を指定します。これらのメソッドを使用すると、関数を呼び出すことができます。 call() と apply() の両方の最初の
引数は、関数が呼び出されるオブジェクトです。この引数は
の本体内の this キーワードの値になります。 call() への残りの引数は、呼び出される関数に渡される値
です)。たとえば、メソッド f() を定義し、次のステートメントを呼び出します:
f.call (o, 1, 2);
o.m = f;
o.m(1,2); の場合と同等です。例:
function Person(name,age) { //定義メソッド
this.name = name;
this.age = age;
alert(o.name "_" o.age ); //unknown_unknown
person.call(o,"sdcyst",18); //呼び出しと同等: o.person("sdcyst",18) )
alert(o.name "_" o. age); //sdcyst_18
person.apply(o,["name",89]);//apply メソッドは call と同じように機能します。違いは、パラメータを渡す形式が配列を使用して
alert( o.name "_" o.age); //name_89
インスタンス変数とインスタンスを渡すことです。メソッドは、インスタンス オブジェクトに「.」演算子を追加し、その後にアクセスする属性名またはメソッド名を追加することによって取得されますが、クラスのメソッドまたは変数を設定することもできます。
このようにして、直接使用することもできます。クラス名と「.」演算子を入力し、属性名またはメソッド名にアクセスすると、クラス属性とクラス メソッドを定義するのが非常に簡単になります。クラス変数、作成された Person インスタンスの数を定義します。が作成されると、クラス変数のカウンターが 1 増加します
};
if(p1.age > p2 .age) {
return p1;
} else {
}
}
var p1 = new person("p1",18); p2 = new Person("p2",22);
alert("Now there is" Person.counter "Person"); //現在 2 人の Person
var p = Person.whoIsOlder(p1,p2) );
alert(p.name "older"); //p2 は古いです
プロトタイプ属性の適用:
次の例は、元の本から改変されたものです。
radius 属性と area メソッドを使用して Circle クラスを定義するとします。実装は次のようになります。
function Circle (radius) {
this.radius = radius;
this. area = function() {
return 3.14 * this.radius * this.radius;
}
}
Circle クラスの 100 個のインスタンス オブジェクトを定義すると、実際には、radius を除いて、各インスタンス オブジェクトに radius 属性と area メソッドがあります。この場合、Circle クラスの各インスタンス オブジェクトの area メソッドは同じです。この場合、
スペースを節約します。
関数 Circle(radius) {
this.radius = radius;
Circle.prototype.area = function() {
return 3.14 * this.radius * this.radius
}
var c = new Circle(1);
alert(c.area()); //3.14
次に、プロトタイプ属性を使用してみましょう。クラスの継承をシミュレートします。まず、Circle クラスを親クラスとして定義し、次にサブクラス
PositionCircle.
this を定義します。 . radius = radius;
}
Circle.prototype.area = function() { //面積を計算するための親クラスメソッド area を定義します
return this.radius * this.radius * 3.14; >}
function PositionCircle(x,y,radius) { //クラス PositionCircle を定義します
this.x = x; //属性の横軸
this.y = y; //属性の縦軸
円. call(this,radius); //親クラスのメソッドを呼び出すことは、this.Circle(radius) を呼び出して、PositionCircle クラスの
//radius 属性を設定することと同じです
}
PositionCircle。 prototype = new Circle( ); //PositionCircle の親クラスを Circle クラスに設定します。
var pc = new PositionCircle(1,2,1); //3.14
//PositionCircle クラス area メソッドは Circle クラスから継承し、Circle クラスの
//area メソッドはそのプロトタイプ属性に対応するプロトタイプ オブジェクトから継承します。
alert(pc.radius) / /1 PositionCircle クラスの radius 属性は、Circle クラス
/*
注: 前に、Circle オブジェクト
を指すように PositionCircle クラスのプロトタイプ属性を設定したため、pc のプロトタイプ属性は継承されます。 Circle オブジェクトのプロトタイプ属性、および Circle オブジェクトのコンストラクター属性
プロパティ (つまり、Circle オブジェクトに対応するプロトタイプ オブジェクトのコンストラクター属性) は Circle を指すため、ここでポップアップする
はis Circle.
*/
alert(pc.constructor); //Circle
/*このため、クラスの継承関係を設計した後、クラスのコンストラクター属性も設定する必要があります。それ以外の場合は、親クラスのコンストラクター属性を指します。
*/
PositionCircle.prototype .constructor = PositionCircle
alert(pc.constructor); //PositionCircle
スコープ、クロージャ、シミュレートされたプライベート プロパティ
まず簡単にしましょう 変数のスコープについて話しましょう。これらのことは誰もがよく知っているので、ここでは紹介しません。詳細。
var sco = "global"; //グローバル変数
function t() { var sco = "local"; //関数内のローカル変数
}
t(); //ローカル
alert(sco); //グローバルは関数内でローカル変数を使用できません
JavaScript にはブロックレベルのスコープがないことに注意してください。つまり、Java または C/C では、ブロックを "{}" で
囲んで、ブロック内の "{}" ブロックの外側でローカル変数を定義できます。
function f( props) {
for(var i=0; ialert(i) //10 i は for ループの制御文で定義されていますが、関数
if(props == "local") {
alert(sco); >alert( sco); //同様に、関数は if ステートメントで定義された変数を参照できます
}
f("local") //10 local local
関数内 ローカル変数を内部で定義する場合は特に注意してください。
var sco = "global"
function print1() {
alert(sco); >}
function print2 () {
alert(sco); //local
}
alert(sco); ; //未定義
var sco = "ローカル";
print1(); //グローバル
print2(); print3(); //未定義のローカル
最初の 2 つの関数は理解しやすいですが、重要なのは 3 番目の関数です。最初のアラート ステートメントはグローバル変数 "global" を表示しません。 、しかし
は未定義です。これは、print3 関数で sco ローカル変数 (どこにあるかに関係なく) を定義したためです。その場合、グローバル
sco 属性は関数内で機能しないため、最初のアラートは、実際には次の変数と同等です。
function print3() {
alert(sco);この例から、関数内でローカル変数を定義する場合、エラーを避けるために必要な変数を最初に定義するのが最善であると結論付けることができます。 。
関数のスコープは、関数の定義時に決定されます。例:
varscope = "global" //グローバル変数を定義します。
function print() {
}
function change() {
varscope = "local"; //print 関数はスコープ内で呼び出されますが、 change function,
//しかし、print 関数が実行されると、定義されたときのスコープに従って動作します
change() //golbal
クロージャ
クロージャは、変数、コード、スコープを含む式です。JavaScript では、関数は変数、コード、関数スコープの組み合わせであるため、すべての
関数はすべての JavaScript 関数の組み合わせです。
このコードとスコープの組み合わせは、コンピュータサイエンスの文献ではクロージャとして知られています。
すべての JavaScript 関数は、非常に単純なようです。例を見てみましょう。
この整数を毎回 1 ずつ増やすメソッドを書きたいのですが、何も考えずに次のように記述します。 getNext () {
i;
alert(getNext()); //2
alert( getNext ()); //3
常に getNext 関数を使用して次の整数を取得し、その後、誤ってまたは意図的にグローバル変数 i の値を 0 に設定してから getNext を呼び出します。もう一度、
また 1 から始まることがわかります... このとき、 i をプライベート変数として設定して、
のみを変更できるようにすればどんなに素晴らしいだろうと考えるでしょう。メソッド内でこれを関数の外で変更する方法はありません。次のコードはこの要件に基づいています。これについては後で詳しく説明します。
説明の便宜上、次のコードをdemo1とします。
function temp() {
var i = 0;
return i; 🎜>}
return b;
}
alert(getNext()); //1
alert(getNext()); 🎜>alert(getNext()); //3
alert(getNext()); //4
なぜなら、私たちが普段話している JavaScript のほとんどは、クライアント (ブラウザ) の下で行われるため、これも例外ではありません。
JavaScript インタープリターは、最初に「window」によって参照されるオブジェクトであるグローバル オブジェクトを作成します。
次に、定義したすべてのグローバル プロパティとメソッドがこのオブジェクトになります。関数と変数は異なるスコープを持っているため、JavaScript インタープリターが開始されると、
このスコープ チェーンには 1 つのオブジェクトのみが含まれます:
demo1 では、temp 関数。はグローバル関数であるため、temp() 関数のスコープ (scopr) に対応するスコープ チェーンは、js インタープリターの起動時のスコープ チェーンとなり、ウィンドウ オブジェクトは 1 つだけになります。
temp を実行すると、まず呼び出しオブジェクト (アクティブ オブジェクト) が作成され、この呼び出しオブジェクトが temp 関数に対応するスコープ チェーンの先頭に追加されます。これが temp() 関数に対応するスコープです。 > チェーンには、temp 関数に対応する window オブジェクトと call オブジェクト (アクティブ オブジェクト) の 2 つのオブジェクトが含まれています。すると、temp 関数で変数 i を定義し、
で関数 b() を定義したので、これらは次のようになります。オブジェクトのプロパティを呼び出します。もちろん、この前に、最初に argument 属性が呼び出しオブジェクトに追加され、temp() 関数の実行時に
同様に、関数 b() を実行するとスコープ チェーン全体を取得できます。
b() のスコープチェーンでは、b() 関数に対応する呼び出しオブジェクトには引数属性が 1 つだけあり、i 属性がないことに注意してください。これは定義にあるためです。 b() の場合は使用されません。var キーワードを使用して i 属性を宣言します。関数の実行時に、var キーワードで宣言された属性のみが
に追加されます。対応する呼び出しオブジェクトが必要な属性を持っているかどうか。 そうでない場合は、見つからない場合は未定義です。このようにして、実行を見てみましょう。デモ1の。 getNext を使用して temp 関数を参照し、temp 関数は関数 b を返したので、getNext 関数は実際には b 関数への参照になります。
getNext を 1 回実行すると、b() 関数が 1 回実行されます。関数 b() のスコープは関数 temp に依存するため、temp 関数は常にメモリ内に存在します。関数 b が実行されると、最初に
i が検索されますが、これは b に対応する呼び出しオブジェクト内に見つからないため、レベルを上げて temp 関数に対応する呼び出しオブジェクト内で見つけられるため、その値は次のようになります。 1 増加すると、この値が返されます。 このように、getNext 関数が有効である限り、b() 関数は常に有効になります。同時に、b() 関数が依存する temp 関数も消滅しません。 i、そしてこの変数は temp 関数内にあります
関数 人物(名前, 年齢) {
this.getName = function() { 名前を返す };
this.setName = function(newName) { name = newName }; getAge = function() { return age;
this.setAge = function(newAge) { age = newAge };
var p1 = 新しい人);
alert(p1.getName()); //sdcyst
alert(p1.name); // Person('class') には名前属性がないため
p1.name = "mypara" //p1 に name 属性を表示します
alert(p1.getName()); //sdcyst ですが、getName メソッドの戻り値は変更されません
alert(p1.name); mypara は p1 を表示します。オブジェクトの name 属性
p1.setName("sss") //プライベートの「name」属性を変更します
alert(p1.getName()); //sss
alert( p1.name); //mypara
に対して Person クラスがまだ定義されており、2 つのプライベート属性 name と age がそれぞれ対応する get/set メソッドを定義しています。
初期設計中にシミュレートした「名前/年齢」プライベート属性を変更しません。
クロージャを説明するのは確かに簡単な作業ではありません。インターネット上の多くの人も例を使ってクロージャを説明しています。何か間違っている場合は、修正してください。