首頁  >  文章  >  web前端  >  Javascript中的this對象

Javascript中的this對象

不言
不言原創
2018-07-07 11:06:571166瀏覽

 這篇文章主要介紹了關於Javascript中的this對象,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

對於this的使用,我們最常遇到的主要有,在全域函數中,在物件方法中,call和apply時,在閉包中,箭頭函數中以及class中;

我們知道this物件是在運行時基於函數的執行環境綁定的,在呼叫函數之前,this的值並不確定,因此this會在程式碼執行過程中引用不同的物件。哪個物件實例呼叫this所在的函數,那麼this就代表哪個物件實例。

1.    全域函數

       在全域函數中,this等於window;

var name = "Tina";function sayName() {
    alert(this.name);
}
person();//Tina

       在這裡,由於函數person()是在全域環境中執行的,也是在全域作用域中window物件呼叫的person();故此時的this便指向window物件。而當把這個函數賦給物件o並呼叫o.sayName()時,this引用的是物件o,因此對this.name的求值就變成了對o.name求值。

 name = "Tina"

2. 物件方法

       #當函數被當作某個物件的方法呼叫時,this等於那個物件;

var name="Tina";var obj={
      name="Tony",
      getName: function() {
            alert(this.name);
      }
};
obj.getName();//"Tony"

3.call()和apply( )和bind()

      我們知道,call(ctx, parm1,parm2,parm3...)和apply(ctx,[parms])的用途都是在在特定的作用域中呼叫函數,實際上等於設定函數體內this物件的值;

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

      在上面的範例中,callSum1()和callSum2( )在執行函數sum()時傳入了this作為this值(因為是在全域作用域中呼叫的,所以傳入的就是window物件);事實上,call和apply最強之處是能夠擴充函數賴以運作的作用域;來看下面的例子:

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"

#      bind()方法會建立一個函數的實例,其this值會被綁定到傳給bind()函數的值。例如:

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

       另一個使用場景是函數綁定,函數綁定要建立一個函數,可以在特定的this環境中以指定參數呼叫另一個函數,該技巧常常和回呼函數與事件處理程序一起使用,以便在將函數作為變數傳遞的同時保留程式碼執行環境。

var handler = {
      message: "Event handled",
      handleClick : function(event) {
            alert(this.message);
      }
};var btn = document.getElementById("my_btn");
btn.addEventListener("click", handler.handleClick, false);

      當按下該按鈕時,就呼叫該函數,顯示一個警告框,雖然看似警告框應該顯示Event handled,然而實際上顯示的是undefined。原因在於沒有儲存handler.handleClick()的執行環境,所以this物件最後指向了DOM按鈕而非handler(在IE8中,this指向window)。一種方法,可以使用一個閉包來修正這個問題。

var handler = {
      message: "Event handled",
      handleClick : function(event) {
            alert(this.message);
      }
};var btn = document.getElementById("my_btn");
btn.addEventListener("click", function(event){
      handler.handleClick(event);
}, false);

      這個解決方案在onclick事件處理程序內使用了一個閉包直接呼叫handler.handleClick(),當然,這是特定與本段程式碼的解決方案。我們知道,創建多個閉包可能會讓程式碼變得難以理解和調試。因此,許多JS函式庫實作了一個可以將函數綁定到指定環境的函數,這個函數一般叫bind();ECMAScript 5為所有函數定義了一個原生的bind()方法,它的使用方式如下:

var handler = {
      message: "Event handled",
      handleClick : function(event) {
            alert(this.message+":"+event.type);
      }
};var btn = document.getElementById("my_btn");
btn.addEventListenr("click", handler.handleClick.bind(handler), false);

      一個簡單的bind()函數接受一個參數和一個環境,​​並傳回一個在給定環境中呼叫給定函數的函數,並且將所有參數原封不動地傳遞過去。語法如下:

function bind(fn, context) {      
return function() {            
return fn.apply(context, arguments);
      };
}

      這個函數在bind()中建立了一個閉包,閉包使用apply呼叫傳入的函數,並傳遞給applycontext物件個arguments物件數組,這裡的arguments物件是內部函數(匿名函數)的,而非bind()的參數。當呼叫返回的函數時,它會在給定環境中執行被傳入的函數並給出所有參數。

      原生的bind()方法和前面自訂的bind()方法類似,都是要傳入作為this值的物件。它們主要用於事件處理程序以及setTimeout()和setInterval()。 bind綁定在react事件處理中也常常和箭頭函數一樣起到綁定this的效果。

