首頁 web前端 js教程 JavaScript中this綁定方式總結

JavaScript中this綁定方式總結

Nov 29, 2019 pm 03:42 PM
this

最近在回顧js的一些基礎知識,把《你不知道的js》系列又看了一遍,this始終是重中之重,還是決定把this相關知識做一個系統的總結,也方便自己日後回顧。

JavaScript中this綁定方式總結

this的四個綁定規則

#1.預設綁定

這是最常用的函數呼叫類型:獨立函數呼叫(即函數是直接使用不帶任何修飾的函數引用進行呼叫的)。可以把這條規則看成是無法套用其他規則時的預設規則。

預設綁定的this在非嚴格模式下指向window嚴格模式下指向undefined,例如下面的函數foo在非嚴格模式下:

var a = 2;
function foo(){
    var a = 3;
    console.log(this.a);
}
foo(); //2
登入後複製

【相關課程推薦:JavaScript影片教學】  

這裡的foo()方法內的this指向了window,因此window.a = 2;

嚴格模式下,this.指向undefined,因此存取this.a會報錯:

var a = 2;
function foo(){
    "use strict";
    var a = 3;
    console.log(this.a);
}
foo(); //Uncaught TypeError: Cannot read property 'a' of undefined
登入後複製

2.隱含綁定

如果呼叫位置上有上下文對象,或者說被某個對象“擁有”或“包含”,則使用隱式綁定。

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
obj.foo(); // 2
登入後複製

上例中的foo是透過obj.foo()的方式呼叫的,呼叫位置會使用obj上下文來引用函數,因此foo中的this指向了obj。

另外foo是當做引用被加入到obj中的,但是無論是直接在obj 中定義還是先定義再添加為引用屬性,foo嚴格上來說都不屬於obj,因此上述定義裡面的“擁有」與「包含」加上了引號,這樣說是為了方便理解。

常見的隱式呼叫場景:

