1. 程式人生 > >js this 詳解

js this 詳解

this詳解

 你可能遇到過這樣的 JS 面試題:

{

var obj = {

foo: function () {

console.log(this)

}

}

var bar = obj.foo

obj.foo(); // 打印出的 this 是 obj

bar(); // 打印出的 this 是 window

}

 這個原因是因為“Javascript中this永遠是指向呼叫它的物件”

 函式呼叫

 函式的三種呼叫方式

func(p1, p2)

obj.child.method(p1, p2)

func.call(context, p1, p2) // 先不講 apply

 前面兩種是常見的呼叫方式,但是第三種呼叫形式,才是正常呼叫形式:

{

func(p1, p2)

// 等價於

func.call(undefined, p1, p2)



obj.child.method(p1, p2)

// 等價於

obj.child.method.call(obj.child, p1, p2)

}

其他兩種都是語法糖,可以等價地變為 call 形式:

 至此我們的函式呼叫只有一種形式:

func.call(context, p1, p2)

這樣,this 就好解釋了

 this,就是上面程式碼中的 context。就這麼簡單。

 this 是你 call 一個函式時傳的 context,由於你從來不用 call 形式的函式呼叫,所以你一直不知道。

先看 func(p1, p2) 中的 this 如何確定:

 當你寫下面程式碼時

{

function func() {

console.log(this)

}

func();

 等價於

func.call(undefined) // 可以簡寫為 func.call()

}

 按理說打印出來的 this 應該就是 undefined 了吧,但是瀏覽器裡有一條規則:

 如果你傳的 context 就 null 或者 undefined,那麼 window 物件就是預設的 context(嚴格模式下預設 context 是 undefined)

 因此上面的列印結果是 window。

 如果你希望這裡的 this 不是 window,很簡單:

{

func.call(obj) // 那麼裡面的 this 就是 obj 物件了

}

 再看 obj.child.method(p1, p2) 的 this 如何確定

{

var obj = {

foo: function () {

console.log(this)

}

}

obj.foo()

}

 按照「轉換程式碼」,我們將 obj.foo() 轉換為

{

obj.foo.call(obj)

}

好了,this 就是 obj。搞定。

 回到題目:

{

var obj = {

foo: function () {

console.log(this)

}

}

var bar = obj.foo

obj.foo() // 轉換為 obj.foo.call(obj),this 就是 obj

bar()

}

/轉換為 bar.call(); 由於沒有傳 context;所以 this 就是 undefined;最後瀏覽器給你一個預設的 this —— window 物件

語法

{

function fn() { console.log(this) }

var arr = [fn, fn2]

arr[0]() // 這裡面的 this 又是什麼呢?

}

我們可以把 arr[0]() 想象為arr.0() ,雖然後者的語法錯了,但是形式與轉換程式碼裡的 obj.child.method(p1, p2) 對應上了,於是就可以愉快的轉換了: arr[0](),假想為arr.0(),然後轉換為 arr.0.call(arr),那麼裡面的 this 就是 arr 了:)

總結:

 this 就是你 call 一個函式時,傳入的 context。

 如果你的函式呼叫形式不是 call 形式,請按照「轉換程式碼」將其轉換為 call 形式。

 

1 this的四種用法

在Javascript中this總是指向呼叫它所在方法的物件。因為this是在函式執行時,自動生成的一個內部物件,只能在函式內部使用。

 1.1在一般函式方法中使用 this 指代全域性物件

{

function test() {

this.x = 1;

console.log(this.x);

console.log(this);

};

test();

console.log(window.x)

}

 1.2作為物件方法呼叫,this 指代上級物件

{

function test() {

console.log(this.x);

console.log(this)

}

var o = {};

o.x = 1;

o.m = test;

o.m(); // 1

}

1.3作為建構函式呼叫,this 指代new 出的物件

{

function test() {

this.x = 1;

console.log(this);

}

var o = new test();

console.log(o.x); // 1

執行結果為1。為了表明這時this不是全域性物件,我對程式碼做一些改變:

var x = 2;

function test2() {

this.x = 1;

console.log(this);

}

var o = new test2();

console.log(x); //2

}

 1.4 apply 呼叫 ,apply方法作用是改變函式的呼叫物件,此方法的第一個引數為改變後呼叫這個函式的物件,this指代第一個引數

{

var x = 0;

function test() {

console.log(this.x);

}

var o = {};

o.x = 1;

o.m = test;

o.m.apply(); //0

//apply()的引數為空時,預設呼叫全域性物件。因此,這時的執行結果為0,證明this指的是全域性物件。如果把最後一行程式碼修改為

o.m.apply(o); //1

}
{

function fn(num) {

console.log("fn: " + num);

// count用於記錄fn的被呼叫次數

this.count++;

console.log(this)

}

fn.count = 0;

var i;

for (i = 0; i < 10; i++) {

if (i > 5) {

fn(i);

}

}

console.log(fn.count); //0

console.log(this.count); //NaN

}

 2.箭頭函式的簡單講解

 資料上說的箭頭函式內部是沒有this的,也就是說,箭頭函式裡面的this會繼承自外部的this,下面有個例子,用來詳細說明下:

{

var x = 11;

var obj = {

x: 22,

y: this,//window

say: () => {

console.log(this.x);

}

}

obj.say();

//輸出的值為11

console.log(obj.y);

//輸出的值為window物件

}

obj物件中的this指代的就是window,也就是全域性環境,因為箭頭函式中的this就會就近找到上一個物件中this所指代的物件,

 從以上例子可以看出來,obj內部屬性y就為obj內部this指代的物件,輸出是window。

 看看下面這個例子

{

var a = 11

function test1() {

this.a = 22;

let b = function () {

console.log(this.a);

};

b();

}

var x = new test1();

//輸出11

}