ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript オブジェクト指向の簡潔な入門_js オブジェクト指向
オブジェクト指向に関するいくつかのキーワード: カプセル化、継承、ポリモーフィズム、拡張 JavaScript オブジェクト指向
カプセル化: JavaScript でオブジェクトを作成するモードの中で、個人的にはクロージャによるものだと思います。これは本当の意味での カプセル化 と見なすことができます。まず、クロージャを簡単に紹介します。
<script type="text/javascript"> function myInfo(){ var name ="老鱼",age =27; var myInfo = "my name is" + name + "i am" + age +"years old"; function showInfo(){ alert(myInfo); } return showInfo; } var oldFish = myInfo(); oldFish(); </script>
見覚えがあると思いませんか。そうです、これは実際には単純なクロージャ アプリケーションです。簡単な説明: 上記の関数 myInfo で定義された変数は、その埋め込み関数 showInfo でアクセスできます (これは理解しやすいです)。しかし、この埋め込み関数の戻り参照を変数 oldFish に代入すると、この時点で関数はshowInfo は myInfo 関数の本体の外で呼び出されますが、関数本体で定義された変数にもアクセスできます。そうそう!
クロージャーの原則を要約しましょう。関数は呼び出されるスコープではなく、定義されたスコープで実行されます。 実際、インライン関数を返すことは、クロージャを作成する最も一般的な方法でもあります。
上記の説明が抽象的すぎると思われる場合は、上記の関数を一緒に再構成して、より明確な階層があるかどうかを確認してみましょう:
<script type="text/javascript"> var ioldFish = function(name,age){ var name = name,age = age; var myInfo = "my name is" + name + "i am" + age +"years old"; return{ showInfo:function(){ alert(myInfo); } } } ioldFish("老鱼",27).showInfo(); </script>
上記の例のコーディング スタイルは次のとおりです。 ext yui より一般的なものはパブリックとプライベートであり、一目瞭然です。クロージャを使用すると、外部から直接アクセスしたくないものを簡単に隠すことができます。関数内で定義された変数にアクセスしたい場合、外部からの直接アクセスはアクセスできません。見つからなくて、書くのが大変だったので、しばらくしてからやっと戻ってきました。カプセル化とは、他人に見られたくないものを隠すためのものではありませんか?はは…
上記の例を JQ 形式に変換すると、次の例のように書く必要があります。この種のカプセル化モードはオープン ドア モードに属し、そこで定義された変数にアクセスできます。外部的に (下記) たとえば、最初にオブジェクトをインスタンス化し、次に関数の外でオブジェクトの名前または年齢属性にアクセスすると、それらを読み取ることができます。) もちろん、このモードでは、いくつかの「隠しルール」を設定して、チーム開発メンバーは、どの変数がどのようなものであるかを理解しています プライベート使用の場合、通常、警告信号を示すためにプライベート変数とメソッドの前にアンダースコア「_」を追加します。こうして「カプセル化」が実現しました!
<script type="text/javascript"> var ioldFish = function(name,age){ return ioldFish.func.init(name,age); }; ioldFish.func = ioldFish.prototype ={ init:function(name,age){ this.name = name; this.age = age; return this; }, showInfo:function(){ var info = "my name is" + this.name +"i am " +this.age+"years old"; alert(info); } }; ioldFish.func.init.prototype = ioldFish.func; ioldFish(" 老 鱼",27).showInfo(); //var oldFish = new ioldFish("老鱼",27); //alert(oldFish.name); </script>
どのモードが優れているのかと疑問に思う人もいるかもしれません。これはなんと言いますか?どちらの方法にもメリットとデメリットがあるので、併用してください。つまり、外部オブジェクトから直接アクセスできないものは、クロージャを使用してカプセル化するのが原則です。 「必ず」という4つの言葉は非常に奥深く、継続して練習することでしか本当の意味を知ることができません!
継承: これについて言及するとき、ついでにもう 1 つ付け加えておきたいのですが、クロージャのカプセル化の欠点はサブクラスの導出に役立たないため、クロージャにはリスクがあり、カプセル化は危険です。注意が必要です!直観のために、以下の例のオブジェクトの作成方法では「ドアを開ける」モードを採用しています。
JavaScript における 継承 は、一般に「クラス継承」、「プロトタイプ継承」、「メタクラス」の 3 つの方法に分けられます。以下に、3 種類の継承方法の原理を簡単に紹介します。
A. クラスの継承: これは、主流のフレームワークで一般的に使用される継承メソッドです。次の例を参照してください。
<script type="text/javascript"> var Name = function(name){ this.name = name; }; Name.prototype.getName = function(){ alert(this.name); }; var Fish = function(name,age){ Name.call(this,name); this.age = age; }; Fish.prototype = new Name(); Fish.prototype.constructor = Fish; Fish.prototype.showInfo = function(){ alert(this.age); } var ioldFish = new Fish("老鱼",27); ioldFish.getName(); </script>
上記のサブクラス Fish は定義されていません。これは、サブクラス Fish がスーパークラス Name で定義された getName メソッドを継承しているためです。説明すると、ここでのサブクラス Fish のプロトタイプは、スーパークラスのインスタンスを指します。サブクラス Fish 内では getName メソッドが宣言されていませんが、プロトタイプ チェーンの原理に従って、プロトタイプが指す上位レベルのオブジェクトが宣言されます。そのようなメソッドがあるかどうかを検索します。メソッドが見つからない場合は、元のプロトタイプ オブジェクトが検索されます。これは実際には継承の原則です。ここでは特別な説明があり、Fish.prototype.constructor = Fish; です。デフォルトのサブクラスのプロトタイプはそれ自体を指す必要がありますが、プロトタイプは以前にスーパークラスのインスタンス オブジェクトを指していたため、ここで設定し直す必要があります。 。もちろん、関連するコードは、extend
B を偽装する関数を通じて編成できます。これは、メモリ パフォーマンスの点でクラス継承よりも優れています。
<script type="text/javascript"> function clone(object){ var F = function(){}; F.prototype = object; return new F(); }; var Name = { name:"who's name", showInfo:function(){ alert(this.name); } }; var Fish = clone(Name); //Fish.name = "老鱼"; Fish.showInfo(); </script>
明らかに、プロトタイプ継承の中核は clone 関数であり、これはプロトタイプ チェーンの原理でもありますが、違いはスーパー クラスを直接複製するため、サブクラスがすべての属性を継承することです。特に、このタイプの継承では、オブジェクト変数を作成し、対応するプロパティとメソッドを定義するだけで済みます。
C. いくつかの一般的で汎用性の高いメソッドを関数にカプセル化します。これらのメソッドは、次の種類の関数を通じて使用する必要があります。さまざまなクラスに必要なメソッドを選択的に渡すこともできます。
<script type="text/javascript"> function agument(receveClass,giveClass){ if(arguments[2]){ var len = arguments.length; for(i=2;i<len;i++){ receveClass.prototype[arguments[i]] = giveClass.prototype[arguments[i]]; } } else{ for(method in giveClass.prototype){ if(!receveClass.prototype[method]){ receveClass.prototype[method] = giveClass.prototype[method]; } } } }; var Name = function(){}; Name.prototype ={ sayLike:function(){ alert("i like oldfish"); }, sayLove:function(){ alert("i love oldfish"); } } var Fish = function(){}; var ioldFish = new Fish(); agument(Fish,Name,"sayLove"); ioldFish.sayLove(); ioldFish.sayLike(); </script>
多态 :个人觉得这个比较抽象,很难言传,所以下面就从重载和覆盖两个方面来简单阐述一下。
重载 :上面这个例子中agument函数初始带了两个参数,但是在后面的调用中,agument(Fish,Name,”sayLove”)同样可以带入任意多个参数,javascript的重载,是在函数中由用户自己通过操作arguments这个属性来实现的。
覆盖 :这个很简单,就是子类中定义的方法如果与从超类中继承过来的的方法同名,就覆盖这个方法(这里并不是覆盖超类中的方法,注意一下),这里就不累赘了!
最后重点着墨说一下this和执行上下文 ,在前面举的封装例子中,this都是表示this所在的类的实例化对象本身,但是并不是千篇一律的,打个比方,通过HTML属性定义的事件处理代码,见如下代码:
<script type="text/javascript"> var Name = function(name) { this.name = name; this.getName = function () { alert(this.name); } }; var ioldFish = new Name("老鱼"), btn = document.getElementById('btn'); btn.onclick = ioldFish.getName; //btn.onclick = function(){ioldFish.getName.call(ioldFish)}; </script>
上例中点了按钮以后弹出框里并没有显示出实例对象的属性,这是因为this的执行上下文已经改变了,他现在所在的上下文应该是input这个 HTML标签,但是该标签又不存在getName这个属性,所以自然无法输出这个属性的属性值了!从这个例子我们不难看出:执行上下文是在执行时才确定的,它随时可以变。
当然你可以去掉上面我注释掉的那段代码,通过call改变this的执行上下文,从而获取getName方法。apply方法同样可以实现改变执行上下文的功能,不过在prototype框架中发现了一个更为优美的实现方法bind。看一下这个方法的实现吧,不得不感叹先人的伟大……
Function.prototype.bind = function(obj) { var method = this, temp = function() { return method.apply(obj, arguments); }; }
相信如果能看明白的话,您已经可以靠这些知识点,去写一个简单的脚本框架了,多多实践,相信不久的将来就能高手进级了