ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptの継承の詳細解説(2)_jsオブジェクト指向

JavaScriptの継承の詳細解説(2)_jsオブジェクト指向

WBOY
WBOYオリジナル
2016-05-16 18:50:04897ブラウズ

これ

これは現在のオブジェクトを表します。これがグローバル スコープで使用される場合は、現在のページ オブジェクト ウィンドウを参照します。これが関数で使用される場合、これが参照する内容は、この関数が呼び出されるオブジェクトに基づきます。実行時。 また、2 つのグローバル メソッド apply と call を使用して、関数内の this の特定のポインタを変更することもできます。

まず、これをグローバル スコープで使用する例を見てみましょう:

  <script type="text/javascript">
   console.log(this === window); // true
   console.log(window.alert === this.alert); // true
   console.log(this.parseInt("021", 10)); // 10
  </script>
  

関数内のこれは、次のように、関数の定義時ではなく実行時に決定されます。

  // 定义一个全局函数
  function foo() {
   console.log(this.fruit);
  }
  // 定义一个全局变量,等价于window.fruit = "apple";
  var fruit = "apple";
  // 此时函数foo中this指向window对象
  // 这种调用方式和window.foo();是完全等价的
  foo(); // "apple"

  // 自定义一个对象,并将此对象的属性foo指向全局函数foo
  var pack = {
   fruit: "orange",
   foo: foo
  };
  // 此时函数foo中this指向window.pack对象
  pack.foo(); // "orange"
  

グローバル関数 apply と call を使用すると、次のように関数内の this のポインターを変更できます。

注: 2 つの関数 apply と call は同じ機能を持ちます。唯一の違いは、 2 つの関数のパラメータ定義は異なります。

  // 定义一个全局函数
  function foo() {
   console.log(this.fruit);
  }
  
  // 定义一个全局变量
  var fruit = "apple";
  // 自定义一个对象
  var pack = {
   fruit: "orange"
  };
  
  // 等价于window.foo();
  foo.apply(window); // "apple"
  // 此时foo中的this === pack
  foo.apply(pack); // "orange"
  

JavaScript では関数もオブジェクトであるため、次の興味深い例がわかります:

  // 定义一个全局函数
  function foo() {
   if (this === window) {
    console.log("this is window.");
   }
  }
  
  // 函数foo也是对象,所以可以定义foo的属性boo为一个函数
  foo.boo = function() {
   if (this === foo) {
    console.log("this is foo.");
   } else if (this === window) {
    console.log("this is window.");
   }
  };
  // 等价于window.foo();
  foo(); // this is window.
  
  // 可以看到函数中this的指向调用函数的对象
  foo.boo(); // this is foo.
  
  // 使用apply改变函数中this的指向
  foo.boo.apply(window); // this is window.
  

プロトタイプ

第 1 章で、プロトタイプを使用してクラスと継承の実装をシミュレートしました。プロトタイプは本質的には JavaScript オブジェクトです。 そして、すべての関数にはデフォルトのプロトタイプ属性があります。

この関数がカスタム オブジェクトを作成するシナリオで使用される場合、この関数をコンストラクターと呼びます。 たとえば、以下は簡単なシナリオです:


例として、JavaScript のデータ型 (文字列、数値、配列、オブジェクト、日付) を考えてみましょう。 これらの型が JavaScript のコンストラクターとして実装されていると考える理由があります。

同時に、配列を操作する多くのメソッド (concat、join、push など) もプロトタイプ属性で定義する必要があります。
  // 构造函数
  function Person(name) {
   this.name = name;
  }
  // 定义Person的原型,原型中的属性可以被自定义对象引用
  Person.prototype = {
   getName: function() {
    return this.name;
   }
  }
  var zhang = new Person("ZhangSan");
  console.log(zhang.getName()); // "ZhangSan"
  
実際、JavaScript のすべての組み込みデータ型には読み取り専用のプロトタイプ属性があります (これは理解できます。これらの型のプロトタイプ属性が変更されると、事前定義されたメソッドが消滅するためです)。しかし、それに独自の拡張メソッドを追加することができます。 。
  // 定义数组的构造函数,作为JavaScript的一种预定义类型
  function Array() {
   // ...
  }
  
  // 初始化数组的实例
  var arr1 = new Array(1, 56, 34, 12);
  // 但是,我们更倾向于如下的语法定义:
  var arr2 = [1, 56, 34, 12];
  

