首頁 web前端 js教程 Javascript中神奇的this_javascript技巧

Javascript中神奇的this_javascript技巧

May 16, 2016 pm 03:18 PM
javascript this

Javascript 當中的 this 與其他語言是完全不同的機制,很有可能會讓一些寫其他語言的工程師迷惑。

1. 誤以為 this 指向函數自身

根據 this 的英文文法,很容易將函數中出現的 this 理解為函數本身。在 javascript 當中函數作為一等公民,確實可以在呼叫的時候將屬性值儲存起來。但是如果使用方法不對,就會發生與實際預期不一致的情況。具體情況,請看下面程式碼

  function fn(num){
    this.count++;
  }
  
  fn.count = 0;
  
  for(var i=0;i<3;i++){
    fn(i);
  }
  console.log(fn.count); // 0
登入後複製

如果 fn 函數裡面的 this 指向自身函數,那麼 count 屬性的屬性值就應該產生變化,但實際上卻是紋絲不動。對於這個問題,有些人會利用作用域來解決,例如這麼寫

  var data = {
    count:0
  };
  
  function fn(num){
    data.count++;
  }
  
  for(var i=0;i<3;i++){
    fn(i);
  }
  
  console.log(data.count);  //3
登入後複製

又或者更直接的這樣寫

  function fn(num){
    fn.count++;
  }
  
  fn.count = 0;
  
  for(var i=0;i<3;i++){
    fn(i);
  }
  
  console.log(fn.count);//3
登入後複製

雖然這兩種方式都輸出了正確的結果,但是卻避開了 this 到底綁定在哪裡的問題。如果對一個事物的工作原理不清晰,就往往會產生頭痛治頭,腳痛治腳的問題,從而導致代碼變得的醜陋,而且維護性也會變得很差。

2. this神奇的綁定規則

2.1 預設綁定規則

第一種是最常見的 this 的綁定,看看下面的程式碼

  function fn(){
    console.log(window === this); //浏览器环境
  }
  fn(); //true
登入後複製

函數fn 是直接在全域作用域下呼叫的,沒有帶其他任何修飾,這種情況下,函數呼叫的時候使用了 this 的預設綁定,指向了全域物件。

這樣就清楚了第一個例子中的this 指向, fn 函數中的this 指向了全域變量,所以this.count++ 相當於window.count++(瀏覽器環境下),當然不會對fn 函數的count屬性產生影響。

有一點要說明的是,上面種情況只能在非嚴格模式(strict mode)下才能發生,在嚴格模式下,會將 this 預設綁定為 undefined。以避免全域變數的污染。

2.2 隱式綁定規則

如果函數在以物件為上下文進行調用,那麼 this 的綁定就會產生變化。 this 會綁定到呼叫這個函數的對象,看看下面程式碼:

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

即使函數宣告不在物件當中,this 指向仍會產生變化

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

由此可見,this 的綁定,不與函數定義的位置有關,而是與呼叫者和呼叫方式有關。

在隱式的綁定規則下,有一些特殊的地方,需要注意。

2.2.1 多層物件呼叫 this 的指向

  function fn(){
    console.log(this.a);
  }
  var obj = {
    a:1,
    obj2:obj2
  }
  var obj2 = {
    a:2,
    obj3:obj3
  }
  var obj3 = {
    a:3,
    fn:fn
  }
  
  obj.obj2.obj3.fn(); //3
登入後複製

在多層物件參考下,this 指向的是呼叫的函數的那個物件。

2.2.2 隱式賦值可能有遺失現象

看下面程式碼

  function fn(){
    console.log(this);
  }
  var  obj = {
    fn:fn
  }
  
  var fun = obj.fn;
  fun(); //window
登入後複製

雖然 fn 引用了 obj.fun ,但是函數的呼叫方式,仍是不帶任何修飾的,所以 this 還是綁定在了 window 上。
還有一種情況,容易讓大家忽略,那就是傳參的時候,其實會隱式賦值。

 function fn(){
    console.log(this);
  }
  
  function doFn(fn){
    fn();
  }
  
  var obj = {
    fn:fn
  }
  
  doFn(obj.fn); //window
登入後複製

隱式綁定 this 不是一種很推薦的方式,因為很有可能就發生丟失的情況,如果業務當中對 this 的綁定有要求,建議還是使用顯示綁定的方式。

2.3 明確綁定規則

顯示綁定就是利用函數原型上的 apply 與 call 方法來對 this 進行綁定。用法就是把想要綁定的物件當作第一個參數傳進去。

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

有些時候會想將函數的 this 綁定在某個物件上,但是不需要立即調用,這樣的話,直接利用 call 或 apply 是無法做的。

  function fn(){
    console.log(this);
  }
  
  function bind(fn){
    fn();
  }
  
  var obj = {
    fn:fn
  }
  
  bind.call(obj,fn); //window
登入後複製

