進階教程 2. 變量提高

這是我參與8月更文挑戰的第10天,活動詳情查看:8月更文挑戰javascript

1、變量提高(預解釋)定義

  • 在js代碼執行以前,對全部聲明的變量 進行提早帶var和帶function變量進行提早聲明和定義(賦值);
  • 對於帶var的進行提早聲明,不賦值(變量聲明時不賦值,變量的默認值是undefined),只是瀏覽器知道有這麼個變量。
  • 對於帶function的進行提早聲明 而且賦值爲函數定義自己

變量提高結束後,代碼纔會從上到下執行。前端

  • 當執行到var 變量 = 值;時,此時纔會對變量進行賦值,即在此以後變量才表明這個值自己,在此以前是undefined;
  • 而當執行到function 函數名... 時,跳過,由於在變量提高階段就已經完成了對變量的賦值過程。
console.log(num); // undefined 由於代碼執行到這裏時,num只是通過了變量提高階段的聲明,可是沒有完成賦值,因此是undefined;

var num = 100; // 代碼執行過這一行後完成了賦值,因此num是100
console.log(num);  // 100

console.log(fe); // 函數體自己,由於fe這個變量在函數執行時就已經賦值完成了,因此不管在函數聲明前仍是後使用都是函數自己
fe(); // 由於fe在變量提高階段就已經完成了賦值,因此fe是一個函數,因此能夠成功執行
function fe() {
   console.log('We are Front-end Engineer');

複製代碼

2、變量提高和 js 運行機制

console.log(num); // undefined

var num = 12; // 賦值操做,只在執行到這一行時,num才被賦值爲12;
var obj = {
   name: "江外",
   age: '10'
};
console.log(xy);
function xy(str) {
   var age = 10;
   console.log(str, age);
}
xy('江外');

複製代碼

2.1 js的運行機制及原理:

2.1.1 js運行的環境(棧內存):做用域

做用域是js運行的環境,另一個功能是保存基本數據類型。在js中做用域分爲:java

+全局做用域 :當頁面打開時,首先造成一個全局做用域,執行全局中的代碼,全局做用域是window;
+私有做用域(函數做用域)當函數執行時,會造成一個函數做用域,這個做用域用來保存函數中的基本數據類型同時執行函數代碼;
+塊級做用域(相似私有做用域 ES6)
複製代碼

2.1.2 js運行過程

  • 在js代碼執行前,瀏覽器會開闢一個全局做用域,而後執行變量提高,完成變量提高操做後,代碼開始從上到下開始執行;
  • 當執行時,若是遇到基本數據類型,就在做用域中存儲該基本數據類型;
  • 若是遇到引用數據類型,則瀏覽器會再次分配一個堆內存,而後把引用數據類型的內容存儲到堆內存中,接下來再把這個堆內存的地址賦值給變量(此時這個地址是存儲在做用域內存中的);
  • 遇到函數執行時,會經歷如下幾步:
    1. 瀏覽器開闢一個私有做用域;
    2. 形參賦值,把執行時的實參賦值給函數形參變量;
    3. 私有做用域中變量提高
    4. 函數代碼從上到下執行

2.1.3 私有變量和全局變量

  • 全局變量:在全局做用域中聲明的變量
  • 私有變量:函數的形參以及在函數私有做用域中聲明的變量

2.1.5 預解釋只發生在當前做用域,若是函數不執行時,函數中的變量不會進行變量提高。

function fn() {
   var num = 13;
}
console.log(num); // 報錯:由於預解釋發生在當前做用域中,而當前做用域沒有num的變量,num是fn的私有變量。

複製代碼

2.2 重複聲明的問題

2.2.1 同名變量只會聲明一次,表明的值就是最後一次的值

var num;
var num;
var num; // 這些語句沒有賦值操做,當代碼執行時會略過
var num = 100; // num雖然var了4次,可是並不會聲明4次,只會聲明一次,同時只有這一次纔會將num的值賦值成100;

function fn() {
   console.log(1)
}
function fn() {
   console.log(2)
}
function fn() {
   console.log(3)
}
fn(); // 3
複製代碼

2.2.2 變量提高時,function的優先級高於普通變量

console.log(fe); // 函數體
function fe() { // 當代碼執行到這裏時,直接忽略,由於函數變量賦值已經在變量
   console.log('我是來自江外的FE');
}
var fe = 123; // 代碼執行到這一行時,將變量fe的值修改成123
複製代碼

2.2.3 若是變量名和函數名同名

若是變量名和函數名同名,在執行到變量的賦值語句以前時,這個名字表明函數,可是當執行過變量賦值語句後,變量標識這個新值瀏覽器

function fe() {
   console.log('FE')
}
var fe = 1;
// fe(); // 報錯,由於執行到這裏的時候fe再也不表明一個函數了,而是一個數字
複製代碼

2.3 變量提高的細節問題

2.3.1 等號右側的不會進行變量提高,即便右側是函數也不會進行變量提高

// fn(); // 報錯,
var fn = function () {
   console.log('來自等號右側的你');
};
// console.log(x1); // 報錯:x1 is not defined
// console.log(x); // undefined
var x = function x1() {
   console.log(x1);
};

複製代碼

2.3.2 條件語句中的變量無論條件成立與否都會參與當前做用域中的變量提高;

console.log(n); // undefined
if (NaN === Number('I Love programming')) {
   var n = 1;
}
console.log(n); // undefined,之內條件不成立,因此賦值語句沒執行,因此n仍然是undefined
複製代碼

2.3.3 函數中,return 下面的代碼雖然不執行,但仍會進行函數做用域中的變量提高;

function add(a, b) {
   console.log(n); // undefined
   fe(); // 執行了
   return a + b;
   var n = 123;
   function fe() {
      console.log('前端工程師從入門到刪庫跑路')
   }
}
add();
複製代碼

2.3.4 函數的返回值不參與變量提高; return右邊的不會參與變量提高

function minus(a, b) {
   console.log(foo);
   return function foo() {
      console.log('函數的返回值不參與變量提高')
   }
}
minus();
複製代碼

2.4 帶var和不帶var的區別

2.4.1 在全局中

用var和function聲明的變量,也至關於給window上添加一個同名屬性markdown

  • 全局做用域是一個內置對象 window

console.log(window);前端工程師

var num = 2019;
console.log(window.num); // 2019
window.num = 2020;
console.log(num); // 2020 num和window.num是綁定在一塊兒的
console.log('num' in window); // in 運算符 檢測對象是否有某個屬性,有返回true,不然false


function fe() {
   console.log('FE')
}
window.fe();
fe();

複製代碼

2.5 不帶var 的不會參與變量提高

console.log(a); // undefined
var a = 1;

console.log(b); // 報錯:
b = 2; // 不帶var 不會參與變量提高,因此不會提早聲明和賦值
console.log(b);

複製代碼

3、做用域鏈

js中做用域函數

  • 全局做用域
  • 私有做用域
  • 塊級做用域
function fn() {
   console.log(n);
}
fn(); // 15

function fn2() {
   console.log(x)
}
fn3();


function fn3() {
   x = 16;
}
fn2();
複製代碼

3.1 做用域鏈:變量的查找機制:

當在做用域中查找一個變量的時候,先看當前做用域中是否聲明過這個變量,若是聲明過,就使用這個變量,若是沒有生命過,那麼就去上級做用域(上級做用域就是函數聲明時所在的做用域)查找,找到就使用,若是沒有就一直向上查找,一直找到window爲止,若是本次使用變量是賦值,那麼就至關於給window上面增長一個屬性,若是是引用變量,就會報錯;post

function fe() { // fe 是在全局中定義的,因此fe的上級做用域就是全局做用域
  var n = 200;
  return function f() { // 這個function就是在fe的做用域中定義的,因此該函數的上級做用域就是fe的做用域
    console.log(n) // 200
  }
}
var fn = fe();
fn();

複製代碼

3.2 上級做用域的肯定:

  • 如何查找上級做用域,就看這個函數是在哪裏定義的。
function fe() { // fe 是在全局中定義的,因此fe的上級做用域就是全局做用域
   var n = 200;
   return function f() { // 這個function就是在fe的做用域中定義的,因此該函數的上級做用域就是fe的做用域
      console.log(n) // 200
   }
}
var fn = fe();
fn();

複製代碼
相關文章
相關標籤/搜索