注: ここには罠があります。配列のプロトタイプに拡張メソッドを追加した後、for-in を使用して配列をループすると、この拡張メソッドもループアウトされます。
  // 向JavaScript固有类型Array扩展一个获取最小值的方法
  Array.prototype.min = function() {
   var min = this[0];
   for (var i = 1; i < this.length; i++) {
    if (this[i] < min) {
     min = this[i];
    }
   }
   return min;
  };
  
  // 在任意Array的实例上调用min方法
  console.log([1, 56, 34, 12].min()); // 1
  
次のコードはこれを示しています (min メソッドが Array プロトタイプに拡張されていると仮定しています):
解決策も非常に簡単です:
  var arr = [1, 56, 34, 12];
  var total = 0;
  for (var i in arr) {
   total += parseInt(arr[i], 10);
  }
  console.log(total); // NaN
  
  
  var arr = [1, 56, 34, 12];
  var total = 0;
  for (var i in arr) {
   if (arr.hasOwnProperty(i)) {
    total += parseInt(arr[i], 10);
   }
  }
  console.log(total); // 103
  

コンストラクター

constructor は常に、現在のオブジェクトを作成したコンストラクターを指します。例:

  // 等价于 var foo = new Array(1, 56, 34, 12);
  var arr = [1, 56, 34, 12];
  console.log(arr.constructor === Array); // true
  // 等价于 var foo = new Function();
  var Foo = function() { };
  console.log(Foo.constructor === Function); // true
  // 由构造函数实例化一个obj对象
  var obj = new Foo();
  console.log(obj.constructor === Foo); // true
  
  // 将上面两段代码合起来,就得到下面的结论
  console.log(obj.constructor.constructor === Function); // true
  

しかし、コンストラクターがプロトタイプに遭遇すると、興味深いことが起こります。

各関数にはデフォルトの属性プロトタイプがあり、このプロトタイプのコンストラクターはデフォルトでこの関数を指すことがわかっています。次の例に示すように:


関数のプロトタイプを再定義したとき (注: 上記の例との違い、これは変更ではなくオーバーライドです)、コンストラクターの動作が少し奇妙でした。 、次の例に示すように:

なぜ?
  function Person(name) {
   this.name = name;
  };
  Person.prototype.getName = function() {
   return this.name;
  };
  var p = new Person("ZhangSan");
  
  console.log(p.constructor === Person); // true
  console.log(Person.prototype.constructor === Person); // true
  // 将上两行代码合并就得到如下结果
  console.log(p.constructor.prototype.constructor === Person); // true
  
Person.prototype を上書きすると、次のコード操作と同等であることがわかります:
  function Person(name) {
   this.name = name;
  };
  Person.prototype = {
   getName: function() {
    return this.name;
   }
  };
  var p = new Person("ZhangSan");
  console.log(p.constructor === Person); // false
  console.log(Person.prototype.constructor === Person); // false
  console.log(p.constructor.prototype.constructor === Person); // false
  
そして、コンストラクターは常に自分自身を作成するコンストラクターを指すため、この時点では Person.prototype.constructor === Object 、つまり:
この問題を解決するにはどうすればよいですか?メソッドも非常に簡単で、 Person.prototype.constructor をオーバーライドするだけです:
  Person.prototype = new Object({
   getName: function() {
    return this.name;
   }
  });
  
  function Person(name) {
   this.name = name;
  };
  Person.prototype = {
   getName: function() {
    return this.name;
   }
  };
  var p = new Person("ZhangSan");
  console.log(p.constructor === Object); // true
  console.log(Person.prototype.constructor === Object); // true
  console.log(p.constructor.prototype.constructor === Object); // true
  
  function Person(name) {
   this.name = name;
  };
  Person.prototype = new Object({
   getName: function() {
    return this.name;
   }
  });
  Person.prototype.constructor = Person;
  var p = new Person("ZhangSan");
  console.log(p.constructor === Person); // true
  console.log(Person.prototype.constructor === Person); // true
  console.log(p.constructor.prototype.constructor === Person); // true
  

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