上面這個例子,看似好像可以,但實際上是 bind 函數的 this 綁定到了 obj 這個對象,但是 fn 仍然是沒有任何修飾的調用,所以 fn 仍然是默認的綁定方式。

  function fn(){
    console.log(this);
  }
  
  function bind(fn,obj){
    return function(){
      fn.apply(obj,arguments);
    }
  }
  
  var obj = {
    fn:fn
  }
  
  var fun = bind(fn,obj);
  fun(); //obj
登入後複製

这样调用,就可以将灵活多变的 this ,牢牢的控制住了,因为 fn 的调用方式为 apply 调用。所以,this 就被绑定在传入的 obj 对象上,在 ES5 当中,函数的原型方法上多了一个 bind。效果与上面的函数基本一致,具体用法限于篇幅就不多说了。

2.4 new 绑定

new 是一个被很多人误解的一个关键字,但实际上 javascript 的 new 与传统面向对象的语言完全不同。
个人把 new 理解为一种特殊的函数调用,当使用 new 关键字来调用函数的时候,会执行下面操作,

  • 创建一个全新的对象
  • 将空对象的 __proto__ 指向构造函数的 prototype
  • 将新对象的 this 绑定到调用的函数上
  • 如果函数返回值为基本类型或者为 this又或者不返回任何值,那么将会返回这个创建的新对象,如果返回了一个对象,那么则会返回这个对象,而不会返回创建的新对象。
  function fn(a){
    this.a = a;
  }
  fn.prototype.hi = function(){
    console.log('hi')
  }
  
  var obj = new fn(2);
  
  console.log(obj);


  function fn(a){
    this.a = a;
    return {};
  }
  
  var obj = new fn(2);
  
  console.log(obj); //{}

登入後複製

2.5 特殊的传参

null 和 undefined 也是可以作为 this 的绑定对象的,但是实际上应用的是默认的绑定。
但是这种传参的实际效用是什么呢?
常见的用法是将一个数组展开,作为参数传入参数。比如

  function fn(a,b){
    console.log('a:',a,'b:',b);
  }
  
  fn.apply(null,[1,2]); // a: 1 b: 2
登入後複製

但是这种用法会有一个坑,那就是如果函数存在了 this ,那么就会应用默认的绑定规则,将 this 绑定在全局对象上,发生于预期不一致的情况。为了代码更加稳健,可以使创建一个比空对象更空的对象。

var obj = Object.create(null);
console.log(obj.__proto__); //undefined

var obj2 = {}
console.log(obj2.__proto__); //Object {}

登入後複製

Object原型上有一个 create 方法,这个方法会创建一个对象,然后将对象的原型指向传入的参数,所以传入 null 的话,产生一个没有 prototype 的对象,所以会比空对象更加"空"。

所以传入这个对象,会比传入 null 更加安全。

var obj = Object.create(null);

fn.apply(obj,[1,2]);

登入後複製

2.6 根据作用域来决定 this 的绑定

在 ES6 当中,出现了一个新的函数类型,箭头函数。

如果使用箭头函数,那么就不会使用上面提到的四种 this 绑定方式,而是根据作用域来决定

比较常见的是用于事件函数和定时器的情况。

下面是比较常见的传统 this 写法

  function fn(){
    var _this = this;
    setTimeout(function(){
      console.log(_this.a);
    },100)
  }

  var obj = {
    a:2
  }
  
  fn.call(obj); //2
  

登入後複製

如果使用箭头函数则可以这么写

  function fn(){
    setTimeout(()=>{
      //this 来源于 fn 函数的作用域
      console.log(this.a);
    },100)
  }

  var obj = {
    a:2
  }
  
  fn.call(obj); //2

登入後複製

2.7 事件函数当中 this 的绑定机制

如果是在事件函数当中,this 的绑定是指向触发事件的 DOM 元素的,

$('body')[0].addEventListener('click',function(){
  console.log(this);
},false);
登入後複製

点击 body 元素之后,控制台则会显示 body 元素

3. 小结

如果想判断一个函数的 this 绑定在哪里,首先是找到函数的调用位置,之后是按照规则来判断。

  • 如果函数调用时没有任何修饰条件,那么在严格模式下则会绑定到 undefined ,非严格模式下会绑定到全局。
  • 如果是用对象做上下文,来对函数进行调用,那么则会绑定到调用的这个对象上。
  • 如果是用 call 或者 apply 方法来进行调用的,则会绑定到第一个传入参数上。
  • 如果是使用 new 关键字来调用函数的,则会绑定到新创建的那个对象上.
  • 如果是在事件函数内,则会绑定到触发事件的那个DOM元素上。

以上就是关于Javascript中神奇的this的相关介绍,希望对大家的学习有所帮助。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡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)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript與WebSocket:打造高效率的即時影像處理系統 JavaScript與WebSocket:打造高效率的即時影像處理系統 Dec 17, 2023 am 08:41 AM

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數

See all articles