JS变量对象

写在前面的话

接触前端到现在大概也有三个多月了,越深入学习越发现自己理解的学到的只是一知半解。于是翻阅了许多大神如简书中的这波能反杀的前端进阶系列博客,终于对JS有了更深的认识。本篇文章主要是对JS中的变量对象做一下总结,希望可以让看到这篇文章的各位对变量对象有更深的理解。此外,文章不足之处还望大家指出,共勉。


执行上下文的创建

在说变量对象这个东东之前,我们需要先明白JavaScript代码的整个执行过程大概是怎么样的。JavaScript代码的整个执行过程中,分为两个阶段,先开始代码编译阶段,然后开始代码执行阶段。在代码编译阶段,编译器会将JS代码翻译成可执行代码,在这个阶段中确定了作用域规则。接下来,开始代码执行阶段,由引擎完成,这个阶段中会调用各种函数,创建执行上下文,然后执行代码。


变量对象出现阶段

在调用一个函数时,会有一个执行上下文被创建。该执行上下文过程的生命周期分为两个阶段:
创建阶段
在此阶段,执行上下文开始创建变量对象,确定作用域链,this的指向等。
代码执行阶段
创建阶段完成后,开始执行代码,在这个时候,才开始变量的赋值,函数的引用,以及执行其他的代码。

变量对象于执行上下文创建阶段生成

简而言之,调用一个函数时,会生成一个执行上下文,这个执行上下文会先创建变量对象,确定作用域链,this指向等,然后执行上下文开始根据作用域链,先对变量对象中各变量赋值,再执行其他可执行代码。


变量对象生成阶段

1、 创建一个arguments对象,即存放该函数中的所有变量的对象。
2、 检查该函数体中的由function关键字声明的函数,若调用函数中有由function声明的函数,则以该函数的函数名为属性名在变量对象中创建一个属性,属性值为该函数内存地址的引用。若以该函数名创建的属性已经存在,该属性值会将原来的覆盖。

//demo01
function fn() {
    function foo() {
        return 1;
    }
}
//在该例中,首先 foo 作为arguments对象属性名存入其中,此时为
// arguments = {foo: dcab****}     
//此处的 "dcab****" 为 "ƒ foo() {return 1;}" 的内存地址

3、 检查该函数体中的由var关键字声明的变量,每检测到一个就将其变量名作为变量对象的属性名存入其中,属性值为该变量值。如果找到var声明变量,则以该变量名为属性在变量对象中创建属性,属性值为undefined。在变量对象中,若var声明的变量名已经存在,则不重复声明,跳过该var声明变量在变量对象中的属性创建。

综上所述:在函数调用创建变量对象过程中,首先检查function函数声明并在变量对象中以其函数名创建属性,属性值为该函数内存地址的引用,若该属性已存在,则属性值会将原来的覆盖。然后才是检查var声明变量,并以其变量名在变量对象中创建属性,属性值为该变量值,若该属性名已存在,属性值指向的是一个函数的内存的引用,则跳过对该var声明变量在变量对象中属性的创建。

//demo02
function fn() {

    console.log(foo);

    console.log(a);

    var a = 4;

    var foo = 5;

    console.log(foo);

     function foo() {
        return 1;
    }
}
fn();
//大家可以尝试写出该实例的输出结果

在demo02中,会有很多读者认为第一个console.log(foo)的输出结果是1或者undefined,但其实不然。按照变量对象生成顺序,首先检查实例中的function声明,并在变量对象中创建属性名为foo的属性,属性值为foo函数在内存中的引用地址。因为函数体中并没有其他的function声明,所以此时才开始检查var声明变量。首先检查到var声明的变量a,因此将 “ a : undefined, ”作为属性存入变量对象中。这里大家可能会有疑问,var a = 4 ,向变量对象中存储的属性应该是 “ a : 4, ” ,为什么会是 undefined 呢? 需要注意的是,现在我们讨论的是变量对象的创建阶段,并不是代码执行阶段,下面有该实例的实际运行顺序(这个仅是我自己所理解的,如发现不足指出请指出,谢谢)。该实例的变量对象现在还没有创建完成,还有一个var声明的foo变量,这个时候由于变量对象中已经有了foo这个属性,因此这个var声明的foo变量就不会再作为属性存放到变量对象中,即直接跳过foo属性的创建。

var声明的变量遇到同名属性时会跳过而不会覆盖,需要满足两个前提
一、处于函数生命周期的创建对象阶段
二、遵循function声明优先

//demo02实际运行顺序
function fn() {
    function foo() {
        return 1;
    }
    var a = undefined;
    //var foo 因为变量对象中已经存在foo属性,所以跳过该属性的创建

    console.log(foo);     //  ƒ foo() {return 1;}
    console.log(a);       //  undefined
    a = 4;               //  执行代码阶段才会将4赋值给a
    foo = 5;             //   重新将5赋值给foo覆盖掉原来的函数地址引用
    console.log(foo);    //   5 
}
fn();

