js的概念:

  js是解释性语言。解释性:逐行解析,逐行执行


js的特点:

  单线程,同一时间只能做同一事情。

  作为浏览器脚本语言,js的主要用途是与用户交互和操作DOM,这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,js有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,此时浏览器该以哪个线程为准?


js执行过程:

  语法解析

  预编译

  解释执行


预编译:

  在内存中开辟一块空间,用来存放变量和函数。

  作用:消除一些歧义

  预编译发生在函数执行前,也就是说,函数执行时预编译已经结束


全局对象(Global Object):

  在浏览器环境中,js引擎会整合script标签中的内容,产生window对象,这个window对象就是全局对象

    啥叫整合?

    js作用域_window对象

  在node环境中,会产生global对象


全局变量:

  在script中声明的变量为全局变量,全局变量(由var声明的)会挂载到window对象上,作为window对象的一个属性存在

    js作用域_window对象_02


全局函数:

  在script中声明的函数为全局函数,全局函数会作为window对象的方法存在

    js作用域_window对象_03


活动对象(Activation Object):

  也叫激活对象,在函数被调用时产生,用来保存当前函数内部的执行环境(执行期上下文)

    js作用域_预编译_04

  在函数调用结束时销毁

    js作用域_作用域_05


局部变量:

  在函数内部声明的变量是局部变量,局部变量作为ao对象的属性存在

    js作用域_作用域_06

  如何理解局部:

    在函数a的外部不能访问变量i,变量i只能在函数a内部使用,这就是作用域的由来

    如果不执行函数,就不会产生ao对象,就不会存在属性i

    如果执行函数,就会产生ao对象,并将变量i作为ao对象的属性

    函数执行完后,ao对象被销毁,属性i随之被销毁


局部函数:

  在函数内部声明的函数叫局部函数,局部函数作为ao对象的方法存在

    js作用域_window对象_07



全局预编译:

  流程:

    查找变量声明,作为GO对象的属性名,值为undefined

    查找函数声明,作为GO对象的方法名,值为function

  变量声明:

var a // 变量声明
var b = 100 // 变量声明+变量赋值


  函数声明:

function a() {} // 函数声明
var b = function() {} // 函数表达式,不是函数声明


  全局预编译的过程:

console.log(a)
var a = 100
console.log(a)
function a() {
console.log(100)
}
console.log(a)
a()
/*
全局预编译的过程:
1、js引擎整合所有的script标签,产生window对象
2、查找变量的声明,将变量a作为window的属性名,属性值为undefined
3、查找函数的声明,将函数a作为window的属性名,属性值为function

全局预编译结束后,代码从上到下依次执行
*/


  结论:如果存在同名的变量和函数,函数的优先级更高


函数预编译:

  流程:

    在函数被调用时,为当前函数产生AO对象

    查找形参和变量作为AO对象的属性名,值为undefined

    使用实参的值改变形参的值

    查找函数声明作为AO对象的属性名,值为function

  全局预编译与函数预编译的过程:

function a(test) {
var i = 100
function b() {
console.log(200)
}
b()
}
a('test')
/*
全局预编译的过程:
1、js引擎整合所有的script标签,产生window对象
GO:
(1)查找变量的声明,这里没有声明全局变量
(2)查找函数的声明,将函数a作为window对象的属性名,值为undefined
全局预编译结束,自上而下执行代码

调用函数a,产生函数a的AO对象,开始函数预编译:
AO:
(1)查找形参test、变量i作为AO对象的属性名,值为undefined
(2)实参'test'赋值给test
(3)查找局部函数b,b作为AO对象的属性名,值为undefined
函数a的预编译结束(函数b也会产生b的AO,这里省略不做研究),函数a内自上而下执行代码
i = 100
*/


    js作用域_预编译_08


优先级:局部函数>实参>形参/局部变量

// 形参和局部变量重名,以实参为准
function a(i) {
var i
console.log(i) // 100
}
a(100)

// 形参和局部函数重名,以局部函数为准
function b(i) {
console.log(i) // function
function i() {}
}
b(100)



作用域和作用域链:

作用域:

  全局作用域:

    编写在script标签中的js代码,都是全局作用域。或者是单独的js文件中的代码

    全局作用域在页面打开时创建,在页面关闭时销毁

    全局作用域中有一个全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接使用

  局部作用域(函数作用域):

    在函数内部的就是局部作用域

    函数被调用时创建局部作用域,函数执行完毕后,局部作用域被销毁(闭包除外)

    每调用一次函数就会创建一个新的局部作用域,它们之间是相互独立的


 作用域链:

  在js中,函数存在一个隐式属性[[Scopes]],这个属性用来保存当前函数执行时的上下文,由于在数据结构上是链式的,也被称作为作用域链。可以将它理解为一个数组

   [[Scopes]]:

    在函数声明时产生,在函数调用时更新

    记录当前函数的执行环境

    在函数被调用时,将该函数的AO对象压入到[[Scopes]]中

function a() {
console.dir(a)
function b() {
console.dir(b)
function c() {
console.dir(c)
}
c()
}
b()
}
a()


    js作用域_作用域_09

     js作用域_预编译_10


作用域链的作用:

  在访问变量或函数时,会在作用域链上向上查找,最直观的表现是:​内部函数可以使用外部函数声明的变量








js作用域_预编译_11