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

資訊專欄INFORMATION COLUMN

JS筆記四:作用域、變量(函數(shù))提升

FuisonDesign / 1376人閱讀

摘要:變量作用域一個(gè)變量的作用域表示這個(gè)變量存在的上下文。在這種情況下,僅僅函數(shù)聲明的函數(shù)體被提升到頂部。雖然我們無需用來修飾形式參數(shù),但是形式參數(shù)的確也是變量,并且被自動(dòng)提升到次高的優(yōu)先級函數(shù)聲明。

關(guān)于作用域,變量提升,函數(shù)提升的個(gè)人理解

參考:

阮一峰的JavaScript參考教程2.7函數(shù)部分

思否上一篇關(guān)于作用域,提升的博客

一篇關(guān)于作用域和提升的個(gè)人博客

MockingBird博客作用域和變量提升

顏海鏡的博客

大部分例子代碼都引用的原文章內(nèi)容,如侵權(quán),聯(lián)系刪除

其中會穿插[函數(shù)聲明的方法],[函數(shù)覆蓋],[函數(shù)是一等公民]的知識點(diǎn)

寫博客的原因是看到兩個(gè)題目

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo);
}
bar();

答案是10!
你是否會疑惑條件語句if(!foo)并不會執(zhí)行,為什么foo會被賦值為10

再來看第二個(gè)例子

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);

答案還是10嗎?顯然不是,alert輸出了1

作用域(scope)

C語言的一個(gè)例子:

