JavaScriptにおけるthisバインディングの詳細解
9804 ワード
転載は出典を明記してください.https://segmentfault.com/a/11...
標準バインディング
javascriptでは、最も一般的な関数呼び出しのタイプは独立した関数呼び出しであるため、この規則は他の規則が適用されない場合のデフォルトルールと見なされます.関数を呼び出した場合、関数には修飾がなく、すなわち「裸」の呼び出しがあると、デフォルトのバインディング規則が適用され、デフォルトのバインディングは大域的な作用領域を指します.
これはデフォルトのバインディング規則であり、javascriptにおいて最も一般的な関数呼び出しモードであり、
デフォルトのバインディングの厳密なモード
javascriptでは、厳格なモードが使用されている場合、
陰式バインディング
関数が呼び出されたとき、関数にはいわゆる「ドロップポイント」があると、コンテキストオブジェクトがある場合には、関数の
陰でバインディングするとコンテキストが失われます.
明示的なバインディング
明示的なバインディングは、名前の通りにthisをコンテキストに結びつける3つの明示的なバインディングの方法を提供しています.
newバインディング
最後に述べる新たなオブジェクト を作成します.この新しいオブジェクトはプロトタイプ接続されます. この新しいオブジェクトは、関数呼び出しの に結び付けられます.関数が他のオブジェクトに戻っていない場合、 に戻ります.
4つのバインディングの優先度
newバインディング>明示的バインディング>陰的バインディング>標準バインディング
矢印関数のthisバインディング
矢印関数はES 6の重要な特性です.矢印関数の
結び目
以上が
前の4つのバインディング規則については、各規則の呼び出し条件を把握することによって、どの作用領域に結合されているのかをよく理解することができる.
全文が終わる
this
はJavascriptの中で最も味わい深い特性と言えます.高校英語の様々な時制のようです.例えば、受動的な時制、過去の進行時のように、何回見逃しても、次の間違いがあります.本論文は「あなたの知らないJavaScript上巻」に啓発し、javasriptのthis
に対してまとめを行う.this
を学習する第1のステップは、関数自体を指すのではなく、関数を指すのでもない作用領域を理解することである.this
は、実際には、関数が呼び出されたときに起こるバインディングであり、どこを指すかは、関数がどこで呼び出されるかによって完全に決まる.標準バインディング
javascriptでは、最も一般的な関数呼び出しのタイプは独立した関数呼び出しであるため、この規則は他の規則が適用されない場合のデフォルトルールと見なされます.関数を呼び出した場合、関数には修飾がなく、すなわち「裸」の呼び出しがあると、デフォルトのバインディング規則が適用され、デフォルトのバインディングは大域的な作用領域を指します.
function sayLocation() {
console.log(this.atWhere)
}
var atWhere = "I am in global"
sayLocation() // ,this , “I am in global”
もう一つの例を見ますvar name = "global"
function person() {
console.log(this.name) // (1) "global"
person.name = 'inside'
function sayName() {
console.log(this.name) // (2) "global" "inside"
}
sayName() // person sayName ,this
}
person()
この例では、this
関数がグローバルスコープで呼び出されるので、(1)の文のperson
はグローバルオブジェクトに結び付けられている(ブラウザではthis
、nodeではwindow
)ので、第(1)の文は自然に出力されるのはグローバルオブジェクトのglobal
属性であり、もちろんname
である."global"
関数は、person関数内で呼び出されたが、それでも第2の文のsayName
は、全体的なオブジェクトを指す.this
関数がname属性を設定しても.これはデフォルトのバインディング規則であり、javascriptにおいて最も一般的な関数呼び出しモードであり、
person
のバインディング規則も4つのバインディング規則の中で最も簡単なものであり、グローバル作用領域にバインディングされている.デフォルトのバインディングの厳密なモード
javascriptでは、厳格なモードが使用されている場合、
this
は、グローバルオブジェクトにバインディングできない.初めての例ですが、今回は厳しいパターンを加えて声明します.'use strict'
function sayLocation() {
console.log(this.atWhere)
}
var atWhere = "I am in global"
sayLocation()
// Uncaught TypeError: Cannot read property 'atWhere' of undefined
厳密なモードでthis
をグローバルオブジェクトに結びつけると、実際にはthis
がバインディングされているので、上記のコードはエラーとなることがわかる.陰式バインディング
関数が呼び出されたとき、関数にはいわゆる「ドロップポイント」があると、コンテキストオブジェクトがある場合には、関数の
undefined
がこのコンテキストオブジェクトにバインドされます.上の話がはっきりしないと思ったら、やはりコードを見に来ます.function say() {
console.log(this.name)
}
var obj1 = {
name: "zxt",
say: say
}
var obj2 = {
name: "zxt1",
say: say
}
obj1.say() // zxt
obj2.say() // zxt1
簡単ですよね.上記のコードでは、this
、obj1
は、いわゆるobj2
関数の足掛かりであり、専門的にはコンテキストオブジェクトであり、関数にこのコンテキストオブジェクトを指定すると、関数内部のsay
は、自然にこのコンテキストオブジェクトを指している.これも一般的な関数コールモードである.陰でバインディングするとコンテキストが失われます.
function say() {
console.log(this.name)
}
var name = "global"
var obj = {
name: "inside",
say: say
}
var alias = obj.say // (1)
alias() // "global" (2)
ここで出力されているのが見えますが、なぜ上記と違ってthis
と名前を変えただけですか?まず、上の(1)の文のコードを見てみます.javascriptでは、関数は対象であり、対象間は引用伝達であり、値伝達ではありません.したがって、第(1)の文コードは”global“
、つまりobj.say
であり、alias = obj.say = say
は橋として機能しているだけであり、alias = say
は最終的にobj.say
関数のアドレスを参照しているが、このオブジェクトとは関係がない.これはいわゆる「失われたコンテキスト」です.最終的にはalias
関数が実行されますが、単にsay
関数が実行され、alias
関数が出力されます.明示的なバインディング
明示的なバインディングは、名前の通りにthisをコンテキストに結びつける3つの明示的なバインディングの方法を提供しています.
say
、"global"
、apply
、call
.bind
とapply
の使い方は基本的に似ています.call
は、関数を呼び出すパラメータを配列形式でapply(obj,[arg1,arg2,arg3,...]
の呼び出し関数のパラメータを順次与える.call(obj,arg1,arg2,arg3,...)
関数が実行されたら、新しい関数が返されます.コードで説明します.//
function speak() {
console.log(this.name)
}
var name = "global"
var obj1 = {
name: 'obj1'
}
var obj2 = {
name: 'obj2'
}
speak() // global speak.call(window)
speak.call(window)
speak.call(obj1) // obj1
speak.call(obj2) // obj2
したがって、bind
、apply
の役割は、関数にコンテキストを実行するために結合され、明示的に結合されているので、関数内のthisは、自然にcall
またはcall
によって呼び出されたオブジェクトの上に結合されていることがわかる.//
function count(num1, num2) {
console.log(this.a * num1 + num2)
}
var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
count.call(obj1, 1, 2) // 4
count.apply(obj1, [1, 2]) // 4
count.call(obj2, 1, 2) // 5
count.apply(obj2, [1, 2]) // 5
上記の例では、apply
とapply
の用法の違いを説明しています.call
関数は、指定された実行コンテキストを結びつけた新しい関数を返します.それとも、上記のコードを例にしますか?//
function count(num1, num2) {
console.log(this.a * num1 + num2)
}
var obj1 = {
a: 2
}
var bound1 = count.bind(obj1) //
bound1(1, 2) // 4
var bound2 = count.bind(obj1, 1) //
bound2(2) // 4
var bound3 = count.bind(obj1, 1, 2) //
bound3() //4
var bound4 = count.bind(obj1, 1, 2, 3) // ,
bound4() // 4
したがって、bind
方法は新しい関数を返しただけで、この関数内のthisは実行コンテキストを指定し、この新しい関数を返してパラメータを受け入れることができる.newバインディング
最後に述べる
bind
バインディング規則とは、this
オペレータを介してコンストラクタを呼び出すときに起こるnew
バインディングのことです.まず明確にしたいのは、javascriptには他の言語のようなクラスの概念がありません.コンストラクタも普通の関数だけです.コンストラクターの関数名は大文字で始まるだけです.this
オペレータによって呼び出すことができます.function Person(name,age) {
this.name = name
this.age = age
console.log(" ")
}
Person("zxt",22) // " "
console.log(name) // "zxt"
console.log(age) // 22
var zxt = new Person("zxt",22) // " "
console.log(zxt.name) // "zxt"
console.log(zxt.age) // 22
上記の例では、まずnew
関数が定義されています.普通に呼び出すこともできるし、構造関数として呼び出すこともできます.普通に呼び出した場合は、通常の関数で実行され、文字列が出力されます.Person
オペレータを介して新しいオブジェクトが構成されています.次に、2つの呼び出し方法を見てみます.new
それぞれがどこで最初に一般呼出されたかに結び付けられている場合、先に説明したように、デフォルトバインディング規則が適用され、this
がグローバルオブジェクトにバインディングされている場合、グローバルオブジェクトにはそれぞれthis
とname
の2つの属性が追加されます.age
オペレータによって呼び出されると、関数は1つのオブジェクトに戻り、出力結果からnew
のオブジェクトを参照してください.この返却されたオブジェクトに紐付けされています.したがって、this
バインディングとは、new
オペレータを介して関数を呼び出したときに、新しいオブジェクトが生成され、このオブジェクトにコンストラクション内のnew
をバインドすることを意味します.実際には、this
を使用して関数を呼び出して、次の動作が自動的に実行されます.javascript
new
式の関数呼び出しは自動的にこの新しいオブジェクト4つのバインディングの優先度
this
の4つのnew
バインディング規則は、基本的にすべての関数呼び出しの場合をカバーしています.しかし、これらの4つの規則のうちの2つ以上を同時に適用すると、どのような場合、または4つのバインディングの優先順位はどうなりますか?まず、デフォルトバインディングの優先度が分かりやすいです.最低です.他のjavascript
バインディング規則を適用できない場合にのみ、デフォルトバインディングが呼び出されます.暗黙的バインディングと明示的バインディングですか?それともコードバーですか?コードは嘘をつかないことがあります.function speak() {
console.log(this.name)
}
var obj1 = {
name: 'obj1',
speak: speak
}
var obj2 = {
name: 'obj2'
}
obj1.speak() // obj1 (1)
obj1.speak.call(obj2) // obj2 (2)
したがって、上記のコードではthis
が実行され、this
の関数内部のobj1.speak()
がspeak
を指すので、(1)コード出力はもちろんthis
であるが、obj1
関数内のobj1
からspeak
に明示的に結合されている場合、出力結果はthis
になり、この結果から、明示的なバインディングの優先度は暗黙的なバインディングより高いことが分かります.実は、obj2
のこのコードは間接的にobj2
を獲得しただけです.関数の参照は、前に述べたように、暗黙的なバインディングが文脈を失ってしまいます.はい、明示的なバインディングの優先度が陰的バインディングより高いなら、次にnewバインディングと明示的なバインディングを比較します.function foo(something) {
this.a = something
}
var obj1 = {}
var bar = foo.bind(obj1) // bar, this obj1 (1)
bar(2) // this Obj1 , obj1.a === 2
console.log(obj1.a)
var baz = new bar(3) // new ,bar this baz (2)
console.log(obj1.a)
console.log(baz.a)
(1)において、obj1.speak.call(obj2)
関数内部のobj1.speak
は、元々speak
を指していたが、(2)においてはbar
オペレータによって呼び出されたので、this
の関数内部のobj1
は戻りの例に再び向けられている.これは、newバインディングの優先度が、明示的バインディングよりも高いことを示している.これにより、4つのバインディング規則の優先順位付けが得られた.newバインディング>明示的バインディング>陰的バインディング>標準バインディング
矢印関数のthisバインディング
矢印関数はES 6の重要な特性です.矢印関数の
new
は、外層(関数または大域)の作用領域によって決定されます.関数体内のbar
オブジェクトは、前に紹介した呼び出し時に結合されたオブジェクトではなく、定義時に存在するオブジェクトを意味します.例を挙げます.var a = 1
var foo = () => {
console.log(this.a) // , this
}
var obj = {
a: 2
}
foo() // 1 ,
foo.call(obj) // 1, , obj ,
上記の例から、矢印関数のthis強制バインディングは矢印関数定義時に存在する作用領域であり、this
、this
のようなバインディングを表示することによって変更できないことが分かりました.//
function Person(name,age) {
this.name = name
this.age = age
this.speak = function (){
console.log(this.name)
// ( ),this
}
this.bornYear = () => {
// 2016 , new Date().getFullYear() 2016
// ,this
console.log(new Date().getFullYear() - this.age)
}
}
}
var zxt = new Person("zxt",22)
zxt.speak() // "zxt"
zxt.bornYear() // 1994
//
var xiaoMing = {
name: "xiaoming",
age: 18 // 18
}
zxt.speak.call(xiaoMing)
// "xiaoming" this xiaoMing
zxt.bornYear.call(xiaoMing)
// 1994 1998, this zxt
したがって、ES 6の矢印関数は、4つの標準的なバインディング規則を使用するのではなく、現在の用語作用領域に基づいてthis
を決定し、特に、矢印関数は、外層関数によって呼び出されたthisバインディングを継承し、外層関数のapply
がどこにバインディングされているかに関係なく.結び目
以上が
call
におけるthis
すべてのバインディングの場合であり、第6に先立って前述した4つのバインディング規則は、任意の関数呼び出しの場合をカバーすることができ、S 6標準が実施された後、関数の拡張に対して矢印関数が追加され、以前とは違って、矢印関数の役割領域は矢印関数が定義された時に存在する役割領域にある.前の4つのバインディング規則については、各規則の呼び出し条件を把握することによって、どの作用領域に結合されているのかをよく理解することができる.
全文が終わる