首頁 web前端 js教程 深入理解javascript中this指針

深入理解javascript中this指針

Mar 17, 2018 pm 04:21 PM
javascript js this

本文主要跟大家分享深入理解javascript中this指針,在寫java的時候,this用錯了,idea都會直接報錯!

例如…



#在物件導向程式設計裡有兩個重要的概念:一個是類,一個是實例化的物件,類別是一個抽象的概念,用個形象的比喻表述的話,類別就像一個模具,而實例化物件就是透過這個模具製造出來的產品,實例化對象才是我們需要的實實在在的東西,類別和實例化對像有著很密切的關係,但是在使用上類的功能是絕對不能取代實例化對象,就像模具和模具製造的產品的關係,二者的用途是不相同的。

有上面程式碼我們可以看到,this指標在java語言裡只能在實例化物件裡使用,this指標等於這個被實例化好的對象,而this後面加上點操作符,點操作符後面的東西就是this所擁有的東西,例如:姓名,工作,手,腳等等。

其實javascript裡的this指標邏輯上的概念也是實例化物件,這一點和java語言裡的this指標是一致的,但是javascript裡的this指標卻比java裡的this難以理解的多,究其根本原因我個人覺得有三個原因:

原因一:javascript是一個函數編程語言,怪就怪在它也有this指針,說明這個函數編程語言也是物件導向的語言,說的具體點,javascript裡的函數是一個高階函數,程式語言裡的高階函數是可以當作物件傳遞的,同時javascript裡的函數還有可以當作建構函數,這個建構函數可以建立實例化對象,結果導致方法執行時候this指標的指向會不斷變化,很難控制。

原因二:javascript裡的全域作用域對this指標有很大的影響,由上面java的例子我們看到,this指標只有在使用new運算元後才會生效,但javascript裡的this在沒有進行new操作也會生效,這時候this往往會指向全域物件window。

原因三:javascript裡call和apply操作符可以隨意改變this指向,這看起來很靈活,但是這種不合常理的做法破壞了我們理解this指針的本意,同時也讓寫代碼時候很難理解this的真正指向



#上面的三個原因都違反了傳統this指標所使用的方法,它們都擁有有別於傳統this原理的理解思路,而在實際開發里三個原因又往往會交織在一起,so,this,雲裡霧裡了……


入門書:professionnal Javascript for web devolopers,--高階的說法是這樣的:

##this總是指向呼叫該方法的對象!



var name="zhoulujun";

function say(){

#console.log(this.name)

}

say(); //zhoulujun



#在script標籤裡我們可以直接使用this指針,this指標(指向window對象,結果)就是window物件,即使使用三等號它們也是相等的。全域作用域常常會幹擾我們很好的理解javascript語言的特性,這種幹擾的本質就是:

在javascript語言裡全域作用域可以理解為window物件,記住window是物件而不是類,也就是說window是被實例化的物件,這個實例化的過程是在頁面載入時候由javascript引擎完成的,整個頁面裡的要素都被濃縮到這個window對象,因為程式設計師無法透過程式語言來控制和操作這個實例化過程,所以開發時候我們就沒有建構這個this指針的感覺,常常會忽略它,這就是乾擾我們在程式碼裡理解this指針指向window的情形。

這裡this指向window對象,所以this.name->zhoulujun!



當執行say函數的時候, JavaScript 會建立一個Execute context (執行上下文),執行上下文中就包含了say函數運行期所需的所有資訊。 Execute context 也有自己的 Scope chain, 當函數運行時, JavaScript 引擎會先從用 say函數的作用域鏈來初始化執行上下文的作用域鏈。

這裡可以大致記下:

var myObj={
name:"zhoulujun",
fn:function(){
console.log(this.name)
}
};
myObj.fn();
登入後複製



#

这里的this指向obj,因为fn()运行在obj里面……

然后再来看……

var name="zhoulujun";
function say(){
console.log(this.name)
console.log(this)
}
say();
function say2(){
var site="zhoulujun.cn";
console.log(this.site);
}
say2();
登入後複製


myObj2={
site:"zhoulujun.cn",
fn:function(){
console.log(this.site)
}
}
登入後複製



这里的this指向的是对象myObj2,因为你调用这个fn是通过myObj2.fn()执行的,那自然指向就是对象myObj2,这里再次强调一点,this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个

