JavaScript高級程序設計——this

js-this

前言

原本this應該放在《上下文環境和做用域》中來說,結果發現本身整理的時候,例子愈來愈多,因此單獨寫一篇用於加深理解,之後工做或項目中發現一些this相關問題能夠進一步完善文章的例子。算法

this

概念

在函數中this到底取何值,是在函數真正被調用執行的時候肯定的,函數定義的時候肯定不了數組

由於this的取值是執行上下文環境的一部分,每次調用函數,都會產生一個新的執行上下文環境。app

構造函數

所謂構造函數就是用來new對象的函數。其實嚴格來講,全部的函數均可以new一個對象,可是有些函數的定義是爲了new一個對象,而有些函數則不是。另外注意,構造函數的函數名第一個字母大寫(規則約定)。例如:Object、Array、Function等。函數

function Foo() {
            this.name = "yzh";
            this.year = 1996;
            console.log(this); //Object { name: "yzh", year: 1998 }
        }
        var f1 = new Foo();
        console.log(f1.name); //yzh
        console.log(f1.year); //1996

以上代碼中,若是函數做爲構造函數用,那麼其中的this就表明它即將new出來的對象。this

注意,以上僅限newFoo()的狀況,即Foo函數做爲構造函數的狀況。若是直接調用Foo函數,而不是new Foo(),狀況就大不同了。spa

function Foo() {
            this.name = "yzh";
            this.year = 1996;
            console.log(this); //Object { name: "yzh", year: 1998 }
        }
        Foo();  //window

這種狀況下this是window。prototype

函數做爲對象的一個屬性

若是函數做爲對象的一個屬性時,而且做爲對象的一個屬性被調用時,函數中的this指向該對象。指針

var obj = {
            x: 10,
            fn: function() {
                console.log(this); //Object { x: 10, fn: fn() }
                console.log(this.x); //10
            }
        };
        obj.fn();

以上代碼中,fn不只做爲一個對象的一個屬性,並且的確是做爲對象的一個屬性被調用。結果this就是obj對象。code

若是fn函數不做爲obj的一個屬性被調用,會是什麼結果呢?對象

var obj = {
            x: 10,
            fn: function() {
                console.log(this); //window
                console.log(this.x); //undefined
            }
        };
        var f1 = obj.fn;
        f1();

如上代碼,若是fn函數被賦值到了另外一個變量中,並無做爲obj的一個屬性被調用,那麼this的值就是window,this.x爲undefined。

函數用call或者apply調用

爲了防止看不懂這塊先理解下基礎並重要的東西

函數內部屬性

在函數內部,有兩個特殊的對象:arguments和this.
主要介紹下arguments,它是類數組對象,包含傳入函數的全部參數,這個對象還有一個叫callee的屬性,該屬性爲一個指針,指向擁有這個arguments對象的函數。

這個例子主要用於消除函數的執行與同名函數的緊密耦合現象。代碼以下:

function factorial(num) {
            if (num <= 1) {
                return 1;
            } else {
               // return num * factorial(num - 1);
                //1.通常的遞歸算法運用
                return num * arguments.callee(num - 1) 
                //2.更好的作法
            }
        }

        var trueFactorial = factorial;

        factorial = function() {
            return 0;
        };

        alert(trueFactorial(5)); //120
        alert(factorial(5)); //0

函數屬性

每一個函數都包含兩個屬性:length和prototype.
length表示函數但願接受的命名參數的個數

function sayName(name){
            alert(name);
        }      
        
        function sum(num1, num2){
            return num1 + num2;
        }
        
        function sayHi(){
            alert("hi");
        }
        
        alert(sayName.length);  //1
        alert(sum.length);      //2
        alert(sayHi.length);    //0

prototype屬性這裏再也不重複介紹。

函數方法

每一個函數都包含兩個非繼承而來的方法:apply()和call(),這兩個方法的用途都是在特定的做用域中調用函數,實際上等於設置函數體內的this對象值

apply()方法接受兩個參數:一個是在其中運行函數的做用域,另外一個是參數數組。

function sum(num1, num2){
            return num1 + num2;
        }
        
        function callSum1(num1, num2){
            return sum.apply(this, arguments);
        }
        
        function callSum2(num1, num2){
            return sum.apply(this, [num1, num2]);
        }
        
        alert(callSum1(10,10));   //20
        alert(callSum2(10,10));   //20

