ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript プログラムに継承機能を実装する方法 (グラフィック チュートリアル)

JavaScript プログラムに継承機能を実装する方法 (グラフィック チュートリアル)

亚连
亚连オリジナル
2018-05-21 09:33:35806ブラウズ

JavaScript はオブジェクト指向であると主張する言語であり、継承はオブジェクト指向の主要な機能です。興味のある学生はこちらをご覧ください。

概要 JavaScript のすべてのオブジェクトには独自の継承チェーンがあります。つまり、すべてのオブジェクトは、「プロトタイプ」オブジェクトと呼ばれる別のオブジェクトを継承します。 null を除いて、独自のプロトタイプ オブジェクトはありません。

プロトタイプ オブジェクトの重要性は、A オブジェクトが B オブジェクトのプロトタイプである場合、B オブジェクトは A オブジェクトのすべてのプロパティとメソッドを取得できることです。 Object.getPrototypof メソッドは、現在のオブジェクトのプロトタイプ オブジェクトを取得するために使用されます。

var p = Object.getPrototypeOf(obj);

上記のコードでは、オブジェクト p はオブジェクト obj のプロトタイプ オブジェクトです。

Object.create メソッドは、新しいオブジェクトを生成し、指定されたオブジェクトを継承するために使用されます。

var obj = Object.create(p);

上記のコードでは、新しく生成された obj オブジェクトのプロトタイプはオブジェクト p です。

非標準の __proto__ 属性 (前後 2 つのアンダースコア) は、オブジェクトのプロトタイプ オブジェクトをオーバーライドできます。ただし、この属性の使用はできる限り少なくし、代わりに Object.getPrototypeof() および Object.setPrototypeOf() を使用してプロトタイプ オブジェクトの読み取りおよび書き込みを行う必要があります。

var obj = {};
var p = {};

obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true

上記のコードは、__proto__ 属性を通じて p オブジェクトを obj オブジェクトのプロトタイプとして設定します。

これが実際的な例です。

var a = {x: 1};
var b = {__proto__: a};
b.x // 1

上記のコードでは、b オブジェクトは __proto__ 属性を通じてそのプロトタイプ オブジェクトを a オブジェクトに設定するため、b オブジェクトは a オブジェクトのすべての属性とメソッドを取得できます。 b オブジェクト自体には x 属性がありませんが、JavaScript エンジンは __proto__ 属性を通じてそのプロトタイプ オブジェクト a を見つけ、a の x 属性を読み取ります。

new コマンドは、コンストラクターを通じて新しいインスタンス オブジェクトを作成します。本質は、インスタンス オブジェクトのプロトタイプをコンストラクターのプロトタイプ属性にバインドし、インスタンス オブジェクトに対してコンストラクターを実行することです。

var o = new Foo();

// 等同于
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);

プロトタイプ オブジェクト自体の __proto__ 属性は他のオブジェクトを指すこともでき、それによってレベルごとに「プロトタイプ チェーン」を形成します。

var a = { x: 1 };
var b = { __proto__: a };
var c = { __proto__: b };

c.x // 1

1 レベル上のプロトタイプ チェーンで特定の属性を検索すると、パフォーマンスに影響することに注意してください。探しているプロパティが上位レベルのプロトタイプ オブジェクト内にあるほど、パフォーマンスへの影響が大きくなります。存在しないプロパティを探す場合は、プロトタイプ チェーン全体が走査されます。

このアクションは を指します これがどこで定義されているかに関係なく、使用される場合、プロトタイプ オブジェクトではなく、常に現在のオブジェクトを指します。

var o = {
 a: 2,
 m: function(b) {
  return this.a + 1;
 }
};

var p = Object.create(o);
p.a = 12;

p.m() // 13

上記のコードでは、p オブジェクトの m メソッドは、そのプロトタイプ オブジェクト o から取得されます。このとき、m メソッド内の this オブジェクトは o ではなく、p を指します。

コンストラクターの継承 このセクションでは、あるコンストラクターに別のコンストラクターを継承させる方法を紹介します。

Shape コンストラクターがあると仮定します。

function Shape() {
 this.x = 0;
 this.y = 0;
}

