进阶教程 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();

复制代码
相关文章
相关标签/搜索