ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScriptのプロトタイププロトタイプを徹底学習_基礎知識

JavaScriptのプロトタイププロトタイプを徹底学習_基礎知識

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBオリジナル
2016-05-16 15:45:231048ブラウズ

JavaScript はプロトタイプベースのプログラミング言語であり、通常のクラスベースのプログラミングとは大きく異なります。以下に重要な点を列挙します。

1. 関数はファーストクラスのオブジェクトです。これは、関数とオブジェクトが同じ言語ステータスを持つことを意味します

2. クラスはなく、オブジェクトのみがあります
3. 関数もオブジェクトの一種であり、いわゆる関数オブジェクト
4. オブジェクトは参照によって渡されます
では、このプロトタイプベースのプログラミング言語は、プロトタイプの起源である継承 (OO の基本要素) をどのように実装しているのでしょうか。

以下のコード スニペットを見てください:

function foo(a, b, c)
{
return a*b*c;
}
alert(foo.length);
alert(typeof foo.constructor);
alert(typeof foo.call);
alert(typeof foo.apply);
alert(typeof foo.prototype);
上記のコードをブラウザで実行すると、次のようになります。

1.length: 関数のパラメータの数を提供します

2.プロトタイプ: オブジェクトです
3. 他の 3 つは関数です
関数の宣言には、上記の 5 つのプロパティ (メソッドまたは属性) が含まれます。

プロトタイプを見てみましょう。

// prototype
function Person(name, gender)
{
this.name = name;
this.gender = gender;
this.whoAreYou = function(){//这个也是所谓的closure, 内部函数可以访问外部函数的变量
var res = "I'm " + this.name + " and I'm a " + this.gender +".";
return res;
};
}
// 那么在由Person创建的对象便具有了下面的几个属性
Person.prototype.age = 24;
Person.prototype.getAge = function(){
return this.age;
};
flag = true;
if (flag)
{
var fun = new Person("Tower", "male");
alert(fun.name);
alert(fun.gender);
alert(fun.whoAreYou());
alert(fun.getAge());
}
Person.prototype.salary = 10000;
Person.prototype.getSalary = function(){
return this.name + " can earn about " + this.salary + "RMB each month." ;
};
// 下面就是最神奇的地方, 我们改变了Person的prototype,而这个改变是在创建fun之后
// 而这个改变使得fun也具有了相同的属性和方法
// 继承的意味即此
if (flag)
{
alert(fun.getSalary());
alert(fun.constructor.prototype.age);//而这个相当于你直接调用 Person.prototype.age
alert(Person.prototype.age);
}
上記の例から、プロトタイプのメソッドまたは属性を動的に追加でき、プロトタイプによって作成されたオブジェクトは、関連するメソッドと属性を自動的に継承することがわかります。

さらに、各オブジェクトにはコンストラクター属性があり、それを作成した関数オブジェクトを指すために使用されます。たとえば、上の例の fun.constructor は Person を指します。

そこで、当然のことながら、関数オブジェクトによって宣言されたメソッドと属性と、プロトタイプによって宣言されたオブジェクトの違いは何なのかという疑問が生じます。

いくつかの違いがあります:

1. 自分で宣言したメソッドと属性は静的です。つまり、宣言後に新しいメソッドを追加したり、既存のメソッドを変更したりしても、それらによって作成されたオブジェクトには影響しません。つまり、継承は失敗しました

2. プロトタイプは、新しいメソッドを動的に追加したり、既存のメソッドを変更したりできるため、親関数オブジェクトが関連するプロトタイプ属性を宣言すると、それによって作成されたオブジェクトはこれらのプロトタイプ属性を自動的に継承します。
上記の例を続けます:

flag = true;
// 函数内部声明的方法是静态的,无法传递的
Person.school = "ISCAS";
Person.whoAreYou = function(){
return "zhutao";
};//动态更改声明期的方法,并不会影响由其创建的对象的方法, 即所谓的 静态
if (flag)
{
alert(Person.school);
alert(fun.school);//输出的是 "undefined"
alert(Person.whoAreYou()); //输出 zhutao
alert(fun.whoAreYou()); // I'm Tower and I'm a male.
}
Person.prototype.getSalary = function(){
return "I can earn 1000000 USD";
};
if (flag)
{
alert(fun.getSalary());//已经继承了改变, 即所谓的 动态
}
関数オブジェクト自体の属性とプロトタイプの属性があるのですが、それによって作成されたオブジェクトはどのようにして対応する属性を検索するのでしょうか?

基本的には以下の手順と手順に従います。

1. まず関数オブジェクト自体のプロパティを検索し、見つかった場合はすぐに実行します

2. 1 が見つからない場合は、プロトタイプ属性を検索します。見つかった場合は、親オブジェクトのプロトタイプを検索し続けます。が見つかるか、プロトタイプチェーンの最後に到達します (最後は Object オブジェクトになります)
上記は、関数オブジェクト自体のプロパティがプロトタイプのプロパティと同じ (名前が重複している) 場合の解決策にもなります。関数自体のオブジェクトが優先されます。