Shape.prototype.move = function (x, y) {
 this.x += x;
 this.y += y;
 console.info('Shape moved.');
};
Rectangle构造函数继承Shape。

function Rectangle() {
 Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
 this.base = Shape;
 this.base();
}

// 子类继承父类的方法
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

rect instanceof Rectangle // true
rect instanceof Shape // true

rect.move(1, 1) // 'Shape moved.'

上記のコードは、コンストラクターの継承が 2 つの部分に分かれていることを示しています。1 つはサブクラスが親クラスのコンストラクター メソッドを呼び出すことであり、もう 1 つはサブクラスのプロトタイプが親クラスのプロトタイプを指すことです。親クラス。

上記のコードでは、サブクラスは親クラスを全体として継承します。場合によっては、単一のメソッドの継承のみが必要な場合があります。この場合は、次の記述方法を使用できます。

ClassB.prototype.print = function() {
 ClassA.prototype.print.call(this);
 // some code
}

上記のコードでは、サブクラス B の print メソッドが最初に親クラス A の print メソッドを呼び出し、次に独自のコードをデプロイします。これは、親クラス A の print メソッドを継承することに相当します。

__proto__ 属性__proto__ 属性は、現在のオブジェクトのプロトタイプ オブジェクト、つまりコンストラクターのプロトタイプ属性を指します。

var obj = new Object();

obj.__proto__ === Object.prototype
// true
obj.__proto__ === obj.constructor.prototype
// true

上記のコードは、まず新しいオブジェクト obj を作成し、その __proto__ 属性はコンストラクター (Object または obj.constructor) のプロトタイプ属性を指します。したがって、両者を比較すると true が返されます。

したがって、インスタンスオブジェクトobjのプロトタイプオブジェクトを取得するには3つの方法があります。

  • obj.__proto__

  • obj.constructor.prototype

  • Object.getPrototypeOf(obj)

上記の 3 つのメソッドのうち、最初の 2 つはあまり信頼できません。最新の ES6 標準では、__proto__ 属性はブラウザーにのみデプロイする必要があり、他の環境にデプロイする必要はないと規定しています。プロトタイプ オブジェクトを手動で変更すると、Obj.constructor.prototype が無効になる場合があります。

var P = function () {};
var p = new P();

var C = function () {};
C.prototype = p;
var c = new C();

c.constructor.prototype === p // false

上記のコードでは、C コンストラクターのプロトタイプ オブジェクトが p に変更され、その結果、c.constructor.prototype が歪んでしまいました。したがって、プロトタイプ オブジェクトを変更する場合は、通常、同時にコンストラクター属性を設定する必要があります。

C.prototype = p;
C.prototype.constructor = C;

c.constructor.prototype === p // true

したがって、プロトタイプ オブジェクトを取得するには、3 番目の Object.getPrototypeOf メソッドを使用することをお勧めします。この方法は次のように使用されます。

var o = new Object();

Object.getPrototypeOf(o) === Object.prototype
// true

Object.getPrototypeOf メソッドを使用して、ブラウザーが __proto__ 属性をサポートしているかどうかを確認できます。古いブラウザーはこの属性をサポートしていません。

Object.getPrototypeOf({ __proto__: null }) === null

上記のコードは、オブジェクトの __proto__ 属性を null に設定し、次に Object.getPrototypeOf メソッドを使用してオブジェクトのプロトタイプを取得し、それが null に等しいかどうかを判断します。現在の環境が __proto__ 属性をサポートしている場合、この 2 つの比較結果は true になるはずです。

__proto__ 属性を使用すると、インスタンス オブジェクトのプロトタイプを簡単に設定できます。 machine、vehicle、car という 3 つのオブジェクトがあるとします。machine は vehicle のプロトタイプ、vehicle は car のプロトタイプです。これを設定するには 2 行のコードしか必要ありません。

vehicle.__proto__ = machine;
car.__proto__ = vehicle;

以下は、__proto__ 属性とconstructor.prototype 属性の 2 つのメソッドを通じて、プロトタイプ オブジェクトに定義された属性を読み取る例です。

Array.prototype.p = 'abc';
var a = new Array();

