プログラマーとして、C++ の this や Python の self など、オブジェクト指向言語で現在のオブジェクトを参照する参照 (またはポインター) に長い間慣れ親しんでいるかもしれません。これらには、もちろん OO 属性があります (JavaScript は実際にはさらに多くの属性を持っています) JavaScript のいわゆる関数型言語にも、現在のプロパティのオブジェクト (this キーワード) を参照するポインター (または参照) があります。
このキーワードを理解するために、一文を覚えておきたい場合は、このキーワードが常に現在の関数の所有者オブジェクト (実行空間) を指している必要があります。この文を理解する方法については、次のようになります。以下の詳細な説明を参照してください。
では、スコープとは何ですか?
wikipediaの説明は コンピュータープログラミングにおいて、スコープとは、値と式が関連付けられる囲みコンテキストです。 中国語はいわゆるスコープで、値や式に関連付けられたコンテキスト (参照できる実行空間) を指定します。
スコープはこれと何の関係がありますか? 上記の定義から、これは常にこの関数を現在参照しているオブジェクトを指します。現在参照されているオブジェクトを特定したい場合は、現在の関数のスコープを把握する必要があります。 詳細については、以下の分析を参照してください。
このキーワード
以下のいくつかの例をご覧ください。
Python の例:
class Person(object): """a person class """ def __init__(self, name): self.name = name #这里的self指向的是实例化后的对象,如下面中的Magic def get_name(self): return self.name Magic = Person("Magic") print Magic.name
JavaScript の例:
window.name = "Magic from window" var get_name = function(){ // this的具体指向只能在运行时才能确定,也就是确定运行时调用其的对象 return this.name; }; // 输出Magic from window, get_name调用的对象为window alert(get_name()); var obj = {} obj.name = "Magic from obj"; // 输出Magic from obj, 我们强制地使用了 apply来更改调用的对象,使其指向obj alert(get_name.apply(obj)); var innerobj = { "name" : "Magic from innerobj" }; innerobj.get_name = get_name; // 使得innerobj的get_name方法指向了global scope的get_name函数 alert(innerobj.get_name()); // 输出Magic from innerobj, 此时this指向的是innerobj
したがって、上記の単純な例から、これは実行時に特定のポインタを決定することしかできず、そのとき初めて呼び出し元のオブジェクトを知ることができます。そして、これは動的言語の重要な機能でもあります。
それでは、これが現在指している参照オブジェクトを決定するにはどうすればよいでしょうか?通常は次のように判断できます:
グローバル スコープで呼び出された場合 (グローバル スコープが何であるかを明確にするために以下の説明を参照してください)、例: get_name() のトップレベル オブジェクト ウィンドウを指します。
このリファレンスのようなものであれば、innerobj. get_name()、これが innerobj を指していることは明らかです
apply または call を使用して強制参照オブジェクトを指す場合、明らかに get_name などの強制オブジェクトも指すことになります。適用(オブジェクト)。
apply と call について
これら 2 つのキーワードは、この参照オブジェクト (実行スペース) の強制として簡単に理解できます。 2 つの構文は次のとおりです:
fun.call(object, arg1, arg2, .. )
fun.apply(object, [arg1, arg2, ...])
両方の目的は同じです (関数の実行スペースを動的に変更する、または this が指すオブジェクトを変更する)。関数のパラメータの呼び出し方法が異なります
サンプルコードは次のとおりです:
var test_call_apply = function(name, gender, school){ alert(this.age + name + gender + school); }; test_call_apply.call({age:24}, "Magic", "male", "ISCAS"); test_call_apply.apply({age:24}, ["Magic", "male", "ISCAS"]);
スコープの詳細
var global_scope = "I'm global"; var fun = function(){ var fun_scope = "I'm in fun scope"; return innerfun(){ var inner_func_scope = "I'm in the inner fun scope"; return global_scope + fun_scope + inner_func_scope; //此处的引用是重要的,请特别注意 }; }; alert(fun()());
上記のコードに注意してください:
global_scope グローバルスコープです
fun_scope 関数のスコープです
inner_func_scope は関数内にある関数のスコープです
引き続き関数を埋め込むこともでき、その後、複数のスコープが生成されます。
それでは、なぜ innerfun メソッドが独自のスコープ内にない変数を参照できるのかという疑問が生じます。
この質問に答える前に、コンセプト スコープ チェーンを導入する必要があります。 いわゆるスコープ チェーンとは、JavaScript コード内で形成された優先順位と関連するスコープのチェーンを指します。
上記のコードを例に挙げると、
グローバル スコープの場合、それ自体のグローバル スコープ チェーンが作成されます (もちろん、現時点では、このチェーンにはスコープが 1 つだけあります)。
fun 関数のスコープについては、最初に global と同じスコープ チェーンを確立し、次に次の構造のように独自のスコープを追加します (この時点で、このチェーンには 2 つのスコープがあります): global==> ; fun
innerfun の場合、fun 関数が所有するチェーンに加えて、次の構造と同様に独自のスコープも追加されます (もちろん、このチェーンには現時点で 3 つのスコープがあります)。 = =>innerfun
スコープチェーンには次の特徴があります:
順序付け
関数が作成されるたびにスコープが自動的に生成され、独自のスコープチェーンに追加されます
このチェーンは検索時のスタックに似ています。変数を検索するときは、常に先頭から始めます
実際、式を計算するときも、それ自体のスコープチェーンを上から下に検索します。チェーン全体を検索してもこの値が見つからない場合は、すぐに返されます。
この検索メカニズムは、通常チェーンのフロントエンドにあるスコープの優先順位が高いことも決定します。
たとえば、JavaScript が式 global_scope + fun_scope + inner_func_scope; を計算するとき、上の画像のスコープ チェーンを検索して最終結果を決定します。
一些说明
如果你弄清楚了上面的论述,应该说你对this关键字和scope已经具有完全的知识基础了,但是我们需要在实际中更好地使用和理解这些概念,这样才能把能力上升到别一个层次,这也即所谓的理论与实践的关系。
请看下面这个例子:
var change_color = function(){ this.style.color = "red"; }; window.onload = function(){ var text = document.getElementById("text"); text.onclick = change_color; //此时this指向的是text这个对象(dom对象) }; // 下面这行代码是在body中 //这点需要特别注意, inline script指向的是window,此处会无定义 <span id="another" onclick="change_color()">My color will be changed2.</span>
需要特别注意的是:
inline event registration中的this并非指向的是自己的dom节点,而是global scope的window,这点可以从上面的例子中得到证明
这种inline event registration是不可取的,推荐的是 Unobtrusive JavaScript (处理逻辑和页面结构相分离)
javascript 是一种非常强大的动态语言,它是披着C语言外衣的函数式语言,如果你只当作它是一种类C的命令式语言,那么你的知识层次还过于低,而倘若你能够理解到javascript 的函数式语言本质,你在运用 javascript,理解 jQuery 及其它的库,甚至自己写一些 javascript 都会游刃有余的。