摘要:再來看書中的例子變量引用了構(gòu)造函數(shù)新生成的變量,所以相當于與。而全局變量是全局對象的屬性。延伸知識點最近學(xué),發(fā)現(xiàn)這幾個知識點最好形成思維導(dǎo)圖來理解作用域變量聲明提升全局對象全局變量引用上下文構(gòu)造函數(shù)原型鏈與原型。
全局變量 可以先看W3C:JavaScript 全局對象、MDN:this 全局變量,W3C里說得很清楚(JavaScript Window - 瀏覽器對象模型):
alert(window.eval===eval); // true alert(window.Object===Object); // true alert(window.Math===Math); // true alert(window.document===document); // true // 我們一開始就使用那些函數(shù)方法,屬性都是window對象的屬性嗎?
瀏覽器對象模型BOM (Browser Object Model) 使 JavaScript 有能力與瀏覽器“對話”。
還有一個DOM(文檔對象模型,這是對HTML進行操作的根本)這里略過。
JavaScript就是那么奇葩,我們在函數(shù)外部聲明的所有全局變量,其實都是全局對象的屬性!JavaScript除了數(shù)值、布爾、字符串、null、undefined之外,都是對象(可以添加屬性)!// 預(yù)定義的全局變量、函數(shù)(方法)都是window對象的屬性 alert(typeof window.Number) // function alert(typeof window.Math) // object alert(typeof window.Math.random) // function // 更改全局變量 NaN = 11; eval = 22; Object = 33; Math = 44; console.log(window.Math); // 44 console.log(typeof window.Math) // number 全局對象被改寫 alert(NaN); // NaN alert(eval); // 22 alert(Object); // 33 alert(Math); // 44 // 這里可能有一個誤區(qū):全局對象不是window嗎? // 實際上:window也是另外一個全局對象,但是前提是運行在瀏覽器端 // window全局對象提供了與當前窗口、頁面有關(guān)的諸多屬性與方法。 // 除了這些與瀏覽器有關(guān)的全局屬性和方法,window對象還封裝了JS全局對象,并向外暴露JS全局對象的屬性與接口看,這些預(yù)定義好的全局對象、方法我們都可以直接改寫(當然沒人去這么干),當然有些不能改寫(NaN),不同瀏覽器也有不同支持。 具體可以看這兩位大神的總結(jié),相當好:JavaScript中的全局對象介紹 淺析JavaScript中兩種類型的全局對象/函數(shù) 這里開始以this的應(yīng)用為主,雜揉了一些知識。因為我第一次寫這類感想文,編排不好后續(xù)會慢慢更改。 有關(guān)全局變量的測試:
var a = 1; alert(this.a); // 1 alert(this); // "[object Window]"
這里定義了一個全局變量a,之后調(diào)用this.a和this。結(jié)果大家看到了。全局變量a的作用域是整個全局對象內(nèi)可見,或者說變量a是全局對象的屬性。不信?看下面:
var woshiSB = 1; console.log(this); var write = ""; for (var i in this) { write += i+"
" } document.write(write);
看到了下面那個了?沒錯,還記得for-in語句干啥的嘛?遍歷一個對象里的可枚舉屬性(圖中只截取了部分全局對象(變量)),之后我們發(fā)現(xiàn)了了它woshiSB,這可以說在外部環(huán)境、全局上下文環(huán)境中的全局變量是全局對象的屬性。反過來,我們也可以通過在全局對象里創(chuàng)建一個全局變量。且,如果能確定上下文(在任何函數(shù)體外部),滿足這個前提條件,這個this指的就是全局對象了。且,我們可以:
console.log(this.document === document); // true console.log(this.Number===Number); // true // 在瀏覽器中,全局對象為 window 對象: console.log(this === window); // true this.a = 37; // 此時this引用指向全局對象,所以a是全局對象的屬性 console.log(window.a); // 37 window.b = 38; // 同樣的,全局對象window下的屬性是全局變量 console.log(this.b); // 38 this.x = 1; alert(this.x===window.x) // truethis引用的幾種情況(依據(jù)上下文) 第一種:
全局上下文(引用自MDN) 在全局運行上下文中(在任何函數(shù)體外部),this指代全局對象,無論是否在嚴格模式下。
console.log(this.document === document); // true // 在瀏覽器中,全局對象為 window 對象: console.log(this === window); // true this.a = 37; console.log(window.a); // 37關(guān)于嚴格模式,請看真?究極大神 阮一峰:Javascript 嚴格模式詳解
function f1(){ return this; } f1() === window; // true
不是在嚴格模式下執(zhí)行,this的值不會在函數(shù)執(zhí)行時被設(shè)置,此時的this的值會默認設(shè)置為全局對象。
這里提醒一下:嚴格的說,this全稱叫this引用。注意引用二字。
function f2(){ "use strict"; // 這里是嚴格模式 return this; } f2() === undefined; // true
在嚴格模式下,如果this未被執(zhí)行的上下文環(huán)境定義,那么它將會默認為undefined。
所謂上下文,加幾個字理解,想到語文里的“聯(lián)系上下文”了嗎?對,this引用所處的環(huán)境,或者說它位于的作用域。
函數(shù)上下文 在函數(shù)內(nèi)部,this的值取決于函數(shù)是如何調(diào)用的。
包括引用的文章也提到:
如果這個this屬于某個function,那么this指代的就是調(diào)用該function的對象。若這種情況下function只是一個普通的函數(shù),而不是某個類的方法,那么this的指代存在兩種可能: 1.在ECMAScript 3標準,以及ECMAScript 5標準的非嚴格模式下,this指代全局對象。 2.在ECMAScript 5標準的嚴格模式下,this指代undefined。
函數(shù)上下文 —— 對象方法中的this:
var obj = { x:"x", fn:function () { return this.x; } }; console.log(obj.fn())
此時,obj.fn()中的this引用將會指向obj這個全局對象,也就是obj.x
var obj = {x:"x"}; function test(obj) { return this.x; }; obj.fn = test; alert(obj.fn());
在何處或者如何定義調(diào)用函數(shù)完全不會影響到this的行為。我們在定義obj的時候定義了一個obj.fn()方法。但是,我們也可以首先定義函數(shù)然后再將其附屬到o.f。這樣做this的行為也一致。
來看另一個例子:
var f = function (x) { this.y = x+1; }; var a = {y:10,op:f}; var b= {y:20,increment:f}; a.op(100); // 通過a來調(diào)用f,this引用指向a引用的對象 alert(a.y); // 101 b.increment(43); // 通過b來調(diào)用,this引用指向b引用的對象 alert(b.y); // 44
f引用的匿名函數(shù)里的this引用指向調(diào)用它的對象。
函數(shù)上下文 —— 構(gòu)造函數(shù)中的this:
我們來看CDN的例子(改寫過):
function c(){ this.a = 37; }; c.prototype.test = "test"; var o = new c(); o.sb = "Sb"; // o也是對象 console.log(o.a); // logs 37 console.log(o.__proto__===c.prototype); // true console.log(o.sb); // "Sb" Object.prototype.op = "op"; console.log(c.prototype.__proto__===Object.prototype) // true console.log(Object.prototype.__proto__); // null console.log(Object.prototype.constructor===Object); // true
看到這的的默認你已經(jīng)懂了構(gòu)造函數(shù)大部分哈。(我們先讀一遍,對,首先要看懂。)
聲明一個c函數(shù),同時內(nèi)建一個this引用,當然我們并不知道這個引用指向誰。將c函數(shù)作為構(gòu)造函數(shù)調(diào)用,(此時:構(gòu)造函數(shù)會隱式生成一個對象,對象內(nèi)建的隱式鏈接指向構(gòu)造函數(shù)的prototype對象),這個新對象的引用賦值給全局變量o,注意是引用賦值,這個變量同時也是對象,Sb那里也看到了。現(xiàn)在你腦子里是不是生成了一副引用指向圖了呢?把它畫出來你就都知道了。
變量o引用了新對象,則構(gòu)造函數(shù)里的this引用就是指向o,這里展現(xiàn)的是原型鏈,關(guān)于原型鏈我還不夠了解后續(xù)會繼續(xù)學(xué)習(xí);
再來看另一個:
function c2(){ this.a = 37; return {a:38}; }; o = new c2(); console.log(o.a); // logs 38
在最后的例子中(C2),因為在調(diào)用構(gòu)造函數(shù)的過程中,手動的設(shè)置了返回對象,與this綁定的默認對象被取消(本質(zhì)上這使得語句this.a = 37成了“僵尸”代碼,實際上并不是真正的“僵尸”,這條語句執(zhí)行了但是對于外部沒有任何影響,因此完全可以忽略它)。———————————— 引自CDN
其實我對CDN的解釋也不太懂,可以認為,重新改變o引用的對象,則也改變了this引用的對象。則this.a肯定指向新對象。
再來看書中的例子:
var Point = function (x,y) { this.x = x; this.y = y; }; var p = new Point(4,-5); var q = Point(3,8); console.log(typeof q); // "undefined"
p變量引用了構(gòu)造函數(shù)新生成的變量,this.x、this.y所以相當于p.x與p.y。調(diào)用構(gòu)造函數(shù),傳參后,就賦值等等。
而變量q,注意這里只是將Point當做普通函數(shù)調(diào)用而已,執(zhí)行函數(shù)主體后,也沒有返回值。So,q的值類型是undefined。
再來看另一個例子:
var obj = { x:3, doit:function () { console.log("method is called."+this.x); } }; // 1 var fn = obj.doit; // 將obj.doit引用的Function對象賦值給全劇變量 fn(); // "method is called.undefined" // 2 var x = 5; fn(); // "method is called.5" // 3 var obj2 = {x:4,doit2:fn}; obj2.doit2(); // "method is called.4"
全局變量obj引用對象,內(nèi)含obj.x和一個方法,方法內(nèi)存在this引用。
第一次調(diào)用:將對象內(nèi)部doit方法賦值給全部變量fn,此時調(diào)用fn。那么我想問,是誰調(diào)用fn呢?是全局對象。而全局變量是全局對象的屬性。也就是說上下文內(nèi)存在的全局變量都對全局對象可見,作用域嘛。此時,既然全局對象調(diào)用函數(shù)方法,則this引用當然指向全局對象。可是,全局對象并沒有屬性x(或者說沒有全局變量x)所以是undefined。第二次調(diào)用//2:就是反證第一次的,說明this引用的確引用了全局對象,或者我們看這個:
var test = { z:"test-z", fn:function () { console.log(this.z) } }; window.fun = test.fn; // 全局對象的fun方法 var z = "var z"; // 全局變量z window.fun(); // result:"var z" this引用指向全局對象 var z1 = "var z1"; // 全局變量z1 test.z1 = "test.z1"; // 屬性z1 test.fn2 = function () {console.log(this.z1)} test.fn2(); // result:"test.z1" this引用指向test對象
所以當?shù)谌握{(diào)用時,doit2引用的Funcion對象內(nèi)部的this引用指向的是obj2對象。相當于obj2.x
這里要提一下之后要學(xué)到的apply和call方法,它們作用就是顯式的指定this引用要引用的對象,或者說顯式指定接收方對象。嵌套一個來試試:
var x = "var x"; var fn = function () {alert(this.x)}; var obj = { x:"obj.x", fn:function () {alert(this.x)}, obj2:{ x:"obj2.x", fn2:function () {alert(this.x)} } }; // 直接調(diào)用 obj.obj2.fn2(); // obj2.x obj.fn(); // obj.x fn(); // var z哪怕跨級調(diào)用,由于搜索都是按作用域由內(nèi)之外,所以引用的是最近的對象。所以這應(yīng)該是MDN里那句話的意思了。
類似的,this 的綁定只受最靠近的成員引用的影響
再來看一個變形:
var obj = { x:"obj.x", doit:function () { console.log("doit is called."+this.x); // 這里的this.x:obj.x this.test.doit2(); // 這里的this.x:"test.x" // "true" }, test:{ x:"test.x", doit2:function () { var x = "doit2-x"; console.log("doit2 is called."+this.x+" | "+this); // 這里的this.x:"test.x" console.log(this===window.obj.test && this===obj.test); // "true" this引用指向嵌套對象:test } } }; obj.doit(); // this.x: "obj.x" var x = "var z"; window.obj.test.doit2(); // this.x:"test.x"最后提一下原型鏈中的this引用:
var o = { f : function(){ return this.a + this.b; } }; var p = Object.create(o); p.a = 1; p.b = 4; console.log(p.f()); // 5 alert(p.__proto__===o); // true
延伸知識點: 最近學(xué)JS,發(fā)現(xiàn)這幾個知識點最好形成思維導(dǎo)圖來理解:作用域、變量聲明提升、全局對象全局變量、this引用(上下文)、new構(gòu)造函數(shù)、原型鏈與原型......,continue learning。如果該方法存在于一個對象的原型鏈上,那么this指向的是調(diào)用這個方法的對象,表現(xiàn)得好像是這個方法就存在于這個對象上一樣。
在這個例子中,對象p沒有屬于它自己的f屬性,它的f屬性繼承自它的原型。但是這對于最終在o中找到f屬性的查找過程來說沒有關(guān)系;查找過程首先從p.f的引用開始,所以函數(shù)中的this指向p。也就是說,因為f是作為p的方法調(diào)用的,所以它的this指向了p。這是JavaScript的原型繼承中的一個有趣的特性。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/82967.html
摘要:主要聊聊局部變量作用域變量聲明提升作用域中有以下兩種作用域全局作用域函數(shù)作用域全局作用域是函數(shù)之外最外層代碼的作用域。相對于全局作用域,可以稱之為局部作用域相對于全局變量可以將其成為局部變量。然而,這里的是在下一行進行聲明的局部變量。 主要聊聊局部變量、作用域、變量聲明提升 作用域 JavaScript中有以下兩種作用域 全局作用域 函數(shù)作用域 全局作用域是函數(shù)之外(最外層代碼)的...
摘要:全局執(zhí)行環(huán)境的變量對象始終是作用域鏈中的最后一個變量對象。綜上,每個函數(shù)對應(yīng)一個執(zhí)行環(huán)境,每個執(zhí)行環(huán)境對應(yīng)一個變量對象,而多個變量對象構(gòu)成了作用域鏈,如果當前執(zhí)行環(huán)境是函數(shù),那么其活動對象在作用域鏈的前端。 1.幾個概念 先說幾個概念:函數(shù)、執(zhí)行環(huán)境、變量對象、作用域鏈、活動對象。這幾個東東之間有什么關(guān)系呢,往下看~ 函數(shù) 函數(shù)大家都知道,我想說的是,js中,在函數(shù)內(nèi)部有兩個特殊...
摘要:作用域和閉包是最重要的概念之一,想要進一步學(xué)習(xí),就必須理解作用域和閉包的工作原理。全局和局部作用域的關(guān)系在函數(shù)體內(nèi),局部變量的優(yōu)先級高于同名的全局變量。作用域鏈的用途,是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。 作用域和閉包是 JavaScript 最重要的概念之一,想要進一步學(xué)習(xí) JavaScript,就必須理解 JavaScript 作用域和閉包的工作原理。 作用域 任何...
摘要:函數(shù)的作用域也可被分為全局作用域和局部作用域函數(shù)作用域,被定義在指定函數(shù)內(nèi)部的函數(shù)被稱為局部函數(shù)或內(nèi)部函數(shù)。 作用域 變量和函數(shù)都有作用域,作用域就是變量和函數(shù)可被訪問的范圍,控制著變量和函數(shù)的可見性和生命周期(生命周期指一個事物開始到結(jié)束中間那一段時間)變量的作用域可被分為全局作用域和局部作用域(函數(shù)作用域),如果變量是被定義在全局作用域的話,在JavaScript代碼中的任何位置都...
摘要:一旦函數(shù)執(zhí)行完成,其就會從作用域鏈頂部移除,并且執(zhí)行權(quán)會返回到函數(shù)。攀爬作用域鏈當不同執(zhí)行上下文之間存在變量命名沖突,可以通過攀爬作用域鏈解決從頂部到底部。 一、作用域 在 JavaScript 中, 作用域(scope,或譯有效范圍)就是變量和函數(shù)的可訪問范圍,即作用域控制著變量和函數(shù)的可見性和生命周期 二、全局/局部作用域 2.1 全局作用域(Global Scope) (1)不...
閱讀 2484·2023-04-25 19:24
閱讀 1700·2021-11-11 16:54
閱讀 2833·2021-11-08 13:19
閱讀 3547·2021-10-25 09:45
閱讀 2552·2021-09-13 10:24
閱讀 3276·2021-09-07 10:15
閱讀 4014·2021-09-07 10:14
閱讀 2950·2019-08-30 15:56