JavaScipt学习——套公式学习作用域

01引言


JavaScript是门全栈性的语言,尤其是在2016年,经常听到JavaScript要一统天下的梗,甚至有流言说16年会个Vue.js就能找到工作,和当年iOS会个TableView就能找工作一样。(tableView就相当于Android的ListView,不过现在基本都用RecyclerView了)


2016年前端的热门技术基本都和JavaScript有关,比如移动端跨平台的Facebook出品的React Native和阿里的Weex,热修复技术JSPath,以及后端的Node.js(本宝宝非常喜欢的一门技术栈)。昨晚去gibhub看了下,Vue的star数量已经超过了jQuery,虽然star数量并不能证明些什么,但是至少我们已经看到,前端思想已经从之前的document操作到了数据驱动开发的转变(有兴趣的话我可以之后结合Android、iOS、Vue,用一个小demo演示一下这个思想转变),些公司甚至开始尝试用饿了么出品的Element来替代EasyUI(做过后端的同学应该都知道EasyUI真的是AV画质....)


JS技术层出不穷,之前就有一篇很火的文章,2016年学JS是一种什么样的体验,瞬间吓尿了不少人。大家都把注意力放到了框架和新技术上,原生的JS反而遭到了冷落,所以想通过几个基础性的JS问题,和大家一起交流(更希望大家带我飞,但是别把我丢到马航上飞...)



02JavaScript中的作用域


下面一个简单的问题:

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

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

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

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

</script>

这是一个很简单的JS作用域问题,但是你越是强调简单这两个字,就越容易使人放松警惕,所以导致有些同学不假思索的回答输出

  • hello

  • world

  • toby


但是结果是输出

  • hello

  • undefined

  • toby


那么这就奇怪了,为什么会有undefined呢,不是应该是world吗?首先我们要明白,变量的寻找会遵循就近原则,所以js会先在函数中找,找不到才会向外找,而函数内有str2,但是运行到console.log(str2)时str2未定义,所以就出现了undefined



03词法分析


知其然还必须其所以然,那么我们再来看几个例子


例子1

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

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

输出的结果是什么,这个例子好像和上面不一样,有种回到高中数学,题型一变就懵逼的感觉,这个时候可能有些同学会觉得是toby,但是实际输出是

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

为什么是function呢?其实这种作用域的问题,都是可以通过"套公式"来得出,这个公式,就是JS中的词法分析,JS中函数执行前,必须要做的一项工作就是词法分析,那么究竟要什么什么呢?分析参数,分析变量声明,分析函数声明,那么我们就拿这道题来套一下公式


执行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');}


例子2

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

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

那这里的输出又是什么呢?这个好像又和上面的例子不一样,又再次陷入懵逼状态?别怕麻烦,坚定的按照公式再走一次流程(上面的的例子写得比较详细,下面的分析就简单写)


分析之前,首先要弄明白两个概念,一个叫函数声明,一个叫函数表达式


//这个叫函数声明
function userName() {
    console.log('tom');
}

//这个叫函数表达式
var userName = 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


例子3


<script>
    t();
    t2();

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

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


那么我们再来看一个例子,这下彻底回到高中时代,做了两个例子好像感觉掌握了,结果考试你给来看这个?


答案是,t()输出为toby,t2()则会报错.这又是为什么?

  • t()可以调用是因为在词法分析的过程,就已经完成了t函数的分析,所以可以调用

  • t2()不能调用是因为在词法分析的阶段,分析到有一个t2声明,在AO上只是形成了一个属性,但是值为undefined


例子4


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


函数里面套函数,这次竟然又和前面不一样了...这次我不说答案了,直接先套公式走一波


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);}}


例子5


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


好吧,我保证这个是最后一道...这个输出结果是什么呢?我们只要坚定公式没问题,就一定能得出结果,那么再套公式走一波


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);}}}



作者:肥朝   原文

http://www.jianshu.com/p/43bf4f2e0d57


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript 作用域指的是变量、函数和对象可以被访问的范围。在 JavaScript 中,作用域分为全局作用域和局部作用域两种。 全局作用域指的是在函数外定义的变量和函数,它们可以被整个程序访问。在浏览器中,全局作用域指的是 window 对象。 局部作用域指的是在函数内部定义的变量和函数,它们只能在函数内部被访问。在 JavaScript 中,每当一个函数被调用时,都会创建一个新的局部作用域JavaScript 使用词法作用域(也称为静态作用域)规则来决定变量的作用域。这意味着函数作用域是在函数定义时确定的,而不是在函数调用时确定的。因此,在函数内部定义的变量可以在函数外部访问,但函数外部定义的变量不能在函数内部访问。 例如,下面的代码演示了 JavaScript 中的作用域: ``` var x = 10; // 全局变量 function foo() { var y = 20; // 局部变量 console.log(x); // 可以访问全局变量 x console.log(y); // 可以访问局部变量 y } foo(); console.log(x); // 可以访问全局变量 x console.log(y); // 不能访问局部变量 y ``` 在上面的例子中,变量 x 是在全局作用域中定义的,可以在函数内部和外部访问。变量 y 是在函数内部定义的,只能在函数内部访问。函数 `foo()` 调用后,可以在函数内部访问变量 x 和 y。函数调用结束后,只能在全局作用域中访问变量 x,无法访问变量 y。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值