#include 
int main() {
    int x = 1;
    printf("%d, ", x); // 1
    if (1) {
        int x = 2;
        printf("%d, ", x); // 2
    }
    printf("%d
", x); // 1
}

程序依次輸出了1,2,1.
C語言中,我們有塊級作用域(block-level scope)。在一個(gè)代碼塊(一對{}花括號括起來的部分)的中變量并不會覆蓋掉代碼塊外面的變量。

JavaScript中的表現(xiàn):

var x = 1;
console.log(x); // 1
if (true) {
    var x = 2;
    console.log(x); // 2
}
console.log(x); // 2

JavaScript有函數(shù)級作用域(function-level scope)。這一點(diǎn)和C家族完全不同。語句塊,如if語言,不創(chuàng)建新的作用域。僅僅函數(shù)能創(chuàng)建新作用域。

在JavaScript中,如果我們需要實(shí)現(xiàn)block-level scope,我們也有一種變通的方式,那就是通過自執(zhí)行函數(shù)創(chuàng)建臨時(shí)作用域:(閉包)

function foo() {
    var x = 1;
    if (x) {
        (function () {
            var x = 2;
            // some other code
        }());
    }
    // x is still 1.
}

上面代碼在if條件塊中創(chuàng)建了一個(gè)閉包,它是一個(gè)立即執(zhí)行函數(shù),所以相當(dāng)于我們又創(chuàng)建了一個(gè)函數(shù)作用域,所以內(nèi)部的x并不會對外部產(chǎn)生影響。

變量作用域
一個(gè)變量的作用域表示這個(gè)變量存在的上下文。它指定了你可以訪問哪些變量以及你是否有權(quán)限訪問某個(gè)變量。

變量作用域分為局部作用域全局作用域

局部變量(處于函數(shù)級別的作用域)

javascript沒有塊級作用域(被花括號包圍的);當(dāng)是,javascript有擁有函數(shù)級別的作用域,也就是說,在一個(gè)函數(shù)內(nèi)定義的變量只能在函數(shù)內(nèi)部訪問或者這個(gè)函數(shù)內(nèi)部的函數(shù)訪問(閉包除外)

不要忘記使用var關(guān)鍵字

如果聲明一個(gè)變量的時(shí)候沒有使用var關(guān)鍵字,那么這個(gè)變量將是一個(gè)全局變量

// If you don"t declare your local variables with the var keyword, they are part of the global scope
var name = "Michael Jackson";
 
function showCelebrityName () {
    console.log (name);
}
 
function showOrdinaryPersonName () {    
    name = "Johnny Evers";
    console.log (name);
}
showCelebrityName (); // Michael Jackson
 
// name is not a local variable, it simply changes the global name variable
showOrdinaryPersonName (); // Johnny Evers
 
// The global variable is now Johnny Evers, not the celebrity name anymore
showCelebrityName (); // Johnny Evers
 
// The solution is to declare your local variable with the var keyword
function showOrdinaryPersonName () {    
    var name = "Johnny Evers"; // Now name is always a local variable and it will not overwrite the global variable
    console.log (name);
}
局部變量優(yōu)先級大于全局變量

如果在全局作用域中什么的變量在局部作用域中再次聲明,那么在局部作用域中調(diào)用這個(gè)變量時(shí),優(yōu)先調(diào)用局部作用域中聲明的變量

var name = "Paul";
 
function users () {
    // Here, the name variable is local and it takes precedence over the same name variable in the global scope
var name = "Jack";
 
// The search for name starts right here inside the function before it attempts to look outside the function in the global scope
console.log (name); 
}
 
users (); // Jack
全局變量

所有在函數(shù)外面聲明的變量都處于全局作用域中。在瀏覽器環(huán)境中,這個(gè)全局作用域就是我們的Window對象(或者整個(gè)HTML文檔)。

每一個(gè)在函數(shù)外部聲明或者定義的變量都是一個(gè)全局對象,所以這個(gè)變量可以在任何地方被使用,例如:

// name and sex is not in any function
var myName = "zhou";
var sex = "male";
 
//他們都處在window對象中
console.log(window.myName); //paul
console.log("sex" in window); //true

如果一個(gè)變量第一次初始化/聲明的時(shí)候沒有使用var關(guān)鍵字,那么他自動(dòng)加入到全局作用域中。

setTimeout中的函數(shù)是在全局作用域中執(zhí)行的

setTimeout中的函數(shù)所處在于全局作用域中,所以函數(shù)中使用this關(guān)鍵字時(shí),這個(gè)this關(guān)鍵字指向的是全局對象(Window):

var Value1 = 200;
var Value2 = 20;
var myObj = {
  Value1 : 10,
  Value2 : 1,
  
  caleculatedIt: function(){
    setTimeout(function(){
      console.log(this.Value1 * this.Value2);
    }, 1000);
  }
}
 
myObj.caleculatedIt(); //4000
提升(Hoisting) 幾個(gè)過程與詞語意義

在說明提升之前,要搞清楚幾個(gè)詞的意義.

作用域中的名字(屬性名)
例如 var a; 中a就是名字或者叫屬性名

聲明
var a;就是變量聲明
function f (){}就是函數(shù)聲明

賦值
a=1;就是賦值

需要注意的是如果這樣寫var b = 2;,那么這句話就是兩個(gè)過程,分別是聲明賦值
等于下面的代碼

var b;//聲明
b = 2;//賦值

function f (){}函數(shù)在聲明時(shí),聲明與賦值都同時(shí)進(jìn)行.

理解函數(shù)是一等公民

參考:
阮一峰:函數(shù)是一等公民
JavaScript 語言將函數(shù)看作一種,與其它值(數(shù)值、字符串、布爾值等等)地位相同。凡是可以使用值的地方,就能使用函數(shù)。

可以把函數(shù)賦值給變量和對象的屬性

可以當(dāng)作參數(shù)傳入其他函數(shù)

可以作為函數(shù)的結(jié)果返回

函數(shù)只是一個(gè)可以執(zhí)行的,此外并無特殊之處。

由于函數(shù)與其他數(shù)據(jù)類型地位平等,所以在 JavaScript 語言中又稱函數(shù)為第一等公民

function add(x, y) {
  return x + y;
}

// 將函數(shù)賦值給一個(gè)變量
var operator = add;

// 將函數(shù)作為參數(shù)和返回值
function a(op){
  return op;
}
a(add)(1, 1)
// 2
函數(shù)聲明會覆蓋變量聲明

因?yàn)槠涫且坏裙?與其他值地位相同,所以函數(shù)聲明會覆蓋變量聲明

如果存在函數(shù)聲明和變量聲明(注意:僅僅是聲明,還沒有被賦值),而且變量名跟函數(shù)名是相同的,那么,它們都會被提示到外部作用域的開頭,但是,函數(shù)的優(yōu)先級更高,所以變量的值會被函數(shù)覆蓋掉。

// Both the variable and the function are named myName
var myName;?
function myName () {
console.log ("Rich");
}
 
// The function declaration overrides the variable name
console.log(typeof myName); // function

但是,如果這個(gè)變量或者函數(shù)其中是賦值了的,那么另外一個(gè)將無法覆蓋它:

// But in this example, the variable assignment overrides the function declaration
var myName = "Richard"; // This is the variable assignment (initialization) that overrides the function declaration.
 
function myName () {
console.log ("Rich");
}
 
console.log(typeof myName); // string

因?yàn)樯厦娴拇a等價(jià)于

var myName;
function myName () {
console.log ("Rich");
}
//上面是提升的區(qū)域
myName= "Richard";//然后再賦值
console.log(typeof myName); // string
變量的提升與函數(shù)的提升