4. 閉包

      有時候,由於寫閉包的方式不同,在閉包中使用this物件可能會導致一些問題;

var name = "The window";var object = {
      name: "My Object",
      getNameFunc: function() {            
      return function() {                  
      return this.name;
            };
      }
};
alert(object.getNameFunc()());//"The window"

      由于getNameFunc()返回一个函数,因此调用object.getNameFunc()()就会立即调用它返回的函数,结果就是返回一个字符串"The window",即全局变量的值,此时匿名函数没有取得其包含作用域(外部作用域)的this对象。原因在于内部函数在搜索两个特殊变量this和arguments时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。这时,只需把把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。

var name = "The window";var object = {
      name: "My Object",
      getNameFunc: function() {            
      var that = this;            
      return function() {                  
      return that.name;
            };
      }
};
alert(object.getNameFunc()());//"My Object"

//节流function throttle(fn, delay) {      
var previous = Data.now();      
return function() {             
var ctx = this;             
var args = arguments;             
var now = Data.now();             
var diff = now-previous-delay;              
if(diff>=0) {
                    previous = now;
                    setTimeout(function() {
                         fn.apply(ctx, args);
                    }, delay);
              }
       };
}

5. 箭头函数

      我们知道,箭头函数有几个需要注意的点:

      (1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象;

      (2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误;

      (3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数代替;

      (4)不可以使用yield命令,因此箭头函数不能用作Generator函数;

      这里我们只谈论第一点;this对象的指向是可变的,但在箭头函数中,它是固定的;

function foo() {
      setTimeout(() => {
          console.log('id: ', this.id);
      }, 100);
}var id=21;
foo.call({id: 31});//id: 31

      上述代码中,setTimeout是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它真正加入到执行栈后还要等到100毫秒后才会执行,如果是普通函数,此时的this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id:31})所以输出的是id: 31;

      箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子:

function Timer() {      this.s1 = 0;      this.s2 = 0;
      setInterval(() => this.s1++, 1000);
      setInterval(function() {            this.s2++;
      }, 1000);
}var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);//s1: 3setTimeout(() => console.log('s2: ', timer.s2), 3100);//s2: 0

      上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100毫秒后,timer.s1被更新了3次,timer.s2一次都没更新。

      箭头函数可以让this指向固定化,这种特性很有利于封装回调函数。下面代码将DOM事件的回调函数封装在一个对象里面。

var handler = {
      id: '123456',
      init: function() {
            document.addEventListener('click',
                event => this.doSomething(event.type), false);
      },
      doSomething: funcition(type) {
            console.log('Handling ' + type + ' for ' + this.id);
      }
};

      上面代码的init方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。否则,回调函数运行时,this.doSomething这一行会报错,因为此时this指向document对象。this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码的this。正是因为它没有this,所以也就不能用作构造函数。由于箭头函数没有自己的this,所以当然不能用call()、apply()、bind()改变this的指向。

6. class

      类的方法内部如果含有this,它默认指向类的实例。

class Logger {    /*constructor() {
        this.printName = this.printName.bind(this);
    }*/
    printName(name = 'Nicolas') {        this.print(`Hello ${name}`);
    }
    print(text) {
        console.log(text);
    }
}
const logger = new Logger();
const { printName } = logger;
printName();

      上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境,因为找不到print方法而导致报错。一种简单的解决方法就是在构造函数中绑定this。而另一种方法是使用箭头函数:

class Logger {
    constructor() {        this.printName = (name='Nicolas') => {            this.print(`Hello ${name}`);
    }
    print(text) {
        console.log(text);
    }
}
const logger = new Logger();
const { printName } = logger;
printName();

      还有一种方法是使用Proxy,获取方法的时候,自动绑定this。

function selfish (target) {
  const cache = new WeakMap();
  const handler = {
    get (target, key) {
      const value = Reflect.get(target, key);      if (typeof value !== 'function') {        return value;
      }      if (!cache.has(value)) {
        cache.set(value, value.bind(target));
      }      return cache.get(value);
    }
  };
  const proxy = new Proxy(target, handler);  return proxy;
}
const logger = selfish(new Logger());

 以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

foreach, for in, for of 之间的异同

Vue+Electron实现简单桌面应用

JS定时器和单线程异步特性

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

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