首頁 >web前端 >js教程 >Javascript的this的用法詳解

Javascript的this的用法詳解

不言
不言轉載
2019-03-23 09:36:422445瀏覽

這篇文章帶給大家的內容是關於Javascript的this的用法詳解,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

在理解javascript的this之前,先了解作用域。

作用域分為兩種:

1、詞法作用域:引擎在目前作用域或嵌套的子作用域尋找具有名稱識別碼的變數。 (引擎如何查找和在哪裡查找。定義過程發生在程式碼書寫階段)
2、動態作用域:在運行時被動態決定的作用域。

詞法作用域和動態作用域的區別是:詞法作用域是在寫入程式碼或定義時決定的;動態作用域是在執行時決定的。

this的綁定規則

this是在呼叫時被綁定,取決於函數的呼叫位置。由此可以知道,一般情況下(非嚴格模式下),this都會根據函數呼叫(呼叫棧)的上下文來綁定物件。

一、預設綁定

預設綁定:預設綁定是指在非嚴格模式下,且沒有使用別的綁定規則時,this根據函數呼叫(呼叫堆疊)的上下文來綁定物件(全域物件)。 (嚴格模式下則綁定undefined)

舉個栗子:

function foo() {
    console.log(this.a);
};
function bar() {
    var a = 3;
    foo();
}
var a = 2;
bar();  //调用栈在全局作用域,this绑定全局对象

运行结果为: 2
//加上"use strict"运行结果则会变成this is undefined

這裡的函數呼叫時,使用了預設綁定,函數呼叫(呼叫堆疊)的上下文是全域作用域,因此this綁定了全局物件(global)。

eg2:
function foo() {
    console.log(this.a)
};
var a = 2;
(function() {
    "use strict"
    foo();
})();

运行结果为: 2

這裡要注意:對於預設綁定,決定this綁定物件的不是呼叫位置是否處於嚴格模式,而是函數體是否處於嚴格模式(函數體處於嚴格模式則this綁定undefined;否則this綁定全域物件)。另外:嚴格模式和非嚴格模式雖然有可能可以綁定,但是最好不混用。

間接引用一般也是會套用預設綁定規則。

eg:
function foo() {
    console.log(this.a);
};
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo();   //3
(p.foo = o.foo)();  //2

賦值表達式 p.foo = o.foo的回傳值是直接引用目標函數foo。

二、隱式綁定

隱式綁定:由上下文物件調用,綁定到上下文物件。

舉個栗子:

function foo() {
    console.log(this.a);
};
var obj = {
    a: 2,
    foo: foo
};
obj.foo();    //2
foo();        //undefined

這段程式碼中,foo()被當做參考屬性加入到obj物件中,obj呼叫這個引用屬性函數時,會使用該引用屬性上下文, this會被綁定到obj物件。 (這個函數嚴格來說不屬於obj對象,只是作為引用屬性)。屬於隱式綁定。

而下面foo()函數的直接執行,並不是obj物件引用,所以上下文物件是全域物件。故this綁定了undefined。屬於預設綁定。

物件參考鏈中只有上一層或說最後一層在呼叫位置中起作用。

注意:

隱式綁定的函數會遺失綁定物件。 此時它會套用預設綁定,將this綁定到全域物件或undefined上,取決於是否是嚴格模式。
  eg:

function foo() {
    console.log(this.a);
};
var obj = {
    a: 2;
    foo: foo
}
var bar = obj.foo;
var a = 'biubiubiu';
bar();

运行结果:"biubiubiu"

解析:看似bar是obj.foo的一個引用,實際上bar是直接引用了函數foo,是一個單純的函數調用,故實為預設綁定。

參數傳遞就是隱式賦值,因此傳入函數時也會被隱式賦值。
  eg:

function foo() {
    console.log(this.a);
};
var obj = {
    a: 2,
    foo: foo
};
function bar(fn) {
    fn();
};
var a = "biubiubiu";
bar(obj.foo);

运行结果: "biubiubiu"

解析:實際上參數也是隱含賦值,但參數傳入函數中,並在函數中執行。此時也是直接引用了函數foo,因此也是單純的函數調用,採用了預設綁定。

把函數傳入語言內建函數。 (與上面情況基本相似,將自己宣告函數改成語言內建函數)回呼函數遺失this的情況比較常見,況且還有呼叫回呼函數的函數可能還會修改this。

三、顯式綁定