在Javascript中,變量進(jìn)入一個(gè)作用域可以通過下面四種方式:

語言自定義變量:所有的作用域中都存在this和arguments這兩個(gè)默認(rèn)變量

函數(shù)形參:函數(shù)的形參存在函數(shù)作用域中

函數(shù)聲明:function foo() {}

變量定義:var foo

(下面會詳細(xì)的講這四種方式)

在代碼運(yùn)行前,函數(shù)聲明和變量定義通常會被解釋器移動(dòng)到其所在作用域的最頂部

如何理解這句話呢?

function foo() {
    bar();
    var x = 1;
}

上面這段在嗎,被代碼解釋器編譯完后,將變成下面的形式:

function foo() {
    var x;
    bar();
    x = 1;
}

我們注意到,x變量的聲明被移動(dòng)到函數(shù)(自己所在的作用域)的最頂部。然后在bar()后,再對其進(jìn)行賦值,即賦值的位置不變,還是原來的位置.
再來看一個(gè)例子,下面兩段代碼其實(shí)是等價(jià)的:

function foo() {
    if (false) {
        var x = 1;
    }
    return;
    var y = 1;
}
function foo() {
    var x, y;
    if (false) {
        x = 1;
    }
    return;
    y = 1;
}

所以變量的上升(Hoisting)只是其聲明(定義)上升,而變量的賦值并不會上升

我們都知道,創(chuàng)建一個(gè)函數(shù)的方法有兩種,一種是通過函數(shù)聲明function foo(){}
另一種是通過定義一個(gè)變量var foo = function(){} 那這兩種在代碼執(zhí)行上有什么區(qū)別呢?

來看下面的例子:

function test() {
    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function () { // function expression assigned to local variable "foo"
        alert("this won"t run!");
    }
    function bar() { // function declaration, given the name "bar"
        alert("this will run!");
    }
}
test();

在這個(gè)例子中,foo()調(diào)用的時(shí)候報(bào)錯(cuò)了,而bar能夠正常調(diào)用
我們前面說過變量會上升,所以var foo首先會上升到函數(shù)體頂部,然而此時(shí)的foo為undefined,所以執(zhí)行報(bào)錯(cuò)TypeError "foo is not a function"。而對于函數(shù)bar, 函數(shù)本身也是一種變量,所以也存在變量上升的現(xiàn)象,但是它這種聲明方法會上升了整個(gè)函數(shù)(包括聲明和賦值),所以bar()才能夠順利執(zhí)行。

在這種情況下,僅僅函數(shù)聲明的函數(shù)體被提升到頂部。名字“foo”被提升(即聲明變量),但后面的函數(shù)體(即復(fù)制的部分),在執(zhí)行的時(shí)候才被指派。

所以以上代碼相當(dāng)于下面

function test() {
    var foo;
    function bar() { // function declaration, given the name "bar"
    alert("this will run!");
    }
    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    foo = function () { // function expression assigned to local variable "foo"
        alert("this won"t run!");
    }
}
test();

再回到一開始我們提出的兩個(gè)例子,能理解其輸出原理了嗎?

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo);
}
bar();

其實(shí)就是:

var foo = 1;
function bar() {
    var foo;//foo此時(shí)被初始化為undefined
    if (!foo) {//!undefined是true
        foo = 10;
    }
    alert(foo);//輸出的是局部變量foo
}
bar();

那么下面

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);

其實(shí)就是:

var a = 1;
function b() {
    function a() {}
    a = 10;//相當(dāng)于只是覆蓋了在b函數(shù)這個(gè)作用域內(nèi)聲明的a函數(shù)所以這個(gè)a=10只是局部變量,影響不到外面的a
    return;
}
b();
alert(a);

這就是為什么,我們寫代碼的時(shí)候,變量定義總要寫在最前面

四中變量進(jìn)入作用域被提升的順序

以下文字引用自阮一峰的教程函數(shù)一章(2.7)下方評論(需要梯子才能看到評論)
關(guān)于 Hoisting 那部分,有兩點(diǎn)值得說明:


Hoisting 的作用范圍是隨著函數(shù)作用域的。我理解在這里尚未講到函數(shù)作用域,不過可以提一句提醒讀者注意,然后鏈接至作用域的部分進(jìn)一步探討;

“Hoisting 只對 var 聲明的變量有效”,不盡然如此(意思是不完全對)。變量聲明的提升并非 hoisting 的全部,JavaScript 有四種讓聲明在作用域內(nèi)獲得提升的途徑(按優(yōu)先級):