obj.fn();
arguments[i]();//其实就是将点的调用方式变为了[]调用
el.onClick(function(){console.log(this);//this指向el})
登入後複製

隱含遺失

先來看一段程式碼:

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "global"; // a 是全局对象的属性
bar(); // "global"
登入後複製

上述程式碼其實只用看調用的方式:bar(),這其實是一個不帶任何修飾的函數調用,因此應用了預設綁定。

還有一種參數傳遞的方式也會發生隱含遺失,原理其實跟上述例子一樣:

function foo() {
    console.log( this.a );
}
function doFoo(fn) {
    // fn 其实引用的是foo
    fn(); // <-- 调用位置!
}
var obj = {
    a: 2,
    foo: foo
};
var a = "global"; // a 是全局对象的属性
doFoo( obj.foo ); // "global"
登入後複製

顯示綁定

使用call,apply和bind方法可以指定綁定函數的this的值,這種綁定方法叫做顯示綁定。

function foo() {
    console.log( this.a );
}
var obj = {
    a:2
};
foo.call( obj ); // 2
登入後複製

透過foo.call(obj),我們可以在呼叫foo 時強制把它的this 綁定到obj 上

new綁定

#new運算元可以基於一個「建構子」新建立一個物件實例,new的實例化過程如下:

● 建立(或說建構)一個全新的物件。

● 這個新物件會被執行[[ 原型]] 連結。

● 這個新物件會綁定到函數呼叫的this。

● 如果函數沒有回傳其他對象,那麼new 表達式中的函數呼叫會自動傳回這個新物件。

明確了new的實例化過程後,思考如下程式碼:

function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
登入後複製

new foo(2)後新建立了個實例物件bar,然後把這個新物件bar綁定到了foo函數中的this,因此執行this.a = a後其實是把a賦給了bar.a

#優先權

一般情況下this的綁定會根據上述四個綁定規則來,那麼他們同時出現時,該以怎樣的順序來判斷this的指向?下面是具體的規則:

函數是否在new 中呼叫(new 綁定)?如果是的話this 綁定的是新建立的物件( var bar = new foo() )。

函數是否透過call、apply(明確綁定)或硬綁定呼叫?如果是的話,this 綁定的是指定的物件( var bar = foo.call(obj2) )。

函數是否在某個上下文物件中呼叫(隱式綁定)?如果是的話,this 綁定的是那個上下文物件。 ( var bar = obj1.foo() )

如果都不是的話,使用預設綁定。如果在嚴格模式下,就綁定到undefined,否則綁定到全域物件。 ( var bar = foo() )

綁定例外

1.使用call,appy,bind這種明確綁定的方法,參數傳入null或者undefined作為上下文時,函數呼叫還是會使用預設綁定

function foo() {
    console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
登入後複製

什麼情況下需要將上下文傳為null呢?

1.使用bind函數來實現柯里化

function foo(a,b) {
    console.log(a,b);
}
// 使用 bind(..) 进行柯里化
var bar = foo.bind( null, 2 );
bar( 3 ); // 2,3
登入後複製

2.使用apply(..) 來展開一個數組,並當作參數傳入一個函數

function foo(a,b) {
    console.log(a,b);
}
// 把数组展开成参数
foo.apply( null, [2, 3] ); // 2,3
登入後複製

其實上面兩種使用場景其實都不關心call/app/bind第一個參數的值是什麼,只是想傳個佔位值而已。

但總是傳入null可能會出現一些難以追蹤的bug,比如說當你在使用的第三方函式庫中的某個函數中有this時,this會被錯誤的綁定到全域在物件上,造成一些難以預料的後果(修改全域變數)

var a = 1;//全局变量
const Utils = {
    a: 2,
    changeA: function(a){
        this.a = a;
    }
}
Utils.changeA(3);
Utils.a //3
a //1
Utils.changeA.call(null,4);
Utils.a //3
a //4,修改了全局变量a!
登入後複製

更安全的做法:

var o = Object.create(null);
Utils.changeA.call(o,6);
a //1, 全局变量没有修改
o.a // 6 改的是变量o
登入後複製

2.間接引用

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() 而不是p.foo() 或者o.foo()。根据我们之前说过的,这里会应用默认绑定。

this词法(箭头函数)

上述的几种规则适用于所有的正常函数,但不包括ES6的箭头函数。箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域(词法作用域)来决定this

function foo() {
// 返回一个箭头函数
    return (a) => {
        //this 继承自foo()
        console.log( this.a );
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是3 !
登入後複製

foo() 内部创建的箭头函数会捕获调用时foo() 的this。由于foo() 的this 绑定到obj1,bar(引用箭头函数)的this 也会绑定到obj1,箭头函数的绑定无法被修改。(new 也不行!)

几个例子加深理解

this的理论知识讲解得差不多了,来几个例子看看自己有没有理解全面:

1.经典面试题:以下输出结果是什么

var length = 10;
function fn() {
    console.log(this.length);
}
var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};
obj.method(fn, 1);
登入後複製

obj中method方法里面调用了两次fn。第一次是直接调用的“裸露”的fn,因此fn()中this使用默认绑定,this.length为10.第二次调用时通过arguments0的方式调用的,arguments[0]其实指向的就是fn,但是是通过obj[fn]这种对象上下文的隐式绑定的,因此this指向arguments,而arguments只有一个一项(method中只有fn一个参数),因此arguments.length为1。因此打印的结果为:

10
1
登入後複製

2.以下输出什么

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = function () {
            return new Date().getFullYear() - this.birth; // this指向window或undefined
        };
        return fn();
    }
};
obj.getAge();
登入後複製

答案是严格模式下会报错,非严格模式下输出NaN

原因也是因为在调用obj.getAge()后,getAge方法内的this使用隐式绑定。但是return fn()的时候用的是“裸露的fn”使用默认绑定,fn里面的this指向window或者undefined。

使用箭头函数来修正this的指向:

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
        return fn();
    }
};
obj.getAge(); // 25
登入後複製

使用箭头函数后,fn中的this在他的词法分析阶段就已经确定好了(即fn定义的时候),跟调用位置无关。fn的this指向外层的作用域(即getAge中的this)

3.以下输出为什么是'luo'