下面再借用波大神的一个demo:

//demo03
function test() {
    console.log(foo);
    console.log(bar);
    var foo = "hello";
    console.log(foo);
    var bar = function(){
      return "hello";
    }
}

如果你理解透了demo2,那么demo3就不是问题,这里就不再赘述了。


变量提升

关于变量提升,先给大家举个例子:
相信大家都懂function声明的函数变量与var声明的函数变量之间的区别,即function声明的变量,在声明之前调用或者在声明之后调用该函数都可以,对输出结果无影响,而var声明的函数变量,若在声明之前调用,则会返回undefined,但很多朋友都是只知其然而不知其所以然。

其实这里的基本原理就是上述的变量对象的创建阶段,会首先检查function声明的变量,因此只要是function关键字声明的变量,位置在调用之前或者在调用之后都无所谓,因为它的优先级在创建变量对象阶段最高,当检测完了function声明的变量后,才开始检查var变量声明的变量,并且在创建阶段对var声明的变量赋值为undefined,如果var声明的变量有值,到达执行代码阶段才会将值赋给该变量。因此如果在var声明函数变量之前调用该变量,会报错undefined。

因此,无论function关键字声明的变量与var声明的变量处于何位置,在该执行上下文的变量创建阶段中,function声明变量总是作为变量对象的属性最先被创建,接着是var声明的变量作为变量对象的属性被创建,这就是变量提升。


JS变量声明的优先级

变量添加到作用域中的方法及优先级从高到低排序如下:

  • 1 var声明变量并赋值
  • 2 function声明
  • 3 formal parameter(形参)
  • 4 JS预定义变量
  • 5 var声明但不赋值
  • 6 函数的外部作用域的其他声明
function fnTest(arguments){
    alert(arguments);//function arguments(){return "hello world"}
    var arguments = 99;
    function arguments(){return "hello world"};
    alert(arguments);//99
}
fnTest(100);

首先,在变量对象创建阶段,function关键字声明变量要比var声明的关键字的优先级要高,文章前面已经说明。但是在同一执行上下文中若var声明变量并赋值,最终则总会覆盖与之同名的函数声明。

对这个优先级顺序不太理解的同学可以移步去寂寞先生的博客中一探究竟。下面我们来看一个实例:

var name = 'world';
(function () {
    if(typeof name === 'undefined'){
        var name = 'Jack';
        console.log('Goodbye'+name);   // GoodbyeJack
    } else {
        console.log('Hello'+name);
    }
})();

这里首先要说一下沿着作用域链查找变量的方法:

  1. 在局部作用域中查找(若找到则返回该变量,否则继续向下找)
  2. 在上一级的作用域中查找,(······);
  3. 在全局作用域中查找(若找不到,则返回 typeError)

此实例中,虽然在函数体外定义了全局变量name,但是函数调用过程中,会优先考虑自身作用域内的变量,而该函数内部有var声明的变量name,因此不再考虑外部全局变量name。在该函数执行上下文中,首先创建变量对象,检查到有var声明的变量,因此将“ name:undefined ”作为属性值先存储在变量对象中。在if条件判断时,name的值其实还是undefined,因为按照执行上下文的顺序,这个时候代码并没有执行到 “ name = Jack ”,所以if条件成立,执行if语句中的代码。


小结

关于变量对象,到这里大概也就介绍完毕了。在实际工作中可能很少会用到这些知识,也就面试时候可能会有一些与之有关简短的代码题,但是个人觉得理解了变量对象对学习JavaScript还是有很大帮助的,会让我们更容易接受其他的一些知识,毕竟每个知识点都不是孤立的,他们之间都有“冥冥之中”的联系~

希望大家能通过本篇文章加深对变量对象的理解,有所收获。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,716评论 2 17
  • 《ijs》速成开发手册3.0 官方用户交流:iApp开发交流(1) 239547050iApp开发交流(2) 10...
    叶染柒丶阅读 5,064评论 0 7
  • 继承 一、混入式继承 二、原型继承 利用原型中的成员可以被和其相关的对象共享这一特性,可以实现继承,这种实现继承的...
    magic_pill阅读 1,048评论 0 3
  • 稳定的感情就像一份稳定的收入 但我喜欢股市的大起大落 要么荣华富贵一生 要么穷困潦倒一世 ...
    挑金扁担的不平衡者阅读 191评论 3 3
  • "一切顺利” 一幅画的产生,对于成熟的大家来说,创作过程该有很多目的性的因素,这个思维过程我体验甚少。这幅小画...
    书书虫阅读 505评论 6 7