JavaScriptはthisのいくつかの面接問題と紹介について

7989 ワード

原文のリンク
他の言語と比較して、関数のthisキーワードはJavaScriptでの挙動が少し違っています.また,厳密モードと非厳密モードの間にもいくつかの違いがある.
圧倒的多数の場合、関数の呼び出し方式はthisの値を決定する.thisは実行中に値が割り当てられず、関数が呼び出される度にthisの値も異なるかもしれない.ES 5は、関数がどのように呼び出されるかを考慮することなく、関数のbind値を設定するthis方法を導入する.
まず面接問題をいくつか作ります.
第一コース:
var name = "caibaojian.com"; 
var person = {
  name: "kang",
  pro: {
    name: "Michael",
    getName: function() {
      return this.name;
    }
  }
};
console.log(person.pro.getName()); // Michael
var pepole = person.pro.getName;
console.log(pepole()); // caibaojian.com
第二コース:
var name = "caibaojian.com";
var person = {
  name: "kang",
  pro: {
    name: "Michael",
    getName: function() {
      console.log(this);
      return this.name;
    }
  }
};
console.log(person.pro.getName()); // Object { name: "...", getName: () }, Michael
var pepole = person.pro.getName;
console.log(pepole()); // Window, caibaojian.com
第三コース:
'use strict';
var name = "caibaojian.com"; 
var person = {
  name: "kang",
  pro: {
    name: "Michael",
    getName: function() {
      console.log(this);
      return this.name;
    }
  }
};
console.log(person.pro.getName()); // Object { name: "...", getName: () }, Michael
var pepole = person.pro.getName;
console.log(pepole()); // undefined
第四コース:
var name = "caibaojian.com",
    person = {
      name : "kang",
      getName : function(){
       return function(){
         return this.name;
       };
      }
    };

console.log(person.getName()()); // caibaojian.com
次に、thisの使い方について説明します.
一、グローバルコンテキスト
大域的な動作コンテキスト(任意の関数の外部)では、thisは、厳密なモードであるかどうかにかかわらず、大域的なオブジェクトを指す.ブラウザでは、グローバルオブジェクトはwindowオブジェクトである.
console.log(this.document === document); // true
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
二、関数の文脈
関数内部では、thisの値は、関数がどのように呼び出されるかによって決まる.
1、直接呼び出し
非厳密モードでは、thisの値は、関数実行時には設定されません.このときのthisの値は、デフォルトでグローバルオブジェクトに設定されます.
function foo(){
  return this;
}

foo() === window; // true
厳格なモードでは、thisは、彼が実行環境に入ったときの値を保持するので、以下のthisは、undefinedにデフォルトで設定されるだろう.
function foo(){
  "use strict"; //     
  return this;
}

foo() === undefined; // true
厳密なモードでは、thisが実行されていないコンテキスト環境定義の場合、undefinedはデフォルトである.
2、対象方法におけるthis関数がオブジェクト内の方法で呼び出されると、それらのthisは、関数を呼び出すオブジェクトである.
以下の例では、obj.f()が呼び出されると、関数内のthisobjオブジェクトにバインドされる.
var obj = {
  prop: 37,
  foo: function() {
    return this.prop;
  }
};

console.log(obj.foo()); // 37
なお、呼び出し関数がどこにあるか、またはどのように定義されているかは、thisの動作に全く影響しない.
前の例では、objを定義する際に、そのメンバfooに匿名関数を定義した.しかし、まず関数を定義してからobj.fooに付属することもできる.このようにしてthisの評価も上記と一致している.
var obj = { prop: 37 };

function independent() {
  return this.prop;
}

obj.foo = independent;

console.log(obj.foo()); // 37
これは、thisの値が、fooのメンバとして関数objによってのみ呼び出されることに関係していることを示している.
同様に、thisのバインディングは、最も近いメンバ参照によってのみ影響される.
以下の例では、方法gをオブジェクトobj.bの関数として呼び出す.今回の実行中、関数のthisobj.bに向けられます.実は、これは相手自身のメンバーとはあまり関係がなく、一番近い引用が一番重要です.
var obj = { prop: 37 };

function independent() {
  return this.prop;
}

obj.b = {
  g: independent,
  prop: 42
};

