千鋒教育JavaScript全套視頻教程(10天學(xué)會Js,前端javascrip

一.this對象的理解
函數(shù)的 this
關(guān)鍵字在 JavaScript
中的表現(xiàn)略有不同,此外,在嚴(yán)格模式和非嚴(yán)格模式之間也會有一些差別
在絕大多數(shù)情況下,函數(shù)的調(diào)用方式?jīng)Q定了 this
的值(運(yùn)行時(shí)綁定)
this
關(guān)鍵字是函數(shù)運(yùn)行時(shí)自動生成的一個內(nèi)部對象,只能在函數(shù)內(nèi)部使用,總指向調(diào)用它的對象
舉個例子:
function baz() { ??// 當(dāng)前調(diào)用棧是:baz ??// 因此,當(dāng)前調(diào)用位置是全局作用域 ?? ??console.log( "baz" ); ??bar(); // <-- bar的調(diào)用位置 } function bar() { ??// 當(dāng)前調(diào)用棧是:baz --> bar ??// 因此,當(dāng)前調(diào)用位置在baz中 ?? ??console.log( "bar" ); ??foo(); // <-- foo的調(diào)用位置 } function foo() { ??// 當(dāng)前調(diào)用棧是:baz --> bar --> foo ??// 因此,當(dāng)前調(diào)用位置在bar中 ?? ??console.log( "foo" ); } baz(); // <-- baz的調(diào)用位置
同時(shí),this
在函數(shù)執(zhí)行過程中,this
一旦被確定了,就不可以再更改
var a = 10; var obj = { ?a: 20 } function fn() { ?this = obj; // 修改this,運(yùn)行后會報(bào)錯 ?console.log(this.a); } fn();
二、綁定規(guī)則
根據(jù)不同的使用場合,this
有不同的值,主要分為下面幾種情況:
- 默認(rèn)綁定
- 隱式綁定
- new綁定
- 顯示綁定
默認(rèn)綁定
全局環(huán)境中定義person
函數(shù),內(nèi)部使用this
關(guān)鍵字
var name = 'Jenny'; function person() { ??return this.name; } console.log(person());?//Jenny
上述代碼輸出Jenny
,原因是調(diào)用函數(shù)的對象在游覽器中位window
,因此this
指向window
,所以輸出Jenny
注意:
嚴(yán)格模式下,不能將全局對象用于默認(rèn)綁定,this會綁定到undefined
,只有函數(shù)運(yùn)行在非嚴(yán)格模式下,默認(rèn)綁定才能綁定到全局對象
隱式綁定
函數(shù)還可以作為某個對象的方法調(diào)用,這時(shí)this
就指這個上級對象
function test() { ?console.log(this.x); } var obj = {}; obj.x = 1; obj.m = test; obj.m(); // 1
這個函數(shù)中包含多個對象,盡管這個函數(shù)是被最外層的對象所調(diào)用,this
指向的也只是它上一級的對象
var o = { ??a:10, ??b:{ ????fn:function(){ ??????console.log(this.a); //undefined ???} ?} } o.b.fn();
上述代碼中,this
的上一級對象為b
,b
內(nèi)部并沒有a
變量的定義,所以輸出undefined
這里再舉一種特殊情況
var o = { ??a:10, ??b:{ ????a:12, ????fn:function(){ ??????console.log(this.a); //undefined ??????console.log(this); //window ???} ?} } var j = o.b.fn; j();
此時(shí)this
指向的是window
,這里的大家需要記住,this
永遠(yuǎn)指向的是最后調(diào)用它的對象,雖然fn
是對象b
的方法,但是fn
賦值給j
時(shí)候并沒有執(zhí)行,所以最終指向window
new綁定
通過構(gòu)建函數(shù)new
關(guān)鍵字生成一個實(shí)例對象,此時(shí)this
指向這個實(shí)例對象
function test() { this.x = 1; } var obj = new test(); obj.x // 1
上述代碼之所以能過輸出1,是因?yàn)?code>new關(guān)鍵字改變了this
的指向
這里再列舉一些特殊情況:
new
過程遇到return
一個對象,此時(shí)this
指向?yàn)榉祷氐膶ο?/p>
function fn()? {? ??this.user = 'xxx';? ??return {};? } var a = new fn();? console.log(a.user); //undefined
如果返回一個簡單類型的時(shí)候,則this
指向?qū)嵗龑ο?/p>
function fn()? {? ??this.user = 'xxx';? ??return 1; } var a = new fn;? console.log(a.user); //xxx
注意的是null
雖然也是對象,但是此時(shí)new
仍然指向?qū)嵗龑ο?/p>
function fn()? {? ??this.user = 'xxx';? ??return null; } var a = new fn;? console.log(a.user); //xxx
顯示修改
apply()、call()、bind()
是函數(shù)的一個方法,作用是改變函數(shù)的調(diào)用對象。它的第一個參數(shù)就表示改變后的調(diào)用這個函數(shù)的對象。因此,這時(shí)this
指的就是這第一個參數(shù)
var x = 0; function test() { console.log(this.x); } var obj = {}; obj.x = 1; obj.m = test; obj.m.apply(obj) // 1
關(guān)于apply、call、bind
三者的區(qū)別,我們后面再詳細(xì)說
三、箭頭函數(shù)
在 ES6 的語法中還提供了箭頭函語法,讓我們在代碼書寫時(shí)就能確定 this
的指向(編譯時(shí)綁定)
舉個例子:
const obj = { ?sayThis: () => { ??console.log(this); } }; obj.sayThis(); // window 因?yàn)?JavaScript 沒有塊作用域,所以在定義 sayThis 的時(shí)候,里面的 this 就綁到 window 上去了 const globalSay = obj.sayThis; globalSay(); // window 瀏覽器中的 global 對象
雖然箭頭函數(shù)的this
能夠在編譯的時(shí)候就確定了this
的指向,但也需要注意一些潛在的坑
下面舉個例子:
綁定事件監(jiān)聽
const button = document.getElementById('mngb'); button.addEventListener('click', ()=> { ??console.log(this === window) // true ??this.innerHTML = 'clicked button' })
上述可以看到,我們其實(shí)是想要this
為點(diǎn)擊的button
,但此時(shí)this
指向了window
包括在原型上添加方法時(shí)候,此時(shí)this
指向window
Cat.prototype.sayName = () => { ??console.log(this === window) //true ??return this.name } const cat = new Cat('mm'); cat.sayName()
同樣的,箭頭函數(shù)不能作為構(gòu)建函數(shù)
四、優(yōu)先級
隱式綁定 VS 顯式綁定
function foo() { ??console.log( this.a ); } var obj1 = { ??a: 2, ??foo: foo }; var obj2 = { ??a: 3, ??foo: foo }; obj1.foo(); // 2 obj2.foo(); // 3 obj1.foo.call( obj2 ); // 3 obj2.foo.call( obj1 ); // 2
顯然,顯示綁定的優(yōu)先級更高
new綁定 VS 隱式綁定
function foo(something) { ??this.a = something; } var obj1 = { ??foo: foo }; var obj2 = {}; obj1.foo( 2 ); console.log( obj1.a ); // 2 obj1.foo.call( obj2, 3 ); console.log( obj2.a ); // 3 var bar = new obj1.foo( 4 ); console.log( obj1.a ); // 2 console.log( bar.a ); // 4
可以看到,new綁定的優(yōu)先級>
隱式綁定
new
綁定 VS 顯式綁定
因?yàn)?code>new和apply、call
無法一起使用,但硬綁定也是顯式綁定的一種,可以替換測試
function foo(something) { ??this.a = something; } var obj1 = {}; var bar = foo.bind( obj1 ); bar( 2 ); console.log( obj1.a ); // 2 var baz = new bar( 3 ); console.log( obj1.a ); // 2 console.log( baz.a ); // 3
bar
被綁定到obj1上,但是new bar(3)
并沒有像我們預(yù)計(jì)的那樣把obj1.a
修改為3。但是,new
修改了綁定調(diào)用bar()
中的this
我們可認(rèn)為new
綁定優(yōu)先級>
顯式綁定
綜上,new綁定優(yōu)先級 > 顯示綁定優(yōu)先級 > 隱式綁定優(yōu)先級 > 默認(rèn)綁定優(yōu)先級函數(shù)的 this
關(guān)鍵字在 JavaScript
中的表現(xiàn)略有不同,此外,在嚴(yán)格模式和非嚴(yán)格模式之間也會有一些差別
在絕大多數(shù)情況下,函數(shù)的調(diào)用方式?jīng)Q定了 this
的值(運(yùn)行時(shí)綁定)
this
關(guān)鍵字是函數(shù)運(yùn)行時(shí)自動生成的一個內(nèi)部對象,只能在函數(shù)內(nèi)部使用,總指向調(diào)用它的對象
舉個例子:
function baz() { ??// 當(dāng)前調(diào)用棧是:baz ??// 因此,當(dāng)前調(diào)用位置是全局作用域 ?? ??console.log( "baz" ); ??bar(); // <-- bar的調(diào)用位置 } function bar() { ??// 當(dāng)前調(diào)用棧是:baz --> bar ??// 因此,當(dāng)前調(diào)用位置在baz中 ?? ??console.log( "bar" ); ??foo(); // <-- foo的調(diào)用位置 } function foo() { ??// 當(dāng)前調(diào)用棧是:baz --> bar --> foo ??// 因此,當(dāng)前調(diào)用位置在bar中 ?? ??console.log( "foo" ); } baz(); // <-- baz的調(diào)用位置
同時(shí),this
在函數(shù)執(zhí)行過程中,this
一旦被確定了,就不可以再更改
var a = 10; var obj = { ?a: 20 } function fn() { ?this = obj; // 修改this,運(yùn)行后會報(bào)錯 ?console.log(this.a); } fn();
二、綁定規(guī)則
根據(jù)不同的使用場合,this
有不同的值,主要分為下面幾種情況:
- 默認(rèn)綁定
- 隱式綁定
- new綁定
- 顯示綁定
默認(rèn)綁定
全局環(huán)境中定義person
函數(shù),內(nèi)部使用this
關(guān)鍵字
var name = 'Jenny'; function person() { ??return this.name; } console.log(person());?//Jenny
上述代碼輸出Jenny
,原因是調(diào)用函數(shù)的對象在游覽器中位window
,因此this
指向window
,所以輸出Jenny
注意:
嚴(yán)格模式下,不能將全局對象用于默認(rèn)綁定,this會綁定到undefined
,只有函數(shù)運(yùn)行在非嚴(yán)格模式下,默認(rèn)綁定才能綁定到全局對象
隱式綁定
函數(shù)還可以作為某個對象的方法調(diào)用,這時(shí)this
就指這個上級對象
function test() { ?console.log(this.x); } var obj = {}; obj.x = 1; obj.m = test; obj.m(); // 1
這個函數(shù)中包含多個對象,盡管這個函數(shù)是被最外層的對象所調(diào)用,this
指向的也只是它上一級的對象
var o = { ??a:10, ??b:{ ????fn:function(){ ??????console.log(this.a); //undefined ???} ?} } o.b.fn();
上述代碼中,this
的上一級對象為b
,b
內(nèi)部并沒有a
變量的定義,所以輸出undefined
這里再舉一種特殊情況
var o = { ??a:10, ??b:{ ????a:12, ????fn:function(){ ??????console.log(this.a); //undefined ??????console.log(this); //window ???} ?} } var j = o.b.fn; j();
此時(shí)this
指向的是window
,這里的大家需要記住,this
永遠(yuǎn)指向的是最后調(diào)用它的對象,雖然fn
是對象b
的方法,但是fn
賦值給j
時(shí)候并沒有執(zhí)行,所以最終指向window
new綁定
通過構(gòu)建函數(shù)new
關(guān)鍵字生成一個實(shí)例對象,此時(shí)this
指向這個實(shí)例對象
function test() { this.x = 1; } var obj = new test(); obj.x // 1
上述代碼之所以能過輸出1,是因?yàn)?code>new關(guān)鍵字改變了this
的指向
這里再列舉一些特殊情況:
new
過程遇到return
一個對象,此時(shí)this
指向?yàn)榉祷氐膶ο?/p>
function fn()? {? ??this.user = 'xxx';? ??return {};? } var a = new fn();? console.log(a.user); //undefined
如果返回一個簡單類型的時(shí)候,則this
指向?qū)嵗龑ο?/p>
function fn()? {? ??this.user = 'xxx';? ??return 1; } var a = new fn;? console.log(a.user); //xxx
注意的是null
雖然也是對象,但是此時(shí)new
仍然指向?qū)嵗龑ο?/p>
function fn()? {? ??this.user = 'xxx';? ??return null; } var a = new fn;? console.log(a.user); //xxx
顯示修改
apply()、call()、bind()
是函數(shù)的一個方法,作用是改變函數(shù)的調(diào)用對象。它的第一個參數(shù)就表示改變后的調(diào)用這個函數(shù)的對象。因此,這時(shí)this
指的就是這第一個參數(shù)
var x = 0; function test() { console.log(this.x); } var obj = {}; obj.x = 1; obj.m = test; obj.m.apply(obj) // 1
關(guān)于apply、call、bind
三者的區(qū)別,我們后面再詳細(xì)說
三、箭頭函數(shù)
在 ES6 的語法中還提供了箭頭函語法,讓我們在代碼書寫時(shí)就能確定 this
的指向(編譯時(shí)綁定)
舉個例子:
const obj = { ?sayThis: () => { ??console.log(this); } }; obj.sayThis(); // window 因?yàn)?JavaScript 沒有塊作用域,所以在定義 sayThis 的時(shí)候,里面的 this 就綁到 window 上去了 const globalSay = obj.sayThis; globalSay(); // window 瀏覽器中的 global 對象
雖然箭頭函數(shù)的this
能夠在編譯的時(shí)候就確定了this
的指向,但也需要注意一些潛在的坑
下面舉個例子:
綁定事件監(jiān)聽
const button = document.getElementById('mngb'); button.addEventListener('click', ()=> { ??console.log(this === window) // true ??this.innerHTML = 'clicked button' })
上述可以看到,我們其實(shí)是想要this
為點(diǎn)擊的button
,但此時(shí)this
指向了window
包括在原型上添加方法時(shí)候,此時(shí)this
指向window
Cat.prototype.sayName = () => { ??console.log(this === window) //true ??return this.name } const cat = new Cat('mm'); cat.sayName()
同樣的,箭頭函數(shù)不能作為構(gòu)建函數(shù)
四、優(yōu)先級
隱式綁定 VS 顯式綁定
function foo() { ??console.log( this.a ); } var obj1 = { ??a: 2, ??foo: foo }; var obj2 = { ??a: 3, ??foo: foo }; obj1.foo(); // 2 obj2.foo(); // 3 obj1.foo.call( obj2 ); // 3 obj2.foo.call( obj1 ); // 2
顯然,顯示綁定的優(yōu)先級更高
new綁定 VS 隱式綁定
function foo(something) { ??this.a = something; } var obj1 = { ??foo: foo }; var obj2 = {}; obj1.foo( 2 ); console.log( obj1.a ); // 2 obj1.foo.call( obj2, 3 ); console.log( obj2.a ); // 3 var bar = new obj1.foo( 4 ); console.log( obj1.a ); // 2 console.log( bar.a ); // 4
可以看到,new綁定的優(yōu)先級>
隱式綁定
new
綁定 VS 顯式綁定
因?yàn)?code>new和apply、call
無法一起使用,但硬綁定也是顯式綁定的一種,可以替換測試
function foo(something) { ??this.a = something; } var obj1 = {}; var bar = foo.bind( obj1 ); bar( 2 ); console.log( obj1.a ); // 2 var baz = new bar( 3 ); console.log( obj1.a ); // 2 console.log( baz.a ); // 3
bar
被綁定到obj1上,但是new bar(3)
并沒有像我們預(yù)計(jì)的那樣把obj1.a
修改為3。但是,new
修改了綁定調(diào)用bar()
中的this
我們可認(rèn)為new
綁定優(yōu)先級>
顯式綁定
綜上,new綁定優(yōu)先級 > 顯示綁定優(yōu)先級 > 隱式綁定優(yōu)先級 > 默認(rèn)綁定優(yōu)先級