作用域

本文不进行系统的讲解,只枚举及个特殊的例子。
例1

<script>
    var str1 = "hello";
    var str2 = "world";

    function t1() {
        console.log(str1);
        console.log(str2);

        var str2 = "toby";
        console.log(str2);
    }

    //这里会输出什么?
    t1();

</script>

结果:

  • hello
  • undefined
  • tbody
    你一定奇怪为什么是undefined,因为根据函数ti来说,他是有自己的str2变量的,只不过声明在试用之后,所以是undefined。
    例2
<script>
    function t(userName) {
        console.log(userName);//这里输出什么?

        function userName() {
            console.log('tom');
        }
    }
    t('toby');
</script>

你一定以为这里输出的是tbody,但实际处出结果是:

 function userName() {
            console.log('tom');
        }

执行t(‘toby’)的时候,会开始两个阶段,一个是分析阶段,分析完就到执行阶段

分析阶段:

函数运行的瞬间,会生成一个Active Object对象(以下简称AO对象),一个函数作用域内能找到的所有变量,都在AO上,此时用代码表示为: t.AO = {}

分析参数: 接收参数,以参数名为属性,参数值为属性值,因为没有参数,因此分析结果用代码表示为: t.AO = {userName : toby}

分析var声明: t函数内没有var声明,略过

分析函数声明: 这个函数声明有个特点,AO上如果有与函数名同名的属性,则会被此函数覆盖,因为函数在JS领域,也是变量的一种类型,因此用代码表示为: t.AO = { userName : function userName() {console.log(‘tom’);}}

执行阶段:

执行t(‘toby’)的时候,当执行到console.log(userName)时,就调用t.AO.userName,所以,最后的输出结果是function userName() {console.log(‘tom’);}
所以重点就在于,同一域内,同名的函数和变量,函数优先,也就是函数会覆盖变量。
例3:

<script>
    function t(userName) {
        console.log(userName);//这里输出什么?

        var userName = function () {
            console.log('tom');
        }
        console.log(userName);//这里输出什么
    }
    t('toby');
</script>

结果:

  • tbody
  • function () {
    console.log(‘tom’);
    }

分析阶段:

创建AO对象,t.AO = {}

分析参数: t.AO = {userName : toby}

分析var声明: 在AO上,形成一个属性,以var的变量名为属性名,值为undefined,(因为是先分析,后执行,这只是词法分析阶段,并不是执行阶段,分析阶段值都是undefined,如果执行阶段有赋值操作,那值会按照正常赋值改变),也就是说代码应该表示为:t.AO = {userName : undefined},但是还有另外一个原则,那就是如果AO有已经有同名属性,则不影响(也就是什么事都不做),由于分析参数时,AO上已经有userName这个属性了,所以按照这个原则,此时什么事都不做,也就是说,此时按照分析参数时的结果t.AO = {userName : toby}

分析函数声明: 此时没有函数声明,略过

执行阶段:

调用t.AO.userName,所以,最后的输出结果是toby

例4:

<script>
    t();
    t2();

    function t() {
        console.log('toby');//这里会输出什么?
    }

    var t2 = function () {
        console.log('hello toby');//这里会输出什么?
    };
</script>

结果:tbody 报错。因为第一个已解析的时候已经将整个函数提升到顶部,但是第二个相当于变量,只有声明,没有定义。
例5:

<script>
    function t(userName) {
        console.log(userName);//这里输出什么?
        function userName() {
            console.log(userName);//这里输出什么?
        }
        userName();
    }
    t('toby');
</script>

结果:

  • function userName() {console.log(userName);}}
  • function userName() {console.log(userName);}}
    t(‘toby’)的分析和执行阶段

分析阶段:

创建AO对象,t.AO = {}

分析参数: t.AO = {userName : toby}

分析var声明: 有同名属性,不做任何事,还是t.AO = {userName : toby}

分析函数声明: 有同名属性,覆盖: t.AO = {userName : function userName() {console.log(userName);}}

执行阶段:

t.AO.userName 输出为function userName() {console.log(userName);}}

userName()的分析和执行阶段

这里也要搞清楚两个概念

//执行userName()分析的是
function () {
console.log(userName);
};

//而不是
var userName = function () {
console.log(userName);
};

分析阶段:

创建AO对象,userName.AO = {}

分析参数: 无,略过

分析var声明: 无,略过

分析函数声明: 无,略过

执行阶段:

因为此时userName.AO = {}是个空对象,无法执行userName.AO.userName,所以会向上一层找,所以输出t.AO.userName的结果,也就是function userName() {console.log(userName);}}

例6:

<script>
    function t(userName) {
        console.log(userName);//这里输出什么?
        var userName = function () {
            console.log(userName);//这里输出什么?
        }
        userName();
    }
    t('toby');
</script>

结果:

  • toby
  • function () {
    console.log(userName);//这里输出什么?
    }

t(‘toby’)的分析和执行阶段

分析阶段:

创建AO对象,t.AO = {}

分析参数: t.AO = {userName : toby}

分析var声明: 有同名属性,不做任何事,还是t.AO = {userName : toby}

分析函数声明: 无,略过

执行阶段:

执行console.log(userName);时调用t.AO.userName 输出为toby,执行完后,代码继续往下执行,那么就到了进行var的赋值操作(var的分析和执行的区别看例子2中我有解释),此时t.AO = {userName : function userName() {console.log(userName);}}},代码继续往下执行,接着就执行到了userName()

userName()的分析和执行阶段

分析阶段:

创建AO对象,userName.AO = {}

分析参数: 无,略过

分析var声明: 无,略过

分析函数声明: 无,略过

执行阶段:

按照例子4我们知道userName.AO是个空对象,所以会往上调用t.AO.userName,所以输出为:function userName() {console.log(userName);}}}

总结:
  后面的变量与前面的变量同名,如果后面变量的值是undefined,则不会覆盖前面的,也就是只有执行到了才赋值,赋了值才能覆盖。也就是说,用这个同名变量时,看之前谁最后一次赋的值,就是那个值。(赋值为undefined也算赋值)

var x=1;
console.log(x);//1
var x;
console.log(x);//1(这样未赋值的undefine就覆盖不了之前的,如果这里给x=undefined,可以覆盖,输出结果就为undefined)
var x=3;
console.log(x);//3

  JavaScript作用域会先在自己的AO上找,找不到就到父函数的AO上找,再找不到再找上一层的AO,直到找到window.这样就形成一条链,这条AO链就是JavaScript中的作用域链.JavaScript中有两条很重要的链,一条是作用域链,一条是原型链。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值