console.log(obj.b.g()); // 42
3、プロトタイプチェーンのthis同じ概念はプロトタイプ鎖で定義される方法でも一致した.この方法がオブジェクトのプロトタイプチェーン上に存在する場合、thisは、この方法を呼び出すオブジェクトを指し、この方法がこのオブジェクト上に存在するように表現される.
var obj = {
  f : function(){ 
    return this.a + this.b; 
  }
};
var p = Object.create(obj);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5
この例では、オブジェクトpは自身のf属性ではなく、f属性はそのプロトタイプから継承されている.しかし、これは最終的にobjにおいてf属性を発見するルックアッププロセスに関係がない.検索プロセスはまずp.fの参照から開始されるので、関数のthispを指す.つまり、fpの方法として呼び出されたので、thispを指す.これはJavaScriptの原型継承の中の興味深い特性である.
4、gettersetterthis同じ概念も適用される場合の関数は、getterまたはsetterとして呼び出される.getterまたはsetterの関数として、属性の設定または属性を得るオブジェクトにthisが紐付けられます.
function modulus(){
  return Math.sqrt(this.re * this.re + this.im * this.im);
}

var obj = {
  re: 1,
  im: -1,
  get phase(){
    return Math.atan2(this.im, this.re);
  }
};

Object.defineProperty(obj, 'modulus', {
  get: modulus,
  enumerable: true,
  configurable: true
});

console.log(obj.phase, obj.modulus); // -0.785 1.414
5、コンストラクタのthis関数が構築関数として使用されるとき(newキーワードを使用して)、そのthisは作成される新しいオブジェクトと結合される.
なお、コンストラクタが返したデフォルト値がthisによって参照されたオブジェクトである場合には、他のオブジェクトに戻るように手動で設定することができ、戻り値がオブジェクトでない場合にはthisに戻る.
function Fn(){
  this.a = 37;
}

var obj = new Fn();
console.log(obj.a); // 37

function Foo(){
  this.a = 37;
  return { a: 38 };
}

obj = new Foo();
console.log(obj.a); // 38
6、call及びapply関数の関数がthisキーを使用している場合、Functionオブジェクトのプロトタイプからすべての関数が引き継がれるcall()方法およびapply()方法から呼び出した場合、その値は指定されたオブジェクトに結び付けられ得る.
function add(c, d){
  return console.log(this.a + this.b + c + d);
}

var obj = {
  a: 1,
  b: 3
};

add.call(obj, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(obj, [10, 20]); // 1 + 3 + 10 + 20 = 34
callおよびapply関数を使用する場合、伝達されたthis値がオブジェクトでない場合、JavaScriptは内部ToObject動作を用いてオブジェクトに変換しようと試みる.したがって、伝達された値が元の値である7またはfooのような場合、関連するコンストラクタを使用してオブジェクトに変換されるので、元の値7new Number(7)を介してオブジェクトに変換され、文字列foonew String('foo')を使用してオブジェクトに変換される.
function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]
7、bind()方法
ECMAScript 5はFunction.prototype.bindを導入した.fn.bind(someObject)を呼び出して、fnと同じ関数と作用領域を持つ関数を作成しますが、この新しい関数では、thisは、bindの最初のパラメータに恒久的に結びつけられます.
function fn(){
  return this.a;
}

var g = fn.bind({ a: "azerty" });
console.log(g()); // "azerty"

var obj = {
  a: 37,
  foo: fn,
  go: g
};
console.log(obj.foo(), obj.go()); // 37, "azerty"
8、DOMイベント処理関数のthis関数がイベントハンドラ関数として使用されると、そのthisは、イベントをトリガする要素(一部のブラウザは、モニタを動的に追加する場合、この約束を守らない限り、addEventListenerを使用しない)を指す.
//              
var elements = document.getElementsByTagName('*');

for(var i = 0; i < elements.length; i++){
  //        ,      
  elements[i].addEventListener('click', function (e) {
    e.stopPropagation();
    console.log(this);
    console.log(e.currentTarget);
    console.log(e.target);
    //   3      
    this.style.backgroundColor = '#A5D9F3';
  });
}
9、インラインイベント処理関数のthisコードがインライン処理関数によって呼び出されると、そのthisは、モニターのあるDOM要素を指す.
上のalertbuttonを表示します.注意外層コードの中のthisだけがこのように設定されています.
この場合、内部関数が設定されていないthisは、global/windowオブジェクト(すなわち、非厳格モードで呼び出された関数が設定されていないときに指すデフォルトオブジェクト)を指す.
(終わり)