プロトタイプの典型的な例

jQuery または Prototype ライブラリを使用したことのある友人は、これらのライブラリに通常、trim メソッドがあることを知っているかもしれません。

例:

String.prototype.trim = function() {
 return this.replace(/^\s+|\s+$/g, '');
};

使用量のトリム:

' foo bar '.trim(); // 'foo bar'

しかし、これにはもう 1 つの欠点があります。ブラウザの新しいバージョンの JavaScript エンジン自体が String オブジェクトに Trim メソッドを提供するため、定義した Trim が独自の Trim を上書きしてしまうからです。実際、trim メソッドを定義する前に、このメソッドを自分で追加する必要があるかどうかを確認する簡単なテストを行うことができます。

if(!String.prototype.trim) {
 String.prototype.trim = function() {
 return this.replace(/^\s+|\s+$/g, '');
 };
}

プロトタイプチェーン

JavaScript でオブジェクトが定義またはインスタンス化されると、__proto__ という非表示属性が追加され、プロトタイプ チェーンはこの属性に依存して形成されます。ただし、一部のブラウザは __proto__ 属性への直接アクセスをサポートしていないため、__proto__ 属性には決して直接アクセスしないでください。さらに、__proto__ とオブジェクトのプロトタイプ プロパティは同じものではありません。それぞれに独自の目的があります。

どのように理解すればよいでしょうか?実際、myObject 関数を作成すると、実際には Function 型のオブジェクトが作成されます:

console.log(typeof myObject) // 関数

;

ここで、Function は JavaScript の事前定義されたオブジェクトであるため、独自の事前定義された属性 (長さや引数など) とメソッド (call や apply など) も持っていることに注意してください。もちろん、関数には __proto__ もあります。プロトタイプチェーンを実装します。つまり、JavaScript エンジン内に次のようなコード スニペットが存在する可能性があります:

Function.prototype = {
 arguments: null,
 length: 0,
 call: function() {
 // secret code
 },
 apply: function(){
 // secret code
 },
 ...
};

実際、JavaScript エンジンのコードはそれほど単純ではありません。ここでは、プロトタイプ チェーンがどのように機能するかを説明します。

関数 myObject を定義します。この関数にもパラメータ名がありますが、長さや他のメソッド (call など) などの他のプロパティは与えられません。では、なぜ次のコードは正常に実行されるのでしょうか?

console.log(myObject.length); // 结果:1,是参数的个数

这是因为我们定义 myObject 时,同时也给它定义了一个 __proto__ 属性,并赋值为 Function.prototype(参考前面的代码片段),所以我们能够像访问其它属性一样访问 myObject.length,即使我们并没有定义这个属性,因为它会顺着 __proto__ 原型链往上去找 length,最终在 Function 里面找到了。

那为什么找到的 length 属性的值是 1,而不是 0 呢,是什么时候给它赋值的呢?由于 myObject 是 Function 的一个实例:

console.log(myObject instanceof Function); // true
console.log(myObject === Function); // false

当实例化一个对象的时候,对象的 __proto__ 属性会被赋值为其构造者的原型对象,在本示例中就是 Function,此时构造器回去计算参数的个数,改变 length 的值。

console.log(myObject.__proto__ === Function.prototype); // true

而当我们用 new 关键字创建一个新的实例时,新对象的 __proto__ 将会被赋值为 myObject.prototype,因为现在的构造函数为 myObject,而非 Function。

var myInstance = new myObject('foo');
console.log(myInstance.__proto__ === myObject.prototype); // true

新对象除了能访问 Function.prototype 中继承下来的 call 和 apply 外,还能访问从 myObject 中继承下来的 getName 方法:

console.log(myInstance.getName()); // foo
 
var mySecondInstance = new myObject('bar');
 
console.log(mySecondInstance.getName()); // bar
console.log(myInstance.getName()); // foo

其实这相当于把原型对象当做一个蓝本,然后可以根据这个蓝本创建 N 个新的对象。

再看一个多重prototype链的例子:

// 多重prototype链的例子
function Employee(name)
{
this.name = "";
this.dept = "general";
this.gender = "unknown";
}
function WorkerBee()
{
this.projects = [];
this.hasCar = false;
}
WorkerBee.prototype = new Employee; // 第一层prototype链
function Engineer()
{
this.dept = "engineer"; //覆盖了 "父对象"
this.language = "javascript";
}
Engineer.prototype = new WorkerBee; // 第二层prototype链
var jay = new Engineer("Jay");
if (flag)
{
alert(jay.dept); //engineer, 找到的是自己的属性
alert(jay.hasCar); // false, 搜索到的是自己上一层的属性
alert(jay.gender); // unknown, 搜索到的是自己上二层的属性
}

上面这个示例的对象关系如下:

201581395745486.jpg (222×362)

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