2021-4-22 前端達人
this指向的問題也是JavaScript中的難點之一了,也是面試常問的問題,很多時候?qū)his指向的問題就很懵逼,明明應(yīng)該是指向他,為什么又指向他了…所以我就學(xué)習(xí)了一下這方面的知識,整理了一下,希望能夠幫助大家
首先看一段代碼
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 中重復(fù)的使用identify函數(shù)和speak函數(shù)
如果你不使用this的話 你就要顯式的將上下文對象作為參數(shù)傳遞進去,比如這樣:
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指向是這個函數(shù)自身,或者是函數(shù)的作用域,后來發(fā)現(xiàn)其實不是這樣的的,不過也不能說錯了,因為有些情況確實是這樣的,比如這樣:
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屬性++
無法指向函數(shù)作用域
var a = 3
function foo() {
var a = 2;
bar.call(foo);
}
function bar() {
console.log( this.a );
}
foo(); // undefined
我們要記住非常重要的一點:this是在運行的時候進行綁定的,而不是在定義的時候綁定,this的綁定跟函數(shù)聲明的位置沒有關(guān)系,主要是取決于函數(shù)的調(diào)用方式,想要找到this指向誰,我們就要看懂函數(shù)是怎么調(diào)用的。
1.默認(rèn)綁定
當(dāng)一個獨立函數(shù)正常調(diào)用的時候,不帶任何修飾的調(diào)用
// 非嚴(yán)格模式下
var a = 3
function foo(){
console.log(this.a) //a
}
foo()
這種情況下 this.a被解析成了了 全局變量a,this指向是全局對象
// 嚴(yán)格模式下
var a = 3
function foo(){
"use strict"
console.log(this.a) //TypeError
}
foo()
嚴(yán)格模式下 this不會指向全局對象 this綁定的是undefined
2.隱式綁定
調(diào)用位置上是否有上下文對象
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
obj.foo() //2
調(diào)用位置會使用obj上下文對象來引用函數(shù),foo被調(diào)用的時候 他的落腳點指向是obj對象,隱式綁定的規(guī)則就會把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
當(dāng)多層調(diào)用的時候 只有最后一層才會影響函數(shù)的調(diào)用位置 比如上面這個 this綁定的還是 obj 而不是obj2
注意
隱式綁定會出現(xiàn)隱式丟失的問題,會失去綁定對象,最后應(yīng)用默認(rèn)綁定
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函數(shù)本身,此時bar就是一個不帶任何修飾的函數(shù)調(diào)用 應(yīng)用默認(rèn)綁定
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
參數(shù)傳遞也是一種隱式賦值,回調(diào)函數(shù)丟失this是非常常見的…
3.顯式綁定
隱式綁定的時候我們必須在一個對象內(nèi)部包含一個指向函數(shù)的屬性,然后通過屬性間接引用函數(shù),把這個this間接隱式的綁定到這個對象上
如果我們不想在對象內(nèi)部包含函數(shù)的引用 ,而想在某個對象上強制調(diào)用函數(shù)
我們可以把這個函數(shù)綁定到對象的原型上,也算是不用再對象內(nèi)部包含函數(shù)了吧…
更好的辦法是我們可以使用函數(shù)的 call() apply() bind() 這種方法
function foo(){
console.log(this.a)
}
var obj = {
a:2
}
foo.call(obj) //2
foo.apply(obj) //2
如果你第一個參數(shù)傳入的是一個原始類型 比如字符串 布爾 數(shù)字作為綁定對象 這些原始類型會被轉(zhuǎn)換為 對象的形式 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) //會返回一個硬編碼的新函數(shù) 他會把參數(shù)設(shè)置為this的上下文
bar.call(obj2) //2 返回的新函數(shù)
有些api 方法 會提供一個可選參數(shù) context 其作用跟bind一樣 確保你的回調(diào)函數(shù)使用指定的this 比如 array.forEach(fn,context)…
4.new綁定
使用new 來調(diào)用函數(shù)的時候會執(zhí)行以下操作
1.創(chuàng)建一個全新的對象
2.這個新對象會被執(zhí)行原型的鏈接
3.新對象會綁定到函數(shù)調(diào)用的this
4.如果沒有返回其他的對象,那么函數(shù)會自動返回這個對象
function Foo(a){
this.a = a
}
var bar = new Foo(2)
console.log(bar.a) //2
使用new 來調(diào)用Foo函數(shù) 會構(gòu)造一個新對象并把它綁定到Foo調(diào)用中的this上 然后返回了
函數(shù)不帶任何修飾的時候單獨調(diào)用才會觸發(fā)默認(rèn)綁定 所以說默認(rèn)綁定是優(yōu)先級最低的了
那剩下三個規(guī)則哪個的優(yōu)先級最高?
顯示綁定跟隱式綁定比較
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
可以看到 顯示綁定的優(yōu)先級還是更高一點
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綁定的優(yōu)先級比隱式綁定要高
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 調(diào)用bar修改了硬綁定時候的 函數(shù)的this new的優(yōu)先級高一點
所以我們可以根據(jù)下面的優(yōu)先級規(guī)則進行判斷了
1.函數(shù)是否在new中調(diào)用 是的話this綁定新創(chuàng)建的對象 var bar = new Foo()
2.函數(shù)是否通過call apply 顯示綁定或者是 bind硬綁定 如果是的話this指向指定的對象 foo.call(obj)
3.函數(shù)是否在某個上下文中調(diào)用 隱式綁定,如果是 this綁定那個上下文對象 注意綁定丟失的問題
4.如果都不是 就是默認(rèn)綁定非嚴(yán)格模式下綁定的是全局對象 嚴(yán)格模式下綁定的是undefined
1.將null和undefined作為call apply參數(shù) 作為this綁定對象的時候 這些值會被忽略 應(yīng)用的是默認(rèn)綁定
var a =3
function foo(){
console.log(this.a) //3
}
foo.call(null)
2.箭頭函數(shù)
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)
箭頭函數(shù)不使用this綁定的四種規(guī)則,而是根據(jù)外層作用域來決定this的,外層作用域的this綁定的是什么 他的this就是什么
轉(zhuǎn)自:csdn 論壇 作者:Selfimpr歐
藍藍設(shè)計( sillybuy.com )是一家專注而深入的界面設(shè)計公司,為期望卓越的國內(nèi)外企業(yè)提供卓越的UI界面設(shè)計、BS界面設(shè)計 、 cs界面設(shè)計 、 ipad界面設(shè)計 、 包裝設(shè)計 、 圖標(biāo)定制 、 用戶體驗 、交互設(shè)計、 網(wǎng)站建設(shè) 、平面設(shè)計服務(wù)
藍藍設(shè)計的小編 http://sillybuy.com