ホームページ ウェブフロントエンド jsチュートリアル JavaScript オブジェクト作成パターンの簡単な説明

JavaScript オブジェクト作成パターンの簡単な説明

Jan 23, 2017 pm 02:50 PM

はじめに

Web ベースのアプリケーションの人気の高まりと一部のプラグイン (Flash、Siverlight、Java アプレットなど) の廃止により、ますます多くの開発者が JavaScript で複雑なコードを作成しています。アプリケーション:

多くの開発者は、自分で継承を実装することなく、使い慣れた方法でプログラミングできるように、prototype.js などの継承機能を備えたサードパーティのフレームワークを使用することを望んでいます。私も。しかし、私はすぐに、それがどのように機能するかについてほとんど知らないことに罪悪感を感じました。

私はもともと JavaScript の継承パターンについての記事を書こうと思っていましたが、非常に単純なオブジェクトの作成にも、深く勉強する必要があることがすぐにわかりました。

JavaScript オブジェクトを作成してくださいと言われるかもしれませんが、これほど簡単なことはありません。次のようになります。

var myObject = {};
ログイン後にコピー

通常のオブジェクト

JavaScript は、いくつかの組み込み型のみを備えた非常に単純な言語です。このセクションでは、オブジェクトの種類、その作成方法、および継承するプロパティについて詳しく説明します。 JavaScript のオブジェクトは単なるプロパティのコレクションです。プロパティは、(一意の) 名前と任意のタイプの値で構成され、オブジェクトの作成後にそのプロパティを追加および削除できます。次のコードは、「空の」オブジェクトを作成し、いくつかのプロパティを追加します:

var shape = {};
shape.color = 'red';
shape.borderThickness = '2.0';
ログイン後にコピー

次のように、より簡単にオブジェクトの初期化を実装することもできます:

var shape = { 
    color : 'red',
    borderThickness : '2.0'
}
ログイン後にコピー

上記 2 つの形式で取得されたオブジェクトは同じです。

Object のコンストラクターを使用する 3 番目の方法もあります:

varshape = new Object();


これも上記の他の 2 つの方法と同等ですが、問題を見落としています。

私たちが作成したオブジェクトはどのように見えますか?そして、それはどのような性質を持っているのでしょうか? Google Chrome (または IE、Safari、Firefox など) の開発者ツールを使用して簡単に確認できます。通常、ウォッチを追加するか、コンソールにその名前を入力するだけでオブジェクトを検査できます。

ここにシェイプ オブジェクトがあります:

JavaScript オブジェクト作成パターンの簡単な説明 ご覧のとおり、明示的に追加された color と borderThickness という 2 つのプロパティがあります。しかし、この謎の __proto__ とは何でしょうか?

使用されているアンダースコアから、それが私有財産であることが推測できるかもしれません。ただし、ご存知のとおり、JavaScript にはパブリック プロパティしかなく、コード内でのそれらの使用を (たとえ危険であっても) 誰も妨げることはできません。

__proto__ この属性はオブジェクトのプロトタイプを指します。このオブジェクトを開くと、多くの機能があることがわかります。

JavaScript オブジェクト作成パターンの簡単な説明これは、JavaScript の実際の継承メカニズムがプロトタイプ チェーンを中心に展開していることを明らかにしています。オブジェクトにないプロパティ (プロパティが値であるか関数であるかに関係なく) にアクセスしようとすると、JavaScript ランタイムはオブジェクトのプロトタイプにそのプロパティがあるかどうかを確認します。存在する場合はこのプロパティを返し、存在しない場合はプロトタイプ チェーンをずっと検索します。

試してみることができます。コンソールで toString プロパティにアクセスしてみます (ドット メソッドまたはインデックス メソッドを使用できます)。このプロパティは関数であり、呼び出すことができます。

JavaScript オブジェクト作成パターンの簡単な説明

結果はあまり興味深いものではありません!

ご覧のとおり、JavaScript オブジェクトは非常に単純で、プロパティとプロパティの継承を可能にする「隠された」プロトタイプを含む単なる「バッグ」です。