語言定義的聲明,如 this 和 arguments。你不能在作用域內(nèi)重新定義叫做 this 的變量,是因?yàn)?this是語言自動(dòng)定義的聲明,并且它的優(yōu)先級最高,也就是被 Hoisting 到最頂上了,沒人能覆蓋它
形式參數(shù)。雖然我們無需用 var 來修飾形式參數(shù),但是形式參數(shù)的確也是變量,并且被自動(dòng)提升到次高的優(yōu)先級

函數(shù)聲明。除了 var 以外,function declaration 也可以定義新的命名,并且同樣會被 hoisting
至作用域頂部,僅次于前兩者

-最后,是本文提到的常規(guī)變量,也就是 var 聲明的變量

對于上面話的理解:
也就是說優(yōu)先級越高的越?jīng)]法被覆蓋.那么表現(xiàn)在代碼層,提升時(shí)就會轉(zhuǎn)換成下面這樣:
提升時(shí)代碼表現(xiàn)上的排序?yàn)?

1 var a;
2 function f (){};
3 形參
4 this和arguments

這就解釋了在提升時(shí),函數(shù)聲明時(shí)賦值聲明的函數(shù)會覆蓋函數(shù)表達(dá)式方式的聲明(function f (){};)的函數(shù),因?yàn)?strong>函數(shù)表達(dá)式提升了(創(chuàng)建,初始化,賦值都被提升),而賦值時(shí)的函數(shù)只在賦值的時(shí)候才被解析.

var f = function () {
  console.log("1");
}

function f() {
  console.log("2");
}

f() // 1
如果同時(shí)采用function命令和賦值語句聲明同一個(gè)函數(shù),最后總是采用賦值語句的定義。
提升的本質(zhì)

參考
方應(yīng)杭的文章:let
要搞清楚提升的本質(zhì),需要理解 JS 變量的「創(chuàng)建create、初始化initialize 和賦值assign」

有的地方把創(chuàng)建說成是聲明(declare),為了將這個(gè)概念與變量聲明區(qū)別開,我故意不使用聲明這個(gè)字眼。

有的地方把初始化叫做綁定(binding),但我感覺這個(gè)詞不如初始化形象。

我們來看看 var 聲明的「創(chuàng)建、初始化和賦值」過程

假設(shè)有如下代碼:

function fn(){
  var x = 1
  var y = 2
}
fn()

在執(zhí)行 fn 時(shí),會有以下過程(不完全):

進(jìn)入 fn,為 fn 創(chuàng)建一個(gè)環(huán)境。

找到 fn 中所有用 var 聲明的變量,在這個(gè)環(huán)境中「創(chuàng)建」這些變量(即 x 和 y)。

將這些變量「初始化」為 undefined。

開始執(zhí)行代碼

x = 1 將 x 變量「賦值」為 1

y = 2 將 y 變量「賦值」為 2

也就是說 var 聲明會在代碼執(zhí)行之前就將「創(chuàng)建變量,并將其初始化為 undefined」。

這就解釋了為什么在 var x = 1 之前 console.log(x) 會得到 undefined。

接下來來看 function 聲明的「創(chuàng)建、初始化和賦值」過程

假設(shè)代碼如下:

fn2()

function fn2(){
  console.log(2)
}

JS 引擎會有一下過程:

找到所有用 function

聲明的變量,在環(huán)境中「創(chuàng)建」這些變量。

將這些變量「初始化」并「賦值」為 function(){console.log(2) }。(三步全部都被提升)

開始執(zhí)行代碼 fn2()

也就是說 function 聲明會在代碼執(zhí)行之前就「創(chuàng)建、初始化并賦值」。 (三步全部都被提升)

接下來看 let 聲明的「創(chuàng)建、初始化和賦值」過程

假設(shè)代碼如下:

{
  let x = 1
  x = 2
}

我們只看 {} 里面的過程:

找到所有用 let 聲明的變量,在環(huán)境中「創(chuàng)建」這些變量
開始執(zhí)行代碼(注意現(xiàn)在還沒有初始化)
執(zhí)行 x = 1,將 x 「初始化」為 1(這并不是一次賦值,如果代碼是 let x,就將 x 初始化為 undefined)
執(zhí)行 x = 2,對 x 進(jìn)行「賦值」
這就解釋了為什么在 let x 之前使用 x 會報(bào)錯(cuò):

let x = "global"
{
  console.log(x) // Uncaught ReferenceError: x is not defined
  let x = 1
}

原因有兩個(gè)