callsSum1()執行sum()函數時傳入this做爲this值(在全局做用域中調用,因此傳入的對象爲window對象)下面的例子同理。

call()方法與apply()方法差很少,區別在於接受參數的方式不一樣。簡單來講就是:傳遞給函數的參數必須逐個列舉出來。(若是是無參函數就不用傳參數)

function sum(num1, num2){
            return num1 + num2;
        }
        
        function callSum(num1, num2){
            return sum.call(this, num1, num2);
        }
        
        alert(callSum(10,10));   //20

傳遞參數並不是兩個函數的真正用武之地,真正強大的地方是擴充函數賴以運行的做用域

window.color = "red";
        var o = { color: "blue" };
        
        function sayColor(){
            alert(this.color);
        }
        
        sayColor();            //red
        
        sayColor.call(this);   //red
        sayColor.call(window); //red
        sayColor.call(o);      //blue

以下代碼若是不用函數的方法來實現。

window.color = "red";
        var o = { color: "blue" };
        
        function sayColor(){
            alert(this.color);
        }
        
        sayColor();     //red
        
        o.sayColor = sayColor;
        o.sayColor();   //blue

若是要輸出o對象裏的color屬性值,必須把sayColor函數賦給對象o並調用 o.sayColor()時,this引用的對象爲o

迴歸要講的例子

當一個函數被call和apply調用時,this的值就取傳入的對象的值。

var obj = {
            x: 10
        };
        var fn = function() {
            console.log(this); //Object { x: 10 }
            console.log(this.x); //10
        };
        fn.call(obj);

全局 & 調用普通函數

在全局環境下,this永遠是window,這個應該沒有非議。

console.log(this === window); //true

普通函數在調用時,其中的this也都是window。

var x = 10;
        var fn = function() {
            console.log(this); //window
            console.log(this.x); //10
        }
        fn();

注意如下的狀況

var obj = {
            x: 10,
            fn: function() {
                function f() {
                    console.log(this); //Window 
                    console.log(this.x); //undefined
                }
                f();
            }
        };
        obj.fn();

函數f雖然是在obj.fn內部定義的,可是它仍然是一個普通的函數,this仍然指向window。

總結:this指向調用該函數的對象

構造函數的prototype中,this表明着什麼

function Fn() {
            this.name = "yzh";
            this.year = 1996;
        }
        Fn.prototype.getName = function() {
            console.log(this.name);
        }
        var f1 = new Fn();
        f1.getName(); //yzh

在Fn.prototype.getName函數中,this指向的是f1對象。所以能夠經過this.name獲取f1.name的值

bind()

bind()方法建立一個新的函數, 當被調用時,它的this關鍵字被設置爲提供的值 ,在調用新函數時,提供任何一個給定的參數序列。

這個函數其實也很重要,只是當時紅寶書也沒說起不少。

語法

fun.bind(thisArg[, arg1[, arg2[, ...]]])

參數

thisArg

當綁定函數被調用時,該參數會做爲原函數運行時的 this 指向。當使用new 操做符調用綁定函數時,該參數無效。

arg1, arg2, ...

當綁定函數被調用時,這些參數將置於實參以前傳遞給被綁定的方法。

返回值

返回由指定的this值和初始化參數改造的原函數拷貝

描述

bind() 函數會建立一個新函數(稱爲綁定函數),新函數與被調函數(綁定函數的目標函數)具備相同的函數體(在 ECMAScript 5 規範中內置的call屬性)。當目標函數被調用時 this 值綁定到 bind() 的第一個參數,該參數不能被重寫。綁定函數被調用時,bind() 也接受預設的參數提供給原函數。一個綁定函數也能使用new操做符建立對象:這種行爲就像把原函數當成構造器。提供的 this 值被忽略,同時調用時的參數被提供給模擬函數。

示例

建立綁定函數

this.x = 9; 
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 返回 81

var retrieveX = module.getX;
retrieveX(); // 返回 9, 在這種狀況下,"this"指向全局做用域

// 建立一個新函數,將"this"綁定到module對象
// 新手可能會被全局的x變量和module裏的屬性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

End

歡迎拍磚,有更多的好的例子,歡迎提出,文中有誤的地方也歡迎指正。

相關文章
相關標籤/搜索