国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

捕捉JavaScript中this的指向

hiyang / 1644人閱讀

摘要:構造函數(shù)調用構造函數(shù)調用將一個全新的對象作為變量的值,并隱式返回這個新對象作為調用結果。調用方式引起的改變函數(shù)的調用方式最常見的是方法調用構造函數(shù)調用,或者使用調用,也可以是立即執(zhí)行函數(shù)。

JavaScript的this機制很復雜,雖然從一開始從事前端工作就和它打交道,但一直未能弄清楚,道明白。在工作中遇到this相關問題,就知道var self = this,一旦去面試遇到各種this相關面試題目時腦子就一片空白,拿不定結果。本文綜合了一些書籍和網(wǎng)上文章對this的分析和講解,提供一些實例來分析各種場景下this是如何指向的。

全局作用域

在瀏覽器宿主環(huán)境中,this指向window對象,并且在全局作用域下,使用var聲明變量其實就相當于操作全局this

this === window; // true

var foo = "bar";
this.foo === window.foo; // true

在嚴格模式下,this會綁定到undefined

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

foo(); // TypeError: this is not undefined

如果在變量的聲明過程沒有使用let或者var,會隱式創(chuàng)建一個全局變量,但這個變量和普通全局變量的區(qū)別在于它是作為window的一個屬性創(chuàng)建的。二者在使用delete操作符上有明顯的區(qū)別:變量不可以刪除,而對象的屬性是可以刪除的

var a = 2;
b = 3;
a; // 2
b; // 3
delete a;
delete b;
a; // 2
b; // Uncaught ReferenceError: b is not defined
局部作用域

這里的作用域主要是指在對象函數(shù)中的this指向。

函數(shù)調用

作為函數(shù)調用時,函數(shù)中的this默認指向window

var a = 1;
function foo() {
  console.log(this.a);
}

foo(); // 1

如果在立即執(zhí)行函數(shù)中使用了this,它同樣指向window

var a = 1;
(function() {
  var a = 2;
  console.log(this.a);
})(); // 1
方法調用

作為方法調用時,函數(shù)中的this總是指向方法所在的對象。

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

obj.foo();
構造函數(shù)調用

構造函數(shù)調用將一個全新的對象作為this變量的值,并隱式返回這個新對象作為調用結果。也就是說指向新生成的實例。

function Foo(name) {
  this.name = name;
  this.getName = function() {
    console.log(this.name);
  }
}

var a = new Foo("a");
a.getName(); // "a"
使用call和apply方法

可以通過call()apply()方法顯示改變函數(shù)的this指向。

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

foo(); // 1
foo.call(obj); // 2
foo.apply(obj); // 2
使用bind方法

bind()方法創(chuàng)建一個新的函數(shù), 當被調用時,將其this關鍵字設置為提供的值,在調用新函數(shù)時,在任何提供之前提供一個給定的參數(shù)序列,然后返回由指定的this值和初始化參數(shù)改造的原函數(shù)拷貝。

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

var bar = foo.bind(obj);
bar();
箭頭函數(shù)中調用

ES6引入了箭函數(shù)的概念,在箭頭函數(shù)中由于沒有this綁定,所以它的默認指向是由外圍最近一層非箭頭函數(shù)決定的。

var a = 1;
function Foo(a) {
  this.a = a;
  this.getA = function() {
    var x = () => {
      this.a = 3; // 改變了外圍函數(shù)Foo屬性a的值
      console.log(this.a); // 3
    }
    x();
    console.log(this.a); // 3
  }
}

var foo = new Foo(1);
foo.getA();
問題的產(chǎn)生

上面列舉了在正常情況下this的指向結果。但是在實際開發(fā)過程中,對于不同場景,不同的聲明方式、調用方式、賦值和傳值方式都會影響到this的具體指向。

調用方式引起的改變

函數(shù)的調用方式最常見的是方法調用構造函數(shù)調用,或者使用apply/bind/call調用,也可以是立即執(zhí)行函數(shù)。

var a = 10;
var obj = {
  a: 20,
  fn: function() {
    var a = 30;
    console.log(this.a);
  }
}

obj.fn(); // 20
obj.fn.call(); // 10
(obj.fn)(); // 20
(obj.fn, obj.fn)(); // 10
(obj.fn = obj.fn)(); // 10
new obj.fn(); // undefined

對于applycall第一個參數(shù)如果不傳或者傳遞undefinednull則默認綁定到全局對象,所以obj.fn.call()的調用實際上把this指向了window對象。

