this指向的問題也是JavaScript中的難點之一了,也是面試常問的問題,很多時候對this指向的問題就很懵逼,明明應該是指向他,為什么又指向他了…所以我就學習了一下這方面的知識,整理了一下,希望能夠幫助大家
為什么要用this?
首先看一段代碼
function identify(){
return this.name.toUpperCase()
}
function speak(){
var greeting = '你好,我是'+identify.call(this)
console.log(greeting)
}
var me ={
name:'kyle'
}
var you ={
name:"reader"
}
identify.call(me)
identify.call(you)
speak.call(me) //? 你好,我是KYLE
speak.call(you) //? 你好,我是READER
上面的這段代碼中可以從不同的背景關系物件 me 和 you 中重復的使用identify函式和speak函式
如果你不使用this的話 你就要顯式的將背景關系物件作為引數傳遞進去,比如這樣:
function identify(context){
return context.name.toUpperCase()
}
function speak(context){
var greeting = '你好,我是'+identify(context)
console.log(greeting)
}
var me ={
name:'kyle'
}
var you ={
name:"reader"
}
identify(me)
identify(you)
speak(me)
speak(you)
就像這樣,這樣看起來就不想上面那樣簡潔了,你要把一個物件傳來傳去的
認識this
剛見到this的時候 覺得this指向是這個函式自身,或者是函式的作用域,后來發現其實不是這樣的的,不過也不能說錯了,因為有些情況確實是這樣的,比如這樣:
function foo(num){
console.log('foo'+ num)
this.count ++
}
foo.count = 0
var i;
for(i = 0;i<10;i++){
if(i>5){
foo.call(foo,i)
}
}
console.log(foo.count) //4 這樣的話 this指向了foo本身 foo上面的count屬性++
無法指向函式作用域
var a = 3
function foo() {
var a = 2;
bar.call(foo);
}
function bar() {
console.log( this.a );
}
foo(); // undefined
我們要記住非常重要的一點:this是在運行的時候進行系結的,而不是在定義的時候系結,this的系結跟函式宣告的位置沒有關系,主要是取決于函式的呼叫方式,想要找到this指向誰,我們就要看懂函式是怎么呼叫的,
系結規則
1.默認系結
當一個獨立函式正常呼叫的時候,不帶任何修飾的呼叫
// 非嚴格模式下
var a = 3
function foo(){
console.log(this.a) //a
}
foo()
這種情況下 this.a被決議成了了 全域變數a,this指向是全域物件
// 嚴格模式下
var a = 3
function foo(){
"use strict"
console.log(this.a) //TypeError
}
foo()
嚴格模式下 this不會指向全域物件 this系結的是undefined
2.隱式系結
呼叫位置上是否有背景關系物件
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
obj.foo() //2
呼叫位置會使用obj背景關系物件來參考函式,foo被呼叫的時候 他的落腳點指向是obj物件,隱式系結的規則就會把this指向這個背景關系物件,所以this.a就跟 obj.a是一樣的
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
var obj2 = {
a:3,
obj:obj
}
obj2.obj.foo() //2
當多層呼叫的時候 只有最后一層才會影響函式的呼叫位置 比如上面這個 this系結的還是 obj 而不是obj2
注意
隱式系結會出現隱式丟失的問題,會失去系結物件,最后應用默認系結
var a = 3;
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo
bar() //3
bar 是 obj.foo的一個參考 他參考的是foo函式本身,此時bar就是一個不帶任何修飾的函式呼叫 應用默認系結
var a = 3;
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
setTimeout( obj.foo, 100 ) //3
setTimeout(function(fn){
fn()
},100,obj.foo) //3
引數傳遞也是一種隱式賦值,回呼函式丟失this是非常常見的…
3.顯式系結
隱式系結的時候我們必須在一個物件內部包含一個指向函式的屬性,然后通過屬性間接參考函式,把這個this間接隱式的系結到這個物件上
如果我們不想在物件內部包含函式的參考 ,而想在某個物件上強制呼叫函式
我們可以把這個函式系結到物件的原型上,也算是不用再物件內部包含函式了吧…
更好的辦法是我們可以使用函式的 call() apply() bind() 這種方法
function foo(){
console.log(this.a)
}
var obj = {
a:2
}
foo.call(obj) //2
foo.apply(obj) //2
如果你第一個引數傳入的是一個原始型別 比如字串 布爾 數字作為系結物件 這些原始型別會被轉換為 物件的形式 new String() new Number()…
硬系結
Function.prototype.bind()
function foo(){
console.log(this.a)
}
var obj = {
a:2
}
var obj2 = {
a:3
}
var bar = foo.bind(obj) //會回傳一個硬編碼的新函式 他會把引數設定為this的背景關系
bar.call(obj2) //2 回傳的新函式
有些api 方法 會提供一個可選引數 context 其作用跟bind一樣 確保你的回呼函數使用指定的this 比如 array.forEach(fn,context)…
4.new系結
使用new 來呼叫函式的時候會執行以下操作
1.創建一個全新的物件
2.這個新物件會被執行原型的鏈接
3.新物件會系結到函式呼叫的this
4.如果沒有回傳其他的物件,那么函式會自動回傳這個物件
function Foo(a){
this.a = a
}
var bar = new Foo(2)
console.log(bar.a) //2
使用new 來呼叫Foo函式 會構造一個新物件并把它系結到Foo呼叫中的this上 然后回傳了
優先級
函式不帶任何修飾的時候單獨呼叫才會觸發默認系結 所以說默認系結是優先級最低的了
那剩下三個規則哪個的優先級最高?
顯示系結跟隱式系結比較
function foo(){
console.log(this.a)
}
var obj1 = {
a:1,
foo:foo
}
var obj2 = {
a:2,
foo:foo
}
obj1.foo() //1
obj2.foo() //2
obj1.foo.call(obj2) //2
obj2.foo.call(obj1) //1
可以看到 顯示系結的優先級還是更高一點
new 系結跟隱式系結比較
function foo(arg){
this.a = arg
}
var obj1 ={
foo:foo
}
var obj2 ={}
obj1.foo(2)
console.log(obj1.a) //2
var bar = new obj1.foo(4)
console.log(obj1.a) //2
console.log(bar.a) //4
可以看到 new系結的優先級比隱式系結要高
new 系結跟顯示系結比較
new跟call apply無法一起使用 無法通過new foo.call(obj),試一下硬系結
在這里插入代碼片
function foo(arg){
this.a = arg
}
var obj1 ={}
var bar = foo.bind(obj1)
bar(3)
console.log(obj1.a) //3
var baz = new bar(4)
console.log(baz.a) //4
console.log(obj1.a) //3
new 呼叫bar修改了硬系結時候的 函式的this new的優先級高一點
所以我們可以根據下面的優先級規則進行判斷了
1.函式是否在new中呼叫 是的話this系結新創建的物件 var bar = new Foo()
2.函式是否通過call apply 顯示系結或者是 bind硬系結 如果是的話this指向指定的物件 foo.call(obj)
3.函式是否在某個背景關系中呼叫 隱式系結,如果是 this系結那個背景關系物件 注意系結丟失的問題
4.如果都不是 就是默認系結非嚴格模式下系結的是全域物件 嚴格模式下系結的是undefined
系結例外
1.將null和undefined作為call apply引數 作為this系結物件的時候 這些值會被忽略 應用的是默認系結
var a =3
function foo(){
console.log(this.a) //3
}
foo.call(null)
2.箭頭函式
function foo(){
return ()=>{
console.log(this.a)
}
}
var obj1 = {
a:3
}
var obj2 = {
a:4
}
var bar = foo.call(obj1)
bar.call(obj2) //3 this系結的是obj1 而不是obj2!!!
在看一個
function foo(){
setTimeout(()=>{
console.log(this.a) //2
},100)
}
var obj = {
a:2
}
foo.call(obj)
箭頭函式不使用this系結的四種規則,而是根據外層作用域來決定this的,外層作用域的this系結的是什么 他的this就是什么
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/278879.html
標籤:其他
上一篇:Qt開發經驗小技巧131-140
