2018-12-18


JS高级

作用域&作用域链

作用域:

1.作用域的个数:n(函数声明的个数)+1(全局作用域)
2.作用域不会存储变量,只是执行查询规则
3.作用域分全局作用域和函数(局部)作用域
4.作用域是编译时产生的
5.没有块作用域(ES5)
6.作用域管理执行上下文,执行上下文管理变量
7.一般情况下,一个作用域对应一个执行上下文,但递归情况下一个作用域可以对应多个执行上下文。值得注意的是,任何情况下一个作用域只有一个活动的执行上下文

作用域链:

函数嵌套时会产生作用域链

--
作用域的作用:
1.隔离变量
2.为变量查询制定的一套规则。
--
变量查询的规则:
左查询:等号左边的变量用左查询。
--先在变量的当前作用域里面找变量是否声明,没有就到上一层找,直到整条作用域链都没有找到(也就是全局),那么浏览器会自动为变量声明。
右查询:等号非左边的变量用右查询规则。
--先在变量的当前作用域里面找变量是否声明,没有就到上一层找,直到整条作用域链都没有找到(也就是全局),那么报错。
--
左右查询案例:

<script>
        console.log(b); // 报错
        (function(){
            function test(a){
                var b=a;
                console.log(b);//2
            }
            test(2);
        })()
        console.log(b);//b is not defined
</script>

执行步骤:
1.console.log(b); 对b右查询,在当前作用域(全局)查找b的声明,没有则报错
2.执行立即执行函数,然后调用test(2)函数,将实参2传给形参a
3.var b=a;对b执行左查询,在当前作用域有声明var b,且b=a,则b为2,输出2
4.立即执行函数运行结束, 运行console.log(b);对b进行右查询,在当前作用域(全局)查找b的声明,整条作用域链上没有找到,则报错。


作用域面试题:


image.png

以上示例图的执行步骤:
1.先执行var x =10;(x为全局变量)
2.执行show(fn);调用show()函数,将实参fn函数传给形参f,那么形参f拥有fn函数的地址值
3.f(),调用fn()函数。
4.console.log(x);对x执行右查询,在fn()函数中没有对变量x进行声明,那么到全局作用域里面去找,刚好找到var x =10;所以结果为10.


变量释放&内存回收

变量释放:
局部变量————>对应函数的作用域执行完后则变量释放
全局变量————>当运行程序关闭时变量才会释放
内存回收:
垃圾收集器会按照固定的时间间隔周期性的回收内存。一般使用标记清除,引用计数两种策略。


执行上下文&执行上下文栈

执行上下文(可以认为是可执行代码的执行环境):
1.执行上下文的个数:n(函数的调用次数)+1(全局执行上下文)
2.在函数被调用时,都会创建新的其对应作用域的执行上下文。
3.一般情况下,一个作用域对应一个执行上下文,但递归时,一个作用域可能对应多个执行上下文。(每调用一次函数创建一个执行上下文)
4.一个作用域只能有一个活动状态的执行上下文。(单线程、同步执行)
5.执行上下文分为:全局执行上下文(只有一个)、函数执行上下文(可以有无数个)
--
执行上下文栈:
当一个函数被调用时,会创建一个当前被调用函数的执行上下文,并将执行上下文压入执行栈中。当js第一次加载脚本时,默认进入全局执行上下文中。


image.png
<!--
    1. 依次输出什么?
    2. 整个过程中产生了几个执行上下文?
-->
<script type="text/javascript">
  var i
  console.log('global begin: '+ i)   //undefined
  i = 1
  foo(1);
  function foo(i){
    if (i == 4) {
      return;
    }
    console.log('foo() begin:' + i);    //1,2,3
    foo(i + 1);
    console.log('foo() end:' + i);     //3,2,1                                    
  }
   console.log('global end: ' + i)     //1
</script>
image.png

执行上下文的作用:

1.存储对应的变量
2.规则
---全局执行上下文规则:
1.var定义的全局变量,添加为window的属性
2.function声明的全局函数,添加为window的方法
3.提升(函数的提升优于变量的提升)
4.this指向(window)
--
---函数执行上下文规则
1.var定义的局部变量会成为局部执行上下文(在js中拿不到局部执行上下文对象)的属性
2.function声明的局部函数会成为局部执行上下文(在js中拿不到局部上下文对象)的方法
3.形参变量赋值给实参
4.为arguments赋值(实参列表)
5.提升(函数表达式不会提升)
6.this指向(看调用函数的对象的调用形式)
--
---函数执行上下文规则的this指向
1.普通调用      this—>window
   在严格模式下 this—>undefined
2.隐式调用      this—>最近的调用者
3.显示调用      this—>指定的对象
4.构造调用      this—>构造出的实例对象


作用域与执行上下文的关系

对应的执行上下文 会 成为 作用域的属性(执行上下文最终会挂靠给作用域)
--作用域:
从浏览器的角度看作用域,它是一个c++对象,对象的属性就是执行上下文
从js语言的角度看作用域,它是一套规则(左查询、右查询)
--执行上下文:
从浏览器的角度看执行上下文,它是一个c++对象,对象的属性就是它所管理的变量
从js语言的角度看执行上下文,它是一套规则(规则如上)


提升

总结:
1.提升会提升到当前作用域的最顶层
2.变量提升是声明的提升
3.函数提升是整体提升
4.函数的提升优于变量的提升
注意点:如果有两个同名函数,那么后面的函数会覆盖前面所定义的函数(在js语言中没有重载的概念,所以无法通过参数来判断到底调用哪个函数)。


编码规范

1.使用变量前一定要先声明(避免污染全局,迎合js引擎的提升规则)
2.在分支中最好不要定义函数(因为在分支中的函数会变为函数表达式)

注意点:当函数名与变量名重名时
function demo(){...}==>var demo = function(){...}
var demo   //跟函数名重复,js引擎在解析代码时会忽略此条声明
demo=5;
demo()
//最终demo = function(){...}=5    5()    结果报错

隐式丢失

当我们对函数进行隐式调用(对象.属性)时,可能会发生隐式丢失的问题。

  1.以 对象.的形式 进行变量的赋值时容易发生隐式丢失
            var fn = obj.test;
            fn()  // 发生了隐式丢失,原本this应该指向obj,结果fn()独立调用函数,this指向全局

            var write = document.write;
            write("****") // 原本this应该指向document,结果发生隐式丢失,所以报错

--

bind()函数

bind()函数(硬绑定)是用来解决隐式丢失问题的一种手段。
bind(this,[Arg1,Arg2...]),使用bind来实现硬绑定时,会产生一个新的函数,这个新的函数的this指向被绑定的对象(也就是bind中第一个参数this指定的对象),新的函数相当于拷贝了一份原函数,将bind中括号[]中的参数相当于实参([]中的参数可以省略),传给新函数中的形参。

<script>
        var module = {
            x: 42,     
            getX: function (a,b) {
                console.log(a,b);
                return this.x;
            }
        }

        var unboundGetX = module.getX;
        /*
        使用bind硬绑定产生一个boundGetX的函数,boundGetX函数拥有跟原函数一样的函数体(this指向了module),
        相当于拷贝了一份原函数,bind中[1,2]相当于新函数的实参
        */
        var boundGetX = unboundGetX.bind(module,1,2);      
        console.log(boundGetX());
        console.log(boundGetX);
    </script>

函数调用优先级

构造函数 > 显示调用 > 隐式调用 > 普通调用

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

推荐阅读更多精彩内容

  • 来源:仗剑走天涯! 关于javascript的作用域的一些总结,主要参考以上文章,加上自己的整理的理解。 近日对j...
    Michael_林阅读 928评论 0 1
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 今天是大年初一,凌晨五点半起床我们像往年一样去隆兴寺上香祈福,也不算多远,九点就到家了,在这之前奶奶经已...
    陶沐卉阅读 809评论 0 7
  • 过度规划是大多数空想主义者的爱好,这一部分人往往患有“过度规划症”,半夜和年底为病发高峰期。 就比...
    Close_朝生阅读 456评论 0 1
  • 坚持:不管现在的你处于怎样的阶段,是无助还是迷茫,坚持都一定是最划算的 。因为当你心生放弃时,行为上就会得过且过,...
    子非鱼lily阅读 289评论 0 0