JavaScript の固有の動的な性質により、オブジェクトの作成は非常に簡単になります。ただし、ほとんどの複雑なアプリケーションでは、型 (C# および Java 言語ではクラスとして定義) が使用されます。型はオブジェクトのプロパティとメソッドを記述し、その型の複数のインスタンスを作成できるようにします。

次のセクションでは、JavaScript で型を実装する方法を見ていきます。

用語について最後に 1 つ注意します。JavaScript には関数がありますが、オブジェクトのプロパティに対する関数はメソッドと呼ばれます。

コンストラクター

前のセクションでは、プロトタイプ チェーンと、オブジェクトがプロパティとメソッドを継承する方法について簡単に説明しました。オブジェクトが作成されると、そのプロトタイプを変更することはできません。プロパティとメソッドを追加するにはどうすればよいですか?

コンストラクターは上記の問題を解決します。さらに詳しく説明する前に、例をより実用的なものにするためにオブジェクトに関数を追加しましょう:

var shape = { 
    color : 'red',
    borderThickness : '2.0',
    describe : function() {       
       console.log("I am a " + this.color + " shape, with a border that is " +           
          this.borderThickness + " thick");
    }
}
ログイン後にコピー

这给对象shape加了一个函数(也就是方法)。如果你用控制台审查,你会看见describe这个函数是一个属性,和其他东西一样。你可以通过控制台调用这个方法:

JavaScript オブジェクト作成パターンの簡単な説明

构造函数执行完成会在创建的“shape”对象上添加两个属性,同对象初始化创建的“shape”对象相比,你会发现两者看起来似乎一样。然而有个微妙的区别,初始化创建的“shape”对象有个"隐藏”属性__proto__,Chrome指定为Object,而构造函数创建的“shape”对象的__proto__指向Shape。(注:随后你会明白,Chrome使用的这种指定方式!)

译者注:不知道原文作者的Chrome版本,我的机子上Chrome版本是 53.0.2785.113 m (64-bit)。通过构造器构造的对象的__proto__还是指向一个Object,即Shape函数的prototype属性,理论上也应该是Object。后文翻译还是以原作者的Chrome为准:构造函数实例的__proto__为Shape,初始化对象的__proto__为Object。

在讨论这两者微妙的区别的意图之前,我们来为shape对象添加一个describe方法。

你可以在刚才创建的对象的构造函数上直接添加方法。然而,这种构造模式(译者注:指构造函数创建对象的方式)提供了一种更为有力的替代方式。你可以在构造函数的原型属性上添加方法。如下所示:

function Shape(color, borderThickness) { 
    this.color = color;    
    this.borderThickness = borderThickness;
}
Shape.prototype.describe = function() {    
      console.log("I am a " + this.color + " shape, with a border that is " +        
      this.borderThickness + " thick");
};
ログイン後にコピー

若你创建一个shape对象并审查此对象,你会发现如下所示代码:

JavaScript オブジェクト作成パターンの簡単な説明

现在你的shape对象具有describe方法,但这并非对象本身的属性,而是该对象原型上的一个属性。通过上文,我们明白一个定义在Object原型上的方法是如何通过原型链被实例继承的。同样,所有的Shape实例会继承describe方法:

JavaScript オブジェクト作成パターンの簡単な説明

展开Shape的__proto__属性所指的对象,会显示此对象位于原型链的底部:

JavaScript オブジェクト作成パターンの簡単な説明

因此,通过Shape构造器创建的任意对象都可以访问上述显示的所有方法。

JavaScript中所谓的类,其实是一种设计模式:一个构造函数(consturctor)和一个用于在该类实例间共享属性和方法的原型对象(Objcet.prototype)的结合。

为了达到属性继承,代码复用等目的,通过函数来模拟类来创建对象。

(或许你会疑惑,通过Object就可以创建对象了呀?确实如此,new一个Object对象后,给Object对象增加属性和方法,确实可以生成一个对象。但这种做法实在太麻烦,且不易封装复用。下面介绍的方法一就是这种方法的高级版)

方法一:用一个普通的函数来封装(俗称工厂模式)

function createPerson(name, age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        alert(this.name);
    };    
    return o;
}
var p1 = createPerson("Jack", 32);
var p2 = createPerson("Zhang", 30);
p1.sayName();   //Jack
p2.sayName();   //Zhang
ログイン後にコピー

这个例子平淡无奇,createPerson函数内创建了一个Object,并赋予它两个属性(name,age)和一个方法(sayName)。这样部分达到了类的作用。为何说是部分呢?因为它有个明显的缺陷,即无法解决对象识别问题,创建出来的对象(p1,p2)都是Object类型的:

