《你不知道的javascript》這本書讀了有好幾遍了,似乎每一次讀都有新發現,有些內容並不是一下子可以弄懂的,每次讀似乎都能明白一些概念。
再重讀一下this
關鍵字。這個概念非常靈活,也非常難掌握,所以我覺得經常讀讀沒有壞處。期待javascript一桶江湖,這樣學習的成本就低!
參考本書的第二部分的第一章,第二章。
this關鍵字是js中最複雜的機制之一。他被自動定義到所有函數的作用域。
在學習這個關鍵字的過程中似乎也走了很長時間的彎路。你要問我為什麼走了很長時間的彎路,關鍵的地方還是沒有對核心的概念徹底學習和領會,這一點和小學生學習新知識沒有任何區別。要掌握this這個關鍵字,需要緊扣關鍵概念,不要憑空想像這到底是怎麼一回事。
關鍵概念:js中的函數在呼叫的時候,一定,一定,一定會綁定在一個物件上,在分析this關鍵字的時候,一定要知道函數在呼叫的時候這個物件到底是誰? 。
切記:js中函數的呼叫和定義是沒有任何關係的,函數所綁定的物件直到他被呼叫的時候才能知道。
this關鍵字的不確定定是把雙刃劍,一是函數呼叫時的物件不確定性,是js中函數的使用具有很大靈活性,每個物件都可以藉用其他函數來完成功能。二是這也造成了this學習的一些困擾。所以在學習的時候先要理解this關鍵字的優點,然後再去學習造成困擾的地方
先看看第一段程式碼
page 75
//注意只是定义了一个函数,并未调用,这时候函数是没有绑定任何对象function identify() {return this.name.toUpperCase(); }//同上面的函数,但是这个函数内部有点复杂,如果下面的代码看不懂//可以只看上面的函数function speak() {var greeting = "Hello, I'm " + identify.call( this );console.log( greeting ); }var me = { //定义了一个字面量对象 name: "Kyle" };var you = {//定义了一个字面量对象 name: "Reader" };//通过call方式把函数identify分别绑定到两个对象上//这时的this是指向me对象,和you对象 identify.call( me ); // KYLE identify.call( you ); // READER//通过call方式把函数call分别绑定到两个对象上//这时的this是指向me对象,和you对象 speak.call( me ); // Hello, I'm KYLE speak.call( you ); // Hello, I'm READER
#在javascript中定義函數的時候,函數是不屬於任何物件的。這一點非常的關鍵,非常的關鍵,非常的關鍵。這是理解this關鍵字的第一個障礙。
this關鍵字在js函數定義的時候的不確定性使得js函數使用有極大的靈活性,任何物件都可以使用他。
this的綁定和函數定義的位置沒有任何關係,只取決於函數呼叫的方式
.
javascript當一個函數被呼叫的時候,會建立一個活動記錄(有時也稱上下文)。這個記錄包括函數在哪裡被調用,函數的調用方法,傳入的參數。 this就是記錄中的屬性。
這樣在學習javascript關鍵字的首要問題是要解決怎麼知道到函數的呼叫位置
.
#每個js函數在呼叫的時候一定要找到一個對象,綁定
以後才能使用。 這裡是理解了js函數的定義和調用的區別以後需要掌握的一個規模最龐大的概念,在js中一共有四種綁定方式.就我個人來看,綁定規則並不難,難點還是在js的函數作用域的理解
. 尤其是預設綁定
.這個綁定方式有極大的迷惑性。
這個是函數的獨立調用,也就是在一個函數直接調用的時候,似乎是沒有綁定到物件上的,但是根據前面的介紹,js中函數呼叫時必須要綁定到一個物件上。
看下面程式碼 page 83
function foo() { //这是函数的定义位置console.log( this.a ); } var a = 2;//这个变量定义的含义是什么呢?仅仅是赋值给a吗? foo(); // 2 //这是函数的调用位置。为什么会打印出2呢?
很多函數都是這麼呼叫的,照貓畫虎也可以寫出來,但是理解了具體的意義就不一樣了。
foo這個函數定義在全域作用域中(window作用域中),巧合的是他的呼叫也是在全域作用域中,注意這只是巧合,巧合。 那麼foo()呼叫的時候為什麼會印出變數 a的值呢?儘管使用了var這個關鍵字,但分析作用域可以知道,a這個變數實際上是全域變量,說的再明白一點,a實際上是window這個全域物件的一個屬性,2是這個屬性的屬性值。
foo()呼叫的時候是一絲不掛的全裸狀態,只是函數本身,沒有任何修飾符,這時候他也沒有任何函數包裹,處在全域作用域下面,所以foo()裡面的this是指向全域物件的,當要印出this.a的時候,尋找foo()呼叫位置會找到全域作用域,找全域作用域的屬性this.a的時候會印出2這個屬性值。
我們在使用setTimeout,setInterval函數的時候,實際這兩個函數就是一絲不掛的,同樣綁定在window物件上。
函數在呼叫的時候被加入了修飾符。看下面這個程式碼
page 85
function foo() { //定义在全局作用下的函数,仅仅是定义,不是调用位置console.log( this.a ); }var obj = { //定义一个对象 a: 2,foo: foo }; obj.foo(); // 2 给foo()函数找了一个对象,this就指向这个对象了
這是最常見的方式了,如果不寫前面的obj是不是就是上面的預設綁定了?
隱含遺失
常在js程式碼的巢狀回呼函數中看到在外層函數開始的一句
var that=this; //这是什么含义
或許你已經會用了,但理解了其中意義用起來會更加得心應手啊
看下面段代码.这段代码其实以前我也不太理解,问题还是没有彻底领悟js函数定义和调用之间是没有关系的这一点。
page 86
function foo() { //定义了一个函数console.log( this.a ); }var obj = { //定义了一个对象字面量 a: 2,foo: foo //函数作为对对象的属性 };var bar = obj.foo; //把obj对象的函数foo属性赋值给bar变量//这里就是理解这个问题的关键,如果你现在认为调用bar()的时候绑定的对象//是obj那就完全搞错了。这个时候仅仅是把函数foo赋值给了var变量,//并没有把对象也给bar变量,因为这里还不是foo()函数的调用位置,现在//foo函数还没有绑定对象,那么调用bar()的时候对象到底是谁?不知道。//调用的时候才知道。var a = "oops, global"; // 任然是全局对象的属性 bar(); // "oops, global" 这里执行的是默认绑定,this就是去全局对象啦
下面这段代码就是使用var that=this的场景
在使用回调函数的时候要留心。js中函数是一等对象,可以作为另一个函数的参数传入函数。 问题就出在这里了,函数一旦作为实参代替形参的时候,实际也执行了和上面代码一样的赋值过程,实际只是传递了函数本身,原先的对象就没有了。
page 86
function foo() { //定义一个函数console.log( this.a ); }function doFoo(fn) { //fn是形参// 如果函数作为实参传入相当于代码 var fn=obj.foo//和上面一段代码是完全一样的,只是函数本身,并没有绑定任何对象 fn(); // 在这里调用的时候,由于fn只代表foo()函数,被绑定到全局对象上了 }var obj = {a: 2,foo: foo };var a = "oops, global"; // `a` also property on global object doFoo( obj.foo ); // "oops, global"不要被obj.foo迷惑了//没有实际执行函数的调用,此时obj.foo仅仅代表没有绑定任何对象的函数//这个代码块看着眼熟么?这就是javascript中回调函数的样子,当//一个函数作为参数传递进另一个函数的时候,这个参数函数就找不到自己绑定的对象是谁了,//所以就默认绑定到全局对象上了。但是我们既然在一个函数里调用另一个函数,肯定是要用这个函数操作当前的对象,那么既然找不到了,我们就手动给他指定一个对象吧。这就是为什么要使用//var that=this的原因。我觉得理解这个概念,js的功力至少会增加5%
以上是javascript中This用法的實例教程的詳細內容。更多資訊請關注PHP中文網其他相關文章!