console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
執(zhí)行 log 時(shí) x 還沒「初始化」,所以不能使用(也就是所謂的暫時(shí)死區(qū))
看到這里,你應(yīng)該明白了 let 到底有沒有提升:

**let 的「創(chuàng)建」過程被提升了,但是初始化沒有提升。
var 的「創(chuàng)建」和「初始化」都被提升了。
function 的「創(chuàng)建」「初始化」和「賦值」都被提升了。**

最后看 const,其實(shí) const 和 let 只有一個(gè)區(qū)別,那就是 const 只有「創(chuàng)建」和「初始化」,沒有「賦值」過程。

這四種聲明,用下圖就可以快速理解:

所謂暫時(shí)死區(qū),就是不能在初始化之前,使用變量。

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/94751.html

相關(guān)文章

  • You Don't Know Js 閱讀筆記

    摘要:回調(diào)傳遞函數(shù)是將函數(shù)當(dāng)做值并作為參數(shù)傳遞給函數(shù)。這個(gè)例子中就是因?yàn)槭录壎C(jī)制中的傳入了回調(diào)函數(shù),產(chǎn)生了閉包,引用著所在的作用域,所以此處的數(shù)據(jù)無法從內(nèi)存中釋放。 javascript作用域 一門語言需要一套設(shè)計(jì)良好的規(guī)則來存儲變量,并且之后可以方便的找到這些變量,這逃規(guī)則被稱為作用域。 這也意味著當(dāng)我們訪問一個(gè)變量的時(shí)候,決定這個(gè)變量能否訪問到的依據(jù)就是這個(gè)作用域。 一、詞法作用域 ...

    wanglu1209 評論0 收藏0
  • 《你不知道的javascript》筆記_作用與閉包

    摘要:建筑的頂層代表全局作用域。實(shí)際的塊級作用域遠(yuǎn)不止如此塊級作用域函數(shù)作用域早期盛行的立即執(zhí)行函數(shù)就是為了形成塊級作用域,不污染全局。這便是閉包的特點(diǎn)吧經(jīng)典面試題下面的代碼輸出內(nèi)容答案個(gè)如何處理能夠輸出閉包方式方式下一篇你不知道的筆記 下一篇:《你不知道的javascript》筆記_this 寫在前面 這一系列的筆記是在《javascript高級程序設(shè)計(jì)》讀書筆記系列的升華版本,旨在將零碎...

    galaxy_robot 評論0 收藏0
  • 《高性能JavaScript》讀書筆記

    摘要:除此以外,讓元素脫離文檔流也是一個(gè)很好的方法。因?yàn)樵匾坏┟撾x文檔流,它對其他元素的影響幾乎為零,性能的損耗就能夠有效局限于一個(gè)較小的范圍。講完重排與重繪,往元素上綁定事件也是引起性能問題的元兇。高性能這本書非常精致,內(nèi)容也非常豐富。 showImg(https://segmentfault.com/img/bVJgbt?w=600&h=784); 入手《高性能JavaScript》一...

    W_BinaryTree 評論0 收藏0
  • ES6學(xué)習(xí)筆記--塊級綁定

    摘要:對比常量聲明與聲明常量聲明與聲明,都是塊級聲明。最后一點(diǎn)全局塊級綁定與不同于的另一個(gè)方面是在全局作用域上的表現(xiàn)。塊級綁定新的最佳實(shí)踐在的發(fā)展階段,被廣泛認(rèn)可的變量聲明方式是默認(rèn)情況下應(yīng)當(dāng)使用而不是。總結(jié)與塊級綁定將詞法作用域引入。 var變量與變量提升 使用var關(guān)鍵字聲明的變量,無論其實(shí)際聲明位置在何處,都會被視為聲明于所在函數(shù)的頂部(如果聲明不在任意函數(shù)內(nèi),則被視為在全局作用域的頂...

    wangshijun 評論0 收藏0
  • JavaScript MVC 學(xué)習(xí)筆記)類的使用(下)

    摘要:基于函數(shù)進(jìn)行調(diào)用的,用來確保函數(shù)是在指定的值所在的上下文中調(diào)用的。添加私有函數(shù)目前上面為類庫添加的屬性都是公開的,可以被隨時(shí)修改。以基于的富應(yīng)用開發(fā)為主要學(xué)習(xí)資料。 控制類庫的作用域 在類和實(shí)例中都添加proxy函數(shù),可以在事件處理程序之外處理函數(shù)的時(shí)候保持類的作用域。下面是不用proxy的辦法: var Class = function(parent){ var klas...

    Rango 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<