a.__proto__.p // abc
a.constructor.prototype.p // abc

明らかに、__proto__ の方がシンプルに見えます。

通过构造函数生成实例对象时,实例对象的__proto__属性自动指向构造函数的prototype对象。

var f = function (){};
var a = {};

f.prototype = a;
var o = new f();

o.__proto__ === a
// true

属性的继承属性分成两种。一种是对象自身的原生属性,另一种是继承自原型的继承属性。

对象的原生属性对象本身的所有属性,可以用Object.getOwnPropertyNames方法获得。

Object.getOwnPropertyNames(Date)
// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]

对象本身的属性之中,有的是可以枚举的(enumerable),有的是不可以枚举的。只获取那些可以枚举的属性,使用Object.keys方法。

Object.keys(Date) // []
hasOwnProperty()

hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。

Date.hasOwnProperty('length')
// true

Date.hasOwnProperty('toString')
// false

hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法。

对象的继承属性用Object.create方法创造的对象,会继承所有原型对象的属性。

var proto = { p1: 123 };
var o = Object.create(proto);

o.p1 // 123
o.hasOwnProperty("p1") // false

获取所有属性判断一个对象是否具有某个属性(不管是自身的还是继承的),使用in运算符。

"length" in Date // true
"toString" in Date // true

获得对象的所有可枚举属性(不管是自身的还是继承的),可以使用for-in循环。

var o1 = {p1: 123};

var o2 = Object.create(o1,{
 p2: { value: "abc", enumerable: true }
});

for (p in o2) {console.info(p);}
// p2
// p1

为了在for...in循环中获得对象自身的属性,可以采用hasOwnProperty方法判断一下。

for ( var name in object ) {
 if ( object.hasOwnProperty(name) ) {
  /* loop code */
 }
}

获得对象的所有属性(不管是自身的还是继承的,以及是否可枚举),可以使用下面的函数。

function inheritedPropertyNames(obj) {
 var props = {};
 while(obj) {
  Object.getOwnPropertyNames(obj).forEach(function(p) {
   props[p] = true;
  });
  obj = Object.getPrototypeOf(obj);
 }
 return Object.getOwnPropertyNames(props);
}

用法如下:

inheritedPropertyNames(Date)
// ["caller", "constructor", "toString", "UTC", "call", "parse", "prototype", "__defineSetter__", "__lookupSetter__", "length", "arguments", "bind", "__lookupGetter__", "isPrototypeOf", "toLocaleString", "propertyIsEnumerable", "valueOf", "apply", "__defineGetter__", "name", "now", "hasOwnProperty"]

对象的拷贝如果要拷贝一个对象,需要做到下面两件事情。

确保拷贝后的对象,与原对象具有同样的prototype原型对象。
确保拷贝后的对象,与原对象具有同样的属性。
下面就是根据上面两点,编写的对象拷贝的函数。

function copyObject(orig) {
 var copy = Object.create(Object.getPrototypeOf(orig));
 copyOwnPropertiesFrom(copy, orig);
 return copy;
}

function copyOwnPropertiesFrom(target, source) {
 Object
 .getOwnPropertyNames(source)
 .forEach(function(propKey) {
  var desc = Object.getOwnPropertyDescriptor(source, propKey);
  Object.defineProperty(target, propKey, desc);
 });
 return target;
}

多重继承JavaScript不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。

function M1(prop) {
 this.hello = prop;
}

function M2(prop) {
 this.world = prop;
}

function S(p1, p2) {
 this.base1 = M1;
 this.base1(p1);
 this.base2 = M2;
 this.base2(p2);
}
S.prototype = new M1();

var s = new S(111, 222);
s.hello // 111
s.world // 222

上面代码中,子类S同时继承了父类M1和M2。当然,从继承链来看,S只有一个父类M1,但是由于在S的实例上,同时执行M1和M2的构造函数,所以它同时继承了这两个类的方法。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

详细介绍在JS中Map和ForEach的区别

js装饰设计模式学习心得(详细解答)

详解解读JS数值Number类型(图文教程)

以上がJavaScript プログラムに継承機能を実装する方法 (グラフィック チュートリアル)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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