對于(obj.fn)(),咋一看,是立即執(zhí)行函數(shù),那么它的this肯定指向了window對象,其實不然,這里obj.fn只是一個obj對象方法的引用,并沒有改變this的指向。

對于(obj.fn, obj.fn)(),這種操作比較少見,工作中也不會去這樣寫。這里首先我們需要了解逗號操作符會對每個操作數(shù)求值,并返回最后一個操作數(shù)的值,其次是這里使用了逗號操作符,里面必然是一個表達式,這種情況下里面的函數(shù)this指向其實已經(jīng)改變了,指向了全局。對于(obj.fn = obj.fn)()this同樣指向全局。因此可以大膽猜測:如果(x)();中x是一個表達式,并且返回一個函數(shù)(引用),那么函數(shù)x中的this指向全局window。這里還更多的方式來達到同樣目的,比如:(true && obj.fn)() 或者 (false || obj.fn)()。總的來說,我們通過這種方式創(chuàng)建了一個函數(shù)的“間接引用”,從而導致函數(shù)綁定規(guī)則的改變。

對于new obj.fn()的結果其實也沒有什么好說的,函數(shù)使用new操作符調用后返回一個新的實例對象,由于該對象并沒有一個叫a的屬性,所以返回undefined

函數(shù)作為參數(shù)(變量)傳遞時

很多時候,函數(shù)的定義在一個地方,而對象定義方法時只是引用了該函數(shù)。同樣在調用對象方法時,先把它賦值給一個變量(別名),然后使用函數(shù)別名進行調用。使用時有可能導致this綁定的改變。

示例一
var a = 10;
function foo() {
  console.log(this.a);
}

var obj = {
  a: 20,
  foo: foo
}

var bar = obj.foo; // 函數(shù)別名
bar(); // 10

雖然barobj.foo的一個引用,但是實際上,它引用的是foo函數(shù)本身,因此應用了函數(shù)的默認綁定規(guī)則。

示例二
var a = 10;
function foo() {
  console.log(this.a);
}

function doFoo(cb) {
  cb(); // cb 實際上引用的還是foo
}

var obj = {
  a: 20,
  foo: foo
}

doFoo(obj.foo); // 10
setTimeout(obj.foo, 100); // 10

這里我們將obj.foo以參數(shù)的形式傳遞給函數(shù)doFoo和內置函數(shù)setTimeout。參數(shù)傳遞實際上就是一種賦值,和示例一的結果是一樣的。因此,調用回調函數(shù)的函數(shù)會丟失this的指向

改變構造函數(shù)的默認返回對象

構造函數(shù)使用new操作符調用后會返回一個新的實例對象,但是在定義構造函數(shù)時,可以在函數(shù)中返回任何值來覆蓋默認該返回的實例,這樣一來很可能導致實例this的指向改變。

var a = 10;
function f() {
  this.a = 20;
  function c() {
    console.log(this.a);
  }
  return c();
}

new f(); // 10

這里我們將構造函數(shù)foo的默認返回值改成返回一個函數(shù)c執(zhí)行后的結果。當調用new f()后,內部函數(shù)c中的this實際上指向的是全局。但是如果我們將return c()改成return new c()的話,那么new foo()執(zhí)行的結果是返回一個構造函數(shù)c的實例,由于實例對象中并沒有屬性a,因此結果為undefined

方法的接收者引起的問題

在方法的調用中由調用表達式自身來確定this變量的綁定。綁定的this變量的對象被稱為調用接收者

var buffer = {
  entries: [],
  add: function(s) {
    this.entries.push(s);
  },
  concat: function() {
    return this.entries.join("");
  }
}

var source = ["123", "-", "456"];
source.forEach(buffer.add); // Uncaught TypeError: Cannot read property "push" of undefined

由于方法buffer.add()的接收者不是buffer本身,而是forEach方法。事實上,forEach方法的實現(xiàn)使用全局對象作為默認的接收者。由于全局沒有entries屬性,因此會拋出一個錯誤。

要解決上面的問題,一個是使用forEach方法提供的可選參數(shù)作為函數(shù)的接收者。

source.forEach(buffer.add, buffer);

其次是使用bind方法來指定接收者

source.forEach(buffer.add.bind(buffer));
對象的實例屬性和原型屬性

這里想要說明的是,在一個對象的實例中,this即可以訪問實例對象的值,也可以獲取原型上的值。

