参考:https://www.cnblogs.com/wangfupeng1988/p/3988422.html 1 执行上下文环境:在执行代码之前,要将要用到的所有变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。 我的理解:执行上下文环境是浏览器在执行代码之前做的一些准备工作,每次当控制器转到可执行代码的时候,就会进入一个执行上下文。执行上下文为当前代码的执行环境,它会形成一个作用域。 参数 赋值 arguments 赋值 自由变量的取值作用域 赋值
函数声明 赋值
普通变量 声明(默认赋值为undefined) 函数表达式 声明(默认赋值为undefined)
this 赋值 2 函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。可以存在多个上下文环境,由执行上下文栈统一管理,但是只有一个处于活动状态的上下文环境
1)全局代码执行时,会产生一个全局执行上下文环境 2)每次调用函数都又会产生执行上下文环境 3)当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全4)局上下文环境。处于活动状态的执行上下文环境只有一个。
3 函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域
4 函数中this的取值 在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。 1)构造函数
function Foo(){ this.name='wh'; this.year='1998'; console.log(this);//Foo{name:'wh',year:'1998'} } var f1=new Foo(); console.log(f1.name); console.log(f2.name); function Foo(){ this.name='wh'; this.year='1998'; console.log(this);//window } Foo();注意: Foo函数作为构造函数的时候,函数中的this指向new出的对象 Foo作为函数直接调用,this指向window 2)函数属于对象中的一个属性
var obj={ x:10, fn:function(){ console.log(this);//Object{x:10;fn:function} consolw.log(this.x);//10 } obj.fn(); } var obj={ x:10, fn:function(){ console.log(this);//window consolw.log(this.x);//undefined } var fn1=obj.fn; fn1();注意: 1 如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象。 2 如果fn函数被赋值到了另一个变量中,并没有作为obj的一个属性被调用,那么this的值就是window,this.x为undefined。
3)函数用call或者apply调用 当一个函数被call和apply调用时,this的值就取传入的对象的值。
var obj={ a:10; } var fn=function(){ console.log(this);//Object{x:10} console.log(this.x)//10 } fn.call(obj);4)全局 & 调用普通函数 在全局环境下,this永远是window,这个应该没有非议。
// console.log(this===window)true var x=10; var fn=function(){ console.log(this);//window console.log(this.x);//10 } fn()但是有特殊情况
var obj={ x:10, fn:function(){ function f(){ console.log(this);//window console.log(this.x);//undefined }; fn(); } } obj.fn();在整个原型链中,this代表的也都是当前对象的值。
5 作用域: javascript没有块级作用域 例:
for(var i=0;i<10;i++){ //..... } console.log(i)://10注意: 1 )编写代码的时候,不要在“块”里面声明变量,要在代码的一开始就声明好了。以避免发生歧义 2) javascript除了全局作用域之外,只有函数可以创建的作用域 3 )我们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其他地方都不要出现变量声明。而且建议用“单var”形式。
作用域概念:作用域是一个很抽象的概念,类似于一个“地盘”,在函数定义阶段就已经确定 4 )全局代码和fn、bar两个函数都会形成一个作用域。而且,作用域有上下级的关系,上下级关系的确定就看函数是在哪个作用域下创建的。例如,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级。 5 )不同作用域中可以出现同一命名,不会有冲突 6 )要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值。 6 自由变量
var x=10; function fn(){ var b=20; console.log(x+b);//x为一个自由变量 }在fn函数中x:需要到创建fn函数的作用域中查找 自由变量的取值:要到创建这个函数的那个作用域中取值——是“创建”,而非调用 取自由变量a的过程(跨作用域查找): 1 现在当前作用域查找a,如果有则获取并结束。如果没有则继续; 2 如果当前作用域是全局作用域,则证明a未定义,结束;否则继续; 3 (不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域 4 跳转至1查找
7 闭包
闭包的体现 1 函数作为返回值
function fn(){ var max=10; return function bar(x){ if(x>max){ console.log(x); } } } var f1=fn(); f1(15)理解:因为执行fn()时,返回的是一个函数。函数的特别之处在于可以创建一个独立的作用域。而正巧合的是,返回的这个函数体中,还有一个自由变量max要引用fn作用域下的fn()上下文环境中的max。因此,这个max不能被销毁,销毁了之后bar函数中的max就找不到值了。 因此,这里的fn()上下文环境不能被销毁,还依然存在与执行上下文栈中。 在执行f1(15)时,fn()执行上下文环境还存在与栈中,因此bar(15)时,max可以查找到max=10; 结论:闭包使多个上下文环境存在,且不会再函数调用完毕后销毁,从而增加内容开销。
补充: 什么是变量提升:将变量的声明提升到了执行上下文中 执行上下文的代码可分为两个阶段处理: 1 进入执行上下文(准备工作,定义) 2 代码执行(刷新一个新的执行上下文,可为执行上下文里的变量赋值) 执行上下文(看做是一个数组),具有三个重要的属性 1 变量对象(VO) 活动对象(AO) 注意:AO和VO是一个东西,因为在JS环境中,只有一个执行上下文活动,这个活动的(处于栈顶的)执行上下文的变量对象激活,此时这个变量对象为活动对象,只有活动对象的各种属性才能被访问 2 this 3 作用域
变量对象VO vo是一个对象:调用函数,或执行全局代码时创建的对象 1 函数的所有形参 没有实参属性值为undefined 2 确定函数声明 如果变量对象中已经存在相同的属性,则可完全替换同名属性 3 变量声明(var声明) 如果变量名称和已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性(即不会发生替换),而是跳过
函数内预编译四部曲:
创建AO对象 Activation Object (执行期上下文)找形参和变量声明,将变量和形参名作为AO属性名,值为undefined将实参值和形参统一在函数体里面找函数声明,值赋予函数体变量对象和活动对象的区别: 1 其实都是同一个对象,只是处于执行上下文的不同生命周期 2 未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。
全局上下文中的变量对象是window 全局上下文的生命周期,与程序的生命周期一致,只要程序运行不结束,比如关掉浏览器窗口,全局上下文就会一直存在。其他所有的上下文环境,都能直接访问全局上下文的属性。
作用域 JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定。执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。
在JavaScript中,我们可以将作用域定义为一套规则,这套规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找。
作用域是一套规则,那么作用域链是什么呢?是这套规则的具体实现。所以这就是作用域与作用域链的关系,相信大家都应该明白了吧。这里的标识符,指的是变量名或者函数名
闭包 this 作用域链还需要加深理解!!!!!!!!!!!!!!!!!!!!