然后,我们更深入(受不了 …………

myObj3={
site:"zhoulujun.cn",
andy:{
site:"www.zhoulujun.cn",
fn:function(){
console.log(this.site)
}
}
};
myObj3.andy.fn();
登入後複製



这里同样也是对象Object点出来的,但是同样this并没有执行它,那你肯定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不准确,接下来我将补充一句话,我相信你就可以彻底的理解this的指向的问题。

如果,你实在理解不了,就这么样背下来吧!

情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。

情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,如果不相信,那么接下来我们继续看几个例子。

这样既对了吗??深入点(就受不了了……讨厌……

myObj3={
site:"zhoulujun.cn",
andy:{
site:"www.zhoulujun.cn",
fn:function(){
console.log(this)
console.log(this.site)
}
}
};
//    myObj3.andy.fn();
var fn=myObj3.andy.fn;
fn();
登入後複製



其实,这里的 fn等价于

fn:function(age){

console.log(this.name+age);

}

下面我们来聊聊函数的定义方式:声明函数和函数表达式

我们最上面第一个案例定义函数的方式在javascript语言称作声明函数,第二种定义函数的方式叫做函数表达式,这两种方式我们通常认为是等价的,但是它们其实是有区别的,而这个区别常常会让我们混淆this指针的使用,我们再看看下面的代码:



為什麼say可以執行,say3不可以?那是因為:



為什麼say3印出結果是undefined,我在前文裡講到了undefined是在記憶體的堆疊區已經有了變數的名稱,但是沒有堆疊區的變數值,同時堆疊區是沒有特定的物件,這是javascript引擎在預先載入掃描變數定義所致,但是ftn01的列印結果很令人意外,既然列印出完成的函數定義了,而且程式碼並沒有按順序執行,這只能說明一個問題:

在javascript語言透過聲明函數方式定義函數,javascript引擎在預處理過程裡就把函數定義和賦值操作都完成了,在這裡我補充下javascript裡預處理的特性,其實預處理是和執行環境相關,在上篇文章裡我講到執行環境有兩大類:全局執行環境和局部執行環境,執行環境是透過上下文變數體現的,其實這個過程都是在函數執行前完成,預處理就是構造執行環境的另一個說法,總而言之預處理和構造執行環境的主要目的就是明確變量定義,分清變數的邊界,但是在全域作用域構造或者說全域變數預處理時候對於宣告函數有些不同,宣告函數會將變數定義和賦值運算同時完成,因此我們看到上面程式碼的運行結果。由於宣告函數都會在全域作用域建構時候完成,因此宣告函數都是window物件的屬性,這就說明為什麼我們不管在哪裡宣告函數,宣告函數最終都是屬於window物件的原因了。

這裡推薦看下-java一個類別的執行順序:

http://www.zhoulujun.cn/zhoulujun/html/java/javaBase/7704.html



#

其实在javascript语言里任何匿名函数都是属于window对象,它们也都是在全局作用域构造时候完成定义和赋值,但是匿名函数是没有名字的函数变量,但是在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,因为匿名函数也是在全局执行环境构造时候定义和赋值,所以匿名函数的this指向也是window对象,所以上面代码执行时候fn的this都是指向window,因为javascript变量名称不管在那个作用域有效,堆区的存储的函数都是在全局执行环境时候就被固定下来了,变量的名字只是一个指代而已。

类似的情况(面试题喜欢这么考!)……比如:



this都是指向实例化对象,前面讲到那么多情况this都指向window,就是因为这些时候只做了一次实例化操作,而这个实例化都是在实例化window对象,所以this都是指向window。我们要把this从window变成别的对象,就得要让function被实例化,那如何让javascript的function实例化呢?答案就是使用new操作符。

再来看 构造函数:

function  User(){
this.name="zhoulujun";
console.log(this);
}
var andy=new User();
console.log(andy.name)
登入後複製



why andy 的name 是 zhoulujun,那是:因为:

new关键字可以改变this的指向,将这个this指向对象andy,

那andy什么时候又成了思密达,oh,no,is Object?

因為用了new關鍵字就是建立一個物件實例(重要的事情默讀三遍)

我們這裡用變數andy建立了一個User使用者實例(相當於複製了一份User到物件andy裡面),此時僅只是創建,並沒有執行,而呼叫這個函數User的是物件andy,那麼this指向的自然是物件andy,那麼為什麼物件User中會有name,因為你已經複製了一份User函數到物件andy中,用了new關鍵字就等於複製了一份。

java 程式猿: Class user=new User();似曾相識木有…



function既是函數又可以表示對象,function是函數時候還能當做建構函數,javascript的建構子我常認為是把類別和建構子合而為一,當然在javascript語言規範裡是沒有類別的概念,但是我這種理解可以當作建構子和普通函數的一個差別,這樣理解起來會更容易

下面我貼出在《javascript高階程式設計》裡對new運算元的解釋:

new運算子會讓建構子產生以下變更:

# 1.       建立一個新物件;

2.       將建構函數的作用域賦給新物件(因此this就指向了這個新物件);

##3.       執行建構函式中的程式碼(為這個新物件新增屬性);######

4. 返回新对象

……

妈的:读的那么拗口,不明觉厉…………看图……还不

不明白……

var myObj5={
name:"andy"
};
var myObj6=new Object();
myObj6.name="andy";
function  say5(name){
console.log(name)
}
var say6=new Function("name","console.log(name)");
console.log(myObj5)
console.log(myObj6)
say5("andy");
say6("andy");
登入後複製



还不明白,就请奶奶买块豆腐,撞死算了……

第四点也要着重讲下,记住构造函数被new操作,要让new正常作用最好不能在构造函数里写return,没有return的构造函数都是按上面四点执行,有了return情况就复杂了

return这王八蛋……



那么我这样呢……



does it have to be like this?Tell me why(why),is there something I have missed?

Tell me why(why),cos I don't understand…………



那是因为……because of u?no return……



所以:如果返回的是基本类型,就会丢掉…只能返回Object类型……typeof xx ===“object”

看到called 没有?什么鬼!!

其实new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

var a={
name:"andy",
site:"zhoulujun.cn",
fn:function(age){
console.log(this.name+age);
}
};
var b={
name:"zhoulujun",
site:"www.zhoulujun.cn",
fn:function(age){
console.log(this.name+age);
}
};
a.fn(2); //andy2
a.fn.call(b,2) //zhoulujun2
a.fn.apply(b,[2])//zhoulujun2
当然,还有bind……
var arr = [1, 2];
var add = Array.prototype.push.bind(arr, 3);
// effectively the same as arr.push(3)
add();
// effectively the same as arr.push(3, 4)
add(4);
console.log(arr);
// <- [1, 2, 3, 3, 4]
在下面的例子,this将无法在作用域链中保持不变。这是规则的缺陷,并且常常会给业余开发者带来困惑。
function scoping () {
console.log(this);
return function () {
console.log(this);
};
}
scoping()();
// <- Window
// <- Window
有一个常见的方法,创建一个局部变量保持对this的引用,并且在子作用域中不能有同命变量。子作用域中的同名变量将覆盖父作用域中对this的引用。
function retaining () {
var self = this;
return function () {
console.log(self);
};
}
retaining()();
// <- Window
除非你真的想同时使用父作用域的this,以及当前this值,由于某些莫名其妙的原因,我更喜欢是使用的方法.bind函数。这可以用来将父作用域的this指定给子作用域。
function bound () {
return function () {
console.log(this);
}.bind(this);
}
bound()();
// <- Window
登入後複製

写到这里,都看不下去,逻辑有点混乱,有的是从前辈哪里引用的。

改天有时间整理下,然后,在去讲下闭包(……closer





#

以上是深入理解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脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++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來實現線上語音辨識系

建議:優秀JS開源人臉偵測辨識項目 建議:優秀JS開源人臉偵測辨識項目 Apr 03, 2024 am 11:55 AM

人臉偵測辨識技術已經是一個比較成熟且應用廣泛的技術。而目前最廣泛的網路應用語言非JS莫屬,在Web前端實現人臉偵測辨識相比後端的人臉辨識有優勢也有弱勢。優點包括減少網路互動、即時識別,大大縮短了使用者等待時間,提高了使用者體驗;弱勢是:受到模型大小限制,其中準確率也有限。如何在web端使用js實現人臉偵測呢?為了實現Web端人臉識別,需要熟悉相關的程式語言和技術,如JavaScript、HTML、CSS、WebRTC等。同時也需要掌握相關的電腦視覺和人工智慧技術。值得注意的是,由於Web端的計

股票分析必備工具:學習PHP和JS繪製蠟燭圖的步驟 股票分析必備工具:學習PHP和JS繪製蠟燭圖的步驟 Dec 17, 2023 pm 06:55 PM

股票分析必備工具:學習PHP和JS繪製蠟燭圖的步驟,需要具體程式碼範例隨著網路和科技的快速發展,股票交易已成為許多投資者的重要途徑之一。而股票分析是投資人決策的重要一環,其中蠟燭圖被廣泛應用於技術分析。學習如何使用PHP和JS繪製蠟燭圖將為投資者提供更多直觀的信息,幫助他們更好地做出決策。蠟燭圖是一種以蠟燭形狀來展示股票價格的技術圖表。它展示了股票價格的

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協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

PHP與JS開發技巧:掌握繪製股票蠟燭圖的方法 PHP與JS開發技巧:掌握繪製股票蠟燭圖的方法 Dec 18, 2023 pm 03:39 PM

隨著網路金融的快速發展,股票投資已經成為了越來越多人的選擇。而在股票交易中,蠟燭圖是常用的技術分析方法,它能夠顯示股票價格的變動趨勢,幫助投資人做出更精準的決策。本文將透過介紹PHP和JS的開發技巧,帶領讀者了解如何繪製股票蠟燭圖,並提供具體的程式碼範例。一、了解股票蠟燭圖在介紹如何繪製股票蠟燭圖之前,我們首先需要先了解什麼是蠟燭圖。蠟燭圖是由日本人

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

See all articles