2018-12-18


JS高级

作用域&作用域链

作用域:

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

作用域链:

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

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


执行步骤:
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


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中括号[]中的参数相当于实参([]中的参数可以省略),传给新函数中的形参。


函数调用优先级

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

你可能感兴趣的:(2018-12-18)