var A = function( name ){ 
    this.name = name;
};
var B = function(){ 
    A.apply(this,arguments);
};
B.prototype.getName = function(){ 
    return this.name;
};
var b=new B(&#39;sven&#39;);  // B {name: "luo"}
console.log( b.getName() ); // 输出:  &#39;luo&#39;
登入後複製

执行new B('seven')后会返回一个新对象b,并且B函数中的this会绑定到新对象b上,B的函数体内执行A.apply(this.arguments)也就是执行b.name = name;这个时候b的值就是{name:'luo'},所以b.getName()就能输出'luo'啦~

实际在业务使用中,逻辑会更复杂一些,但是万变不离其宗,都按照上面写的规则来代入就好了

本文来自 js教程 栏目,欢迎学习!  

以上是JavaScript中this綁定方式總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1662
14
CakePHP 教程
1418
52
Laravel 教程
1311
25
PHP教程
1261
29
C# 教程
1234
24
聊聊Vue2為什麼能透過this存取各種選項中屬性 聊聊Vue2為什麼能透過this存取各種選項中屬性 Dec 08, 2022 pm 08:22 PM

這篇文章帶大家解讀vue原始碼,來介紹一下Vue2中為什麼可以使用 this 存取各種選項中的屬性,希望對大家有幫助!

一篇搞懂this指向,追趕70%的前端人 一篇搞懂this指向,追趕70%的前端人 Sep 06, 2022 pm 05:03 PM

同事因為this指向的問題卡住的bug,vue2的this指向問題,使用了箭頭函數,導致拿不到對應的props。當我跟他介紹的時候他竟然不知道,隨後也刻意的看了一下前端交流群,至今最起碼還有70%以上的前端程式設計師搞不明白,今天給大家分享一下this指向,如果啥都沒學會,請給我一個大嘴巴子。

使用this關鍵字的巧妙方式在jQuery中 使用this關鍵字的巧妙方式在jQuery中 Feb 25, 2024 pm 04:09 PM

jQuery中this關鍵字的靈活運用在jQuery中,this關鍵字是一個非常重要且靈活的概念,它用來引用目前正在操作的DOM元素。透過合理的運用this關鍵字,我們可以方便地操作頁面中的元素,實現各種互動效果和功能。本文將結合具體的程式碼範例,介紹this關鍵字在jQuery中的靈活運用。簡單的this範例首先,我們來看一個簡單的this範例。假設我們有一

什麼是this?深入解析JavaScript中的this 什麼是this?深入解析JavaScript中的this Aug 04, 2022 pm 05:02 PM

什麼是this?以下這篇文章跟大家介紹一下JavaScript中的this,並聊聊this在函數不同呼叫方式下的區別,希望對大家有所幫助!

JavaScript如何改變this指向?三種方法淺析 JavaScript如何改變this指向?三種方法淺析 Sep 19, 2022 am 09:57 AM

JavaScript如何改變this指向?以下這篇文章跟大家介紹一下JS改變this指向的三種方法,希望對大家有幫助!

Java中this方法怎麼使用 Java中this方法怎麼使用 Apr 18, 2023 pm 01:58 PM

一、this關鍵字1.this的類型:哪個物件呼叫就是哪個物件的參考類型二、用法總結1.this.data;//存取屬性2.this.func();//存取方法3.this( );//呼叫本類別中其他建構方法三、解釋用法1.this.data這種是在成員方法中使用讓我們來看看不加this會出現什麼樣的狀況classMyDate{publicintyear;publicintmonth;publicintday; publicvoidsetDate(intyear,intmonth,intday){ye

JavaScript箭頭函數中的this詳解 JavaScript箭頭函數中的this詳解 Jan 25, 2024 pm 01:41 PM

JavaScript中箭頭函數是一種比較新的語法,沒有自己的this關鍵字,相反箭頭函數的this指向包含它的作用域對象,影響方面有:1、箭頭函數中的this是靜態的;2、箭頭函數不能作為構造函數使用;3、箭頭函數不能用作方法。

帶你去詳解 this 的四個綁定規則 帶你去詳解 this 的四個綁定規則 Nov 01, 2022 pm 05:49 PM

this 關鍵字是 JavaScript 中最複雜的機制之一。它是一個很特別的關鍵字,被自動定義在所有函數的作用域中。但即使是非常有經驗的 JavaScript 開發者也很難說清它到底指向什麼。

See all articles