this學習筆記

this 的值到底是什麼?一次說清楚
你怎麼還沒搞懂 this?
this、apply、call、bind

一、What's this?

由於運行期綁定的特性,JavaScript 中的 this 含義非常多,它可以是全局對象、當前對象或者任意對象,這完全取決於函數的調用方式

隨着函數使用場合的不同,this的值會發生變化。但是有一個總的原則,那就是this指的是,調用函數的那個對象

1、作爲函數調用

在函數被直接調用時this綁定到全局對象。在瀏覽器中,window 就是該全局對象


console.log(this);    //window

function fn1(){
    console.log(this);
}
fn1();  //window

2、內部函數

函數嵌套產生的內部函數的this不是其父函數,仍然是全局變量

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

fn0();

//window

3、setTimeout、setInterval

這兩個方法執行的函數this也是全局對象

document.addEventListener('click', function(e){

console.log(this);    //document
setTimeout(function(){
    console.log(this);    //window
}, 200);

}, false);

4、作爲構造函數調用

所謂構造函數,就是通過這個函數生成一個新對象(object)。這時,this就指這個新對象

new 運算符接受一個函數 F 及其參數:new F(arguments...)。這一過程分爲三步:

創建類的實例。這步是把一個空的對象的 proto 屬性設置爲 F.prototype 。
初始化實例。函數 F 被傳入參數並調用,關鍵字 this 被設定爲該實例。
返回實例。
看例子

function Person(name){
    this.name = name;
}
Person.prototype.printName = function(){
    console.log(this.name);
};

var p1 = new Person('Byron');
var p2 = new Person('Casper');
var p3 = new Person('Vincent');

p1.printName();  //Byron
p2.printName(); //Casper
p3.printName();  //Vincent

5、作爲對象方法調用

在 JavaScript 中,函數也是對象,因此函數可以作爲一個對象的屬性,此時該函數被稱爲該對象的方法,在使用這種調用方式時,this 被自然綁定到該對象

var obj1 = {
    name: 'Byron',
    fn : function(){
        console.log(this); //obj1
    }
};

obj1.fn();  

小陷阱

var fn2 = obj1.fn;

fn2();  //window

因爲fn2等於ƒ (){console.log(this)},所以this指向的是全局變量

6、DOM對象綁定事件

在事件處理程序中this代表事件源DOM對象(低版本IE有bug,指向了window)

document.addEventListener('click', function(e){
    console.log(this);  //document
    var _document = this;  
    setTimeout(function(){
        console.log(this); //window
        console.log(_document);  //document
    }, 200);
}, false);

解決方案

clipboard.png
輸出結果//document//document//document

7、Function.prototype.bind

bind,返回一個新函數,並且使函數內部的this爲傳入的第一個參數
bind的用法是給 函數名.bind(參數)返回的是一個函數,加()才立即執行

var obj1 = {
    name: 'Byron',
    fn : function(){
        console.log(this);
    }
};
var fn3 = obj1.fn.bind()() //window
var fn4 = obj1.fn.bind(obj1)()   //obj1

例二
首先bind的this.sayhello沒有指向page,而是指向的this.node
clipboard.png
解決方案一:
clipboard.png
解決方案二,,this.sayhello,但是這個方案有問題,在執行sayhello函數時,函數內部的this是指向this.node(document.body),而不是指向page的
clipboard.png
解決方案三:

解決方案二的問題,我們用bind函數傳遞了this參數,sayhello函數內部this就還是指向page的了
clipboard.png

8、使用call和apply設置this

call apply,調用一個函數,傳入函數執行上下文及參數

fn.call(context, param1, param2...)

fn.apply(context, paramArray)

語法很簡單,第一個參數都是希望設置的this對象,不同之處在於call方法接收參數列表,而apply接收參數數組


fn2.call(obj1);
fn2.apply(obj1);

難得舉例了,直接看這篇文章寫的更好this、apply、call、bind

二、函數的執行環境和變量

1、函數的執行環境

一個函數被執行時,會創建一個執行環境(ExecutionContext),函數的所有的行爲均發生在此執行環境中,構建該執行環境時,JavaScript 首先會創建 arguments變量,其中包含調用函數時傳入的參數

接下來創建作用域鏈,然後初始化變量。首先初始化函數的形參表,值爲 arguments變量中對應的值,如果 arguments變量中沒有對應值,則該形參初始化爲 undefined。

如果該函數中含有內部函數,則初始化這些內部函數。如果沒有,繼續初始化該函數內定義的局部變量,需要注意的是此時這些變量初始化爲 undefined,其賦值操作在執行環境(ExecutionContext)創建成功後,函數執行時纔會執行,這點對於我們理解JavaScript中的變量作用域非常重要,最後爲this變量賦值,會根據函數調用方式的不同,賦給this全局對象,當前對象等

至此函數的執行環境(ExecutionContext)創建成功,函數開始逐行執行,所需變量均從之前構建好的執行環境(ExecutionContext)中讀取

2、三種變量(名稱有點不規範,暫時可以不管名稱)

實例變量:(this)類的實例才能訪問到的變量

靜態變量:(屬性)直接類型對象能訪問到的變量

私有變量:(局部變量)當前作用域內有效的變量

看個例子

function ClassA(){

var a = 1; //私有變量,只有函數內部可以訪問.外部無法訪問,外部要訪問的話需要返回
this.b = 2; //實例變量,只有實例可以訪問

}

ClassA.c = 3; // 靜態變量,也就是給函數對象增加了屬性

console.log(a); // error無法訪問局部變量
console.log(ClassA.b) // undefined this.b並不是給函數賦值的屬性,而是
console.log(ClassA.c) //3

var classa = new ClassA();
console.log(classa.a);//undefined 無法訪問局部變量
console.log(classa.b);// 2
console.log(classa.c);//undefined ClassA.c只是函數上的c屬性和classa這個對象沒有關係

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章