一、this指向詳解
this的指向在函數定義的時候是確定不了的,只有函數執行的時候才能確定this到底指向誰,實際上this的最終指向的是那個調用它的對象(這句話不那么嚴謹,作為一般情況下是可以的)
再加下面的補充,就完美啦:
情況1:如果一個函數中有this,但是它沒有被上一級的對象所調用,那么this指向的就是window,這里需要說明的是在js的嚴格版中this指向的不是window,但是我們這里不探討嚴格版的問題(在嚴格版中的默認的this不再是window,而是undefined。
function a(){ var user = "追夢子"; console.log(this.user); //undefined console.log(this); //Window } a(); //相當於window.a(); //this最終指向的是調用它的對象,這里的函數a實際是被Window對象所點出來的
情況2:如果一個函數中有this,這個函數有被上一級的對象所調用,那么this指向的就是上一級的對象。
var o = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
console.log(this); //{user: "追夢子", fn: ƒ}
}
}
o.fn(); //this執行時被它的上一級對象o{user: "追夢子", fn: ƒ}調用
情況3:如果一個函數中有this,這個函數中包含多個對象,盡管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象
var o = {
a:10,
b:{
//a:12,
fn:function(){
console.log(this.a); //undefind 有兩個對象b和o,所以此this.a指向它的上一級
}
},
fn1:function(){
console.log(this.a); //10
}
}
o.fn1();
o.b.fn();
另外還有兩種種特殊情況:
第一種:當this遇到return
function fn()
{
this.user = '追夢子';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = '追夢子';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = '追夢子';
return 1;
}
var a = new fn;
console.log(a.user); //追夢子
function fn()
{
this.user = '追夢子';
return undefined;
}
var a = new fn;
console.log(a.user); //追夢子
function fn()
{
this.user = '追夢子';
return null;
}
var a = new fn;
console.log(a.user); //追夢子
總結:如果返回值是一個對象,那么this指向的就是那個返回的對象,如果返回值不是一個對象那么this還是指向函數的實例。
還有一點就是雖然null也是對象,但是在這里this還是指向那個函數的實例,因為null比較特殊。
第二種:
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
這里this指向的是window,是不是有些蒙了?其實是因為你沒有理解一句話,這句話同樣至關重要。
this永遠指向的是最后調用它的對象,也就是看它執行的時候是誰調用的,例子4中雖然函數fn是被對象b所引用,但是在將fn賦值給變量j的時候並沒有執行所以最終指向的是window
二、改變this指向的方法
第一種: new關鍵字改變this指向
//構造函數版this
function Fn(){
this.user = "追夢子";
}
var a = new Fn();
console.log(a.user); //追夢子
用變量a創建了一個Fn的實例(相當於復制了一份Fn到對象a里面),此時僅僅只是創建,並沒有執行,而調用這個函數Fn的是對象a,那么this指向的自然是對象a,那么為什么對象a中會有user,因為你已經復制了一份Fn函數到對象a中,用了new關鍵字就等同於復制了一份
第二種: call()
var a = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
}
}
var b = a.fn;
b.call(a); //若不用call,則b()執行后this指的是Window對象
把b添加到第一個參數的環境中,簡單來說,this就會指向那個對象。
call方法除了第一個參數以外還可以添加多個參數,如下:
var a = {
user:"追夢子",
fn:function(e,ee){
console.log(this.user); //追夢子
console.log(e+ee); //3
}
}
var b = a.fn;
b.call(a,1,2);
第三種:apply()
var a = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
}
}
var b = a.fn;
b.apply(a);
apply方法和call方法有些相似,它也可以改變this的指向,也可以有多個參數,但是不同的是,第二個參數必須是一個數組,如下:
var a = {
user:"追夢子",
fn:function(e,ee){
console.log(this.user); //追夢子
console.log(e+ee); //11
}
}
var b = a.fn;
b.apply(a,[10,1]);
//注意如果call和apply的第一個參數寫的是null,那么this指向的是window對象
var a = {
user:"追夢子",
fn:function(){
console.log(this); //Window {external: Object, chrome: Object, document: document, a: Object, speechSynthesis: SpeechSynthesis…}
}
}
var b = a.fn;
b.apply(null);
第四種:bind()
bind方法和call、apply方法有些不同,如下:
var a = {
user:"追夢子",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a); //代碼沒有被打印
我們發現代碼沒有被打印,對,這就是bind和call、apply方法的不同,實際上bind方法返回的是一個修改過后的函數。
var a = {
user:"追夢子",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
var c = b.bind(a);
console.log(c); //function() { [native code] }
函數c看看,能不能打印出對象a里面的user
var a = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
}
}
var b = a.fn;
var c = b.bind(a);
c();
同樣bind也可以有多個參數,並且參數可以執行的時候再次添加,但是要注意的是,參數是按照形參的順序進行的。
var a = {
user:"追夢子",
fn:function(e,d,f){
console.log(this.user); //追夢子
console.log(e,d,f); //10 1 2
}
}
var b = a.fn;
var c = b.bind(a,10);
c(1,2);
總結: call和apply都是改變上下文中的this並立即執行這個函數,bind方法可以讓對應的函數想什么時候調就什么時候調用,並且可以將參數在執行的時候添加,這是它們的區別
一、this指向詳解
this的指向在函數定義的時候是確定不了的,只有函數執行的時候才能確定this到底指向誰,實際上this的最終指向的是那個調用它的對象(這句話不那么嚴謹,作為一般情況下是可以的)
再加下面的補充,就完美啦:
情況1:如果一個函數中有this,但是它沒有被上一級的對象所調用,那么this指向的就是window,這里需要說明的是在js的嚴格版中this指向的不是window,但是我們這里不探討嚴格版的問題(在嚴格版中的默認的this不再是window,而是undefined。)
function a(){
var user = "追夢子";
console.log(this.user); //undefined
console.log(this); //Window
}
a(); //相當於window.a();
//this最終指向的是調用它的對象,這里的函數a實際是被Window對象所點出來的
- 1
- 2
- 3
- 4
- 5
- 6
- 7
情況2:如果一個函數中有this,這個函數有被上一級的對象所調用,那么this指向的就是上一級的對象。
var o = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
console.log(this); //{user: "追夢子", fn: ƒ}
}
}
o.fn(); //this執行時被它的上一級對象o{user: "追夢子", fn: ƒ}調用
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
情況3:如果一個函數中有this,這個函數中包含多個對象,盡管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象
var o = {
a:10,
b:{
//a:12,
fn:function(){
console.log(this.a); //undefind 有兩個對象b和o,所以此this.a指向它的上一級
}
},
fn1:function(){
console.log(this.a); //10
}
}
o.fn1();
o.b.fn();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
另外還有兩種種特殊情況:
第一種:當this遇到return
function fn() {
this.user = '追夢子';
return {};
}
var a = new fn;
console.log(a.user); //undefined
- 1
- 2
- 3
- 4
- 5
- 6
- 7
function fn() {
this.user = '追夢子';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
- 1
- 2
- 3
- 4
- 5
- 6
- 7
function fn() {
this.user = '追夢子';
return 1;
}
var a = new fn;
console.log(a.user); //追夢子
- 1
- 2
- 3
- 4
- 5
- 6
- 7
function fn() {
this.user = '追夢子';
return undefined;
}
var a = new fn;
console.log(a.user); //追夢子
- 1
- 2
- 3
- 4
- 5
- 6
- 7
function fn() {
this.user = '追夢子';
return null;
}
var a = new fn;
console.log(a.user); //追夢子
- 1
- 2
- 3
- 4
- 5
- 6
- 7
總結:如果返回值是一個對象,那么this指向的就是那個返回的對象,如果返回值不是一個對象那么this還是指向函數的實例。
還有一點就是雖然null也是對象,但是在這里this還是指向那個函數的實例,因為null比較特殊。
第二種:
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
這里this指向的是window,是不是有些蒙了?其實是因為你沒有理解一句話,這句話同樣至關重要。
this永遠指向的是最后調用它的對象,也就是看它執行的時候是誰調用的,例子4中雖然函數fn是被對象b所引用,但是在將fn賦值給變量j的時候並沒有執行所以最終指向的是window
二、改變this指向的方法
第一種: new關鍵字改變this指向
//構造函數版this
function Fn(){
this.user = "追夢子";
}
var a = new Fn();
console.log(a.user); //追夢子
- 1
- 2
- 3
- 4
- 5
- 6
用變量a創建了一個Fn的實例(相當於復制了一份Fn到對象a里面),此時僅僅只是創建,並沒有執行,而調用這個函數Fn的是對象a,那么this指向的自然是對象a,那么為什么對象a中會有user,因為你已經復制了一份Fn函數到對象a中,用了new關鍵字就等同於復制了一份
第二種: call()
var a = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
}
}
var b = a.fn;
b.call(a); //若不用call,則b()執行后this指的是Window對象
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
把b添加到第一個參數的環境中,簡單來說,this就會指向那個對象。
call方法除了第一個參數以外還可以添加多個參數,如下:
var a = {
user:"追夢子",
fn:function(e,ee){
console.log(this.user); //追夢子
console.log(e+ee); //3
}
}
var b = a.fn;
b.call(a,1,2);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
第三種:apply()
var a = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
}
}
var b = a.fn;
b.apply(a);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
apply方法和call方法有些相似,它也可以改變this的指向,也可以有多個參數,但是不同的是,第二個參數必須是一個數組,如下:
var a = {
user:"追夢子",
fn:function(e,ee){
console.log(this.user); //追夢子
console.log(e+ee); //11
}
}
var b = a.fn;
b.apply(a,[10,1]);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
//注意如果call和apply的第一個參數寫的是null,那么this指向的是window對象
var a = {
user:"追夢子",
fn:function(){
console.log(this); //Window {external: Object, chrome: Object, document: document, a: Object, speechSynthesis: SpeechSynthesis…}
}
}
var b = a.fn;
b.apply(null);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
第四種:bind()
bind方法和call、apply方法有些不同,如下:
var a = {
user:"追夢子",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a); //代碼沒有被打印
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
我們發現代碼沒有被打印,對,這就是bind和call、apply方法的不同,實際上bind方法返回的是一個修改過后的函數。
var a = {
user:"追夢子",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
var c = b.bind(a);
console.log(c); //function() { [native code] }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
函數c看看,能不能打印出對象a里面的user
var a = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
}
}
var b = a.fn;
var c = b.bind(a);
c();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
同樣bind也可以有多個參數,並且參數可以執行的時候再次添加,但是要注意的是,參數是按照形參的順序進行的。
var a = {
user:"追夢子",
fn:function(e,d,f){
console.log(this.user); //追夢子
console.log(e,d,f); //10 1 2
}
}
var b = a.fn;
var c = b.bind(a,10);
c(1,2);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
總結: call和apply都是改變上下文中的this並立即執行這個函數,bind方法可以讓對應的函數想什么時候調就什么時候調用,並且可以將參數在執行的時候添加,這是它們的區別