alert(p1 instanceof Object);  //true  
alert(p2 instanceof Object);  //true
ログイン後にコピー

如果你有一堆对象,有Person有Dog,你无法区分这些对象中哪些是Person哪些是Dog。为了解决上述缺陷,引入了构造函数的概念

方法二:通过构造函数来封装:(俗称构造函数模式)

function Person(name, age){  
    this.name = name;  
    this.age = age;  
    this.sayName = function(){  
        alert(this.name);  
    };      
}  
  
var p1 = new Person("Jack", 32);    //用new操作符来调用  
var p2 = new Person("Zhang", 30);  
p1.sayName();   //Jack  
p2.sayName();   //Zhang  
  
alert(p1 instanceof Object);  //true,显然创建的对象都既是Object,也是Person  
alert(p1 instanceof Person);  //true  
alert(p2 instanceof Object);  //true  
alert(p2 instanceof Person);  //true  
  
alert(p1.constructor == Person);  //true  
alert(p1.constructor == Dog);     //false,这样就能区分对象究竟是Person还是Dog了
ログイン後にコピー

Person函数与createPerson函数相比,有以下不同:

1.没有var o = new Object();创建对象,自然最后也没有return o;返回对象

2.没有将属性和方法赋给Object对象,而是赋给this对象

因为Person函数内部使用了this对象,因此你必须用new操作符来创建对象:

var p = new Person("Jack", 32);
p.sayName();//Jack
ログイン後にコピー

如果你忘记用new操作符来创建对象的话:(解决方式见※1)

var p = Person("Jack", 32);  
p;              //undefined  
this.name;          //Jack  
this.age;           //32
ログイン後にコピー

灾难的事情发生了,因为没有用new来调用,因此Person函数内的this将指向window对象(即BOM),因此等于给window对象添加了两个全局变量。

像Person这样的函数被称为【构造函数】。你可能疑惑,既然JavaScript并没有类的概念,那怎么会有构造函数呢?其实构造函数与普通函数唯一的区别,就在于它们的调用方式不同。只要通过new操作符来调用,那它就可以作为构造函数。但构造函数毕竟也是函数,并不存在任何特殊的语法。

但通过构造函数的方式来模拟类的话,也有个缺陷:(一种不好的解决方式见※2)

alert(p1.sayName == p2.sayName);  //false
ログイン後にコピー

sayName只是个普通的方法,如果你创建多少个Person对象,显然你不希望多个对象中都有一个sayName方法的副本。这将造成无谓的浪费。

方法三:通过原型来封装:(俗称原型模式,但其实这里的例子是复合模式,即构造函数模式+原型模式)

每个函数都有个prototype属性,该属性其实是一个指针,指向一个对象,称为原型对象。原型对象中包含着可供所有实例共享的属性和方法。

可以将希望所有对象共享的属性或方法(如上述sayName方法)添加到原型对象中,达到继承复用的目的。

function Person(name, age) {  
    this.name = name;  //name和age没有放到原型对象中,而是仍旧留在构造函数内部  
    this.age = age;    //表示不希望每个实例都共享这两个数  
}  
Person.prototype.sayName = function(){    //将希望被所有对象共享的sayName方法放入原型对象中  
    alert(this.name);  
}  
  
var p1 = new Person("Jack", 32);    //用new操作符来调用  
var p2 = new Person("Zhang", 30);  
p1.sayName();   //Jack  
p2.sayName();   //Zhang  
<pre name="code" class="javascript">alert(p1.sayName == p2.sayName);  //true,sayName方法确实被共享了,而不是每个对象中都有一个独立的副本
ログイン後にコピー

示意图:

JavaScript オブジェクト作成パターンの簡単な説明

Person构造函数(再次声明,无论什么函数内部都有原型指针,指向一个原型对象)内部有个原型指针,指向Person的原型对象。图中也可看出Person的原型对象内部有个constructor属性,指向对应的函数。这样函数和原型对象就双向绑定起来了。

已经展示了忘记用new操作符来调用构造函数的可怕后果。靠程序员自觉显然不是个好主意。有3种方式可以避免这种错误:

1.在函数内部第一行加上use strict;启用严格模式,这样var p = Person("Jack", 32);创建将失败。缺点是你必须保证环境支持ES5,否则无法保证严格模式能起作用。