function Foo() {}
Foo.prototype.name = "bar";
Foo.prototype.logName = function() {
  console.log(this.name);
}
Foo.prototype.setName = function(name) {
  this.name = name;
}
Foo.prototype.deleteName = function() {
  delete this.name;
}

var foo = new Foo();
foo.setName("foo");
foo.logName(); // "foo"

foo.deleteName();
foo.logName(); // "bar"

delete foo.name;
foo.logName(); // "bar"

當執(zhí)行foo.setName("foo")后,給實例對象foo增加了一個屬性name,同時覆蓋了原型中的同名屬性。當執(zhí)行foo.deleteName()時,實際上是將新增值刪除了,還原了初始狀態(tài)。執(zhí)行delete foo.name時,試圖刪除的還是新增的屬性,但是現(xiàn)在已經(jīng)不存在這個值了。如果需要刪除原始值,可以通過delete foo.__proto__.name來實現(xiàn)。

總結

本文只是介紹了一部分有關this的問題,更多知識點可以參考《詳解this》以及MDNthis

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89376.html

相關文章

  • JavaScripr常遇到錯誤和this關鍵字

    摘要:通過使用提供的異常處理語句,可以用結構化的方式來捕捉發(fā)生的錯誤,讓異常處理帶啊與核心業(yè)務代碼實現(xiàn)分離。錯誤與異常處理在應用中的重要性是毋庸置疑的。包括內置對象函數(shù)在內的所有函數(shù)都可以用來調用,這種函數(shù)調用被稱為構造函數(shù)調用。 錯誤與異常 概念 錯誤與異常是什么錯誤,指程序中的費正常運行狀態(tài),在其他編程語言中稱為‘異常’或‘錯誤’。解釋器會為每一個錯誤創(chuàng)建并拋出一個Error對象,其中包...

    klinson 評論0 收藏0
  • javascript-錯誤與異常、 this關鍵字

    摘要:錯誤與異常錯誤,指程序中的非正常運行狀態(tài),在其他編程語言中稱為異常或,錯誤。定義一個全局變量,并賦值對象的方法綁定在中,構造函數(shù)只是一些使用操作符時被調用的函數(shù)。包括內置對象函數(shù)在內的所有函數(shù)都可以用來調用,這種函數(shù)調用被稱為構造函數(shù)調用。 錯誤與異常 錯誤,指程序中的非正常運行狀態(tài),在其他編程語言中稱為‘異常’或,‘錯誤’。解釋器為每個錯誤情形創(chuàng)建并拋出一個Error對象,其中包含錯...

    zhaofeihao 評論0 收藏0
  • 帶你入門 JavaScript ES6 (三)

    摘要:上一章我們學習了遍歷和擴展字符語法。本章我們主要學習中的箭頭函數(shù)箭頭函數(shù)更準確來說叫箭頭函數(shù)表達式。箭頭函數(shù)余普通函數(shù)功能相同,但語法差別比較大。 帶你入門 JavaScript ES6 (三) 本文同步帶你入門 JavaScript ES6 (三),轉載請注明出處。 上一章我們學習了 for of 遍歷和擴展字符語法。本章我們主要學習 ES6 中的箭頭函數(shù) 箭頭函數(shù) 更準確來說叫 箭...

    劉福 評論0 收藏0
  • JavaScript】面向對象之錯誤與異常與this關鍵字

    摘要:一錯誤與異常概述錯誤,指程序中的非正常運行狀態(tài),在其它語言中稱為異常或錯誤將每個錯誤中創(chuàng)建個對象,描述包含的錯誤信息通過使用提供異常的處理語句,可以用結構化方式捕捉發(fā)生錯誤,異常處理代碼與核心代碼實現(xiàn)分離語句語句是指中處理異常一種標準方式, JS(JavaScript)一.錯誤與異常1.概述錯誤,指程序中的非正常運行狀態(tài),在其它語言中稱為異常或錯誤將每個錯誤中創(chuàng)建個Error對象,描述...

    ASCH 評論0 收藏0
  • 我了解到JavaScript異步編程

    摘要:接下來我們看下三類異步編程的實現(xiàn)。事件監(jiān)聽事件發(fā)布訂閱事件監(jiān)聽是一種非常常見的異步編程模式,它是一種典型的邏輯分離方式,對代碼解耦很有用處。 一、 一道面試題 前段時間面試,考察比較多的是js異步編程方面的相關知識點,如今,正好輪到自己分享技術,所以想把js異步編程學習下,做個總結。下面這個demo 概括了大多數(shù)面試過程中遇到的問題: for(var i = 0; i < 3; i++...

    RichardXG 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<