明確綁定:直接將this綁定到指定物件上。 Javascript中絕大多數函數和自己所建立的函數都可以使用這兩種明確綁定的方法。

1、.call()
2、.apply()
這兩種綁定方法,第一個參數是this綁定的物件。 (如果傳入的參數是原始值(字串型別、布林型別、數字型別),這個原始值就會被轉換成物件形式(new String、new Boolean、new Number)這個稱為:裝箱)

舉個栗子:

function foo() {
    console.log(this.a);
};
var obj = {
    a: 2
};
foo.call(obj);

运行结果: 2

然鵝,顯示綁定並不能解決綁定遺失的問題。這時候來了一位新朋友 -- 硬綁定(bind)。

3、.bind()    (硬綁定是常見場景,故es5提供了該內建方法Function.prototype.bind.)
#bind()會傳回一個新編碼函數,把this綁定在指定參數上,並且呼叫函數。

舉個栗子:

function foo(e) {
    console.log(this.a + e);
    return this.a + e;
};
var obj = {
    a: 2
}
var bar = foo.bind(obj); //新编码函数
var b = bar(3); // 2 3
console.log(b); // 5

bind()還有一個功能將除了第一個用於綁定this的參數以外的其他參數傳給下層的函數部分應用,是「柯里化」的一種)。

这里涉及到一个概念:把null或者undefined作为this的绑定对象传入call、apply、bind,这些值在调用的时候会被忽略,实际应用默认绑定规则。
应用场景:
1、使用apply()展开一个数组,并作为参数传递给一个函数。
2、bind()对参数进行柯里化(预先设置一些参数)。

举个栗子:

function foo(a,b) {
    console.log("a:" + a + ",b:" + b);
};
//数组“展开”成参数
foo.apply(null,[2,3]);  //a:2,b:3
//bind()柯里化
var bar = foo.bind(null,2);
bar(3);  //a:2,b:3

解析:传入一个参数作为this绑定对象,如果不传则使用占位符(null),此时会使用默认绑定规则。

上面这个例子可能会产生一定的副作用,如果需要运用这种场景并且更加安全。可以创建一个空对象(可以用任意喜欢的名字来命名)。

var ∅ = Object.create(null);
//上面这个例子就可以改写为:
foo.apply(∅,[2,3]); //a:2,b:3
var bar = foo.bind(∅,2);
bar(3);  //a:2,b:3

注意:硬绑定之后不能使用隐式绑定和显式绑定对this进行修改
在这里介绍一种软绑定的方法softBind()检查this绑定到全局对象或者undefined后,绑定this到指定的默认对象。绑定后效果和硬绑定一样,但是保留隐式绑定或者显式绑定修改this的能力。

四、new绑定

Javascript中的new机制与面向类语言的完全不同。在Javascript中,构造函数只是一些使用new操作符时被调用的函数,不属于一个类,也不会实例化一个类。称为对函数的“构造调用”。

举个栗子:

function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); //2

使用new的过程会创建一个全新的对象,this会绑定这个新对象。如果函数没有返回其他对象,则new表达式函数调用会返回该新对象。(这个新对象会连接prototype)

四种绑定规则的优先级为:new>显式>隐式>默认

箭头函数

箭头函数是根据外层作用域(函数或全局)来决定this。(词法作用域取代this机制)
箭头函数this会绑定调用时的对象,且箭头函数的绑定无法修改(new也不行)。

其实可以理解为,箭头函数的this在词法上继承的是它所在的作用域(函数或全局)的this,而它继承的函数作用域的this绑定的是在该函数调用上下文对象,所以箭头函数的this间接的绑定在调用上下文对象。

简述: 箭头函数this(绑定作用域this)-- 作用域this(绑定在调用上下文对象)。

故:箭头函数this == 调用的上下文对象

举个栗子:

function foo() {
    setTimeout(function() {
        //这里的this在词法上继承自foo()
        console.log(this.a);
    },100);
};
var obj = { a: 2 };
foo.call(obj);  //2

其实这个栗子也等价于:

function foo() {
    var that = this;  //lexical capture of this
    setTimeout(function() {
        console.log(self.a)
    },100);
}
...与上面一样

所以,有两种风格:this风格(四种规则)词法作用域风格(that = this和箭头函数)可供使用。使用时尽量避免混用,否则会造成难以维护的后果。

本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的JavaScript视频教程栏目!

以上是Javascript的this的用法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除