2.改进构造函数:

function Person(name, age) {   
    if(!(this instanceof Person)){  
        return new Person(name, age);  
    }  
    this.name = name;  
    this.age= age;   
}
ログイン後にコピー

上述代码是自解释代码(self-explanatory),一行注释都不用就能看明白。缺点是需要额外的开销(即额外的函数调用),也无法适用于可变参数函数。

3.利用ES5的Object.create函数改进:

function Person(name, age) {  
        var self = this instanceof Person? this : Object.create(Person.prototype);  
        self.name = name;  
        self.age= age;  
        return self;  
}
ログイン後にコピー

Object.create需要一个原型对象作为参数,并返回一个继承自该原型对象的新对象。同样你必须保证环境支持ES5

已经展示了通过构造函数来模拟类的缺陷,即无法实现共享。你可能会疑惑,通过共通函数不就能实现共享了吗?

function Person(name, age) {  
        this.name = name;  
        this.age= age;  
        this.sayName = sayName;  
}  
  
function sayName() {  
        alert(this.name);  
}  
  
var p1 = new Person("Jack", 32);  
var p2 = new Person("Zhang", 30);  
  
alert(p1.sayName == p2.sayName);  //true
ログイン後にコピー

在构造函数中定义个函数指针,指向全局函数sayName,这样确实能达到共享的目的。但你真希望看到全局函数吗?如果Person有5,6个方法,那你就需要定义5,6个全局函数,同样是个灾难。

总结

在JavaScript中,对象创建并不是一个简单的主题!希望通读此文后,你会学到些关于构造函数,原型或JavaScript语言的其它内容。


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

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

独自のJavaScriptライブラリを作成および公開するにはどうすればよいですか? 独自のJavaScriptライブラリを作成および公開するにはどうすればよいですか? Mar 18, 2025 pm 03:12 PM

記事では、JavaScriptライブラリの作成、公開、および維持について説明し、計画、開発、テスト、ドキュメント、およびプロモーション戦略に焦点を当てています。

ブラウザでのパフォーマンスのためにJavaScriptコードを最適化するにはどうすればよいですか? ブラウザでのパフォーマンスのためにJavaScriptコードを最適化するにはどうすればよいですか? Mar 18, 2025 pm 03:14 PM

この記事では、ブラウザでJavaScriptのパフォーマンスを最適化するための戦略について説明し、実行時間の短縮、ページの負荷速度への影響を最小限に抑えることに焦点を当てています。

フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? Apr 04, 2025 pm 02:42 PM

フロントエンドのサーマルペーパーチケット印刷のためのよくある質問とソリューションフロントエンド開発におけるチケット印刷は、一般的な要件です。しかし、多くの開発者が実装しています...

誰がより多くのPythonまたはJavaScriptを支払われますか? 誰がより多くのPythonまたはJavaScriptを支払われますか? Apr 04, 2025 am 12:09 AM

スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

ブラウザ開発者ツールを使用してJavaScriptコードを効果的にデバッグするにはどうすればよいですか? ブラウザ開発者ツールを使用してJavaScriptコードを効果的にデバッグするにはどうすればよいですか? Mar 18, 2025 pm 03:16 PM

この記事では、ブラウザ開発者ツールを使用した効果的なJavaScriptデバッグについて説明し、ブレークポイントの設定、コンソールの使用、パフォーマンスの分析に焦点を当てています。

ソースマップを使用して、マイナイドJavaScriptコードをデバッグするにはどうすればよいですか? ソースマップを使用して、マイナイドJavaScriptコードをデバッグするにはどうすればよいですか? Mar 18, 2025 pm 03:17 PM

この記事では、ソースマップを使用して、元のコードにマッピングすることにより、Minified JavaScriptをデバッグする方法について説明します。ソースマップの有効化、ブレークポイントの設定、Chrome DevtoolsやWebpackなどのツールの使用について説明します。

JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? Apr 04, 2025 pm 05:09 PM

同じIDを持つ配列要素をJavaScriptの1つのオブジェクトにマージする方法は?データを処理するとき、私たちはしばしば同じIDを持つ必要性に遭遇します...

javascriptの分解:それが何をするのか、なぜそれが重要なのか javascriptの分解:それが何をするのか、なぜそれが重要なのか Apr 09, 2025 am 12:07 AM

JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

See all articles