Javascript执行上下文顺序的深入讲解
一 执行上下文"color: #ff0000">二 执行上下文的生命周期
1.创建阶段
执行上下文的生命周期有三个阶段:创建阶段–执行阶段—回收阶段(主要说创建阶段)
当函数被调用,但没有执行任何其内部代码之前,会先确定这三个步骤:
1) 创建变量对象:首先初始化函数的参数 arguments,提升函数声明和变量声明
2) 创建作用域链:在执行期上下文的创建阶段,作用域链是在变量对象之后创建的.作用域链本身包含变量对象。
作用域链用于解析变量。当被要求解析变量时,JavaScript 始终从代码嵌套的最内层开始,如果最内层没有
找到变量,就会跳转到上一层父作用域中查找,直到找到该变量。
3) 确定 this 指向:有多种情况.
2. 执行阶段
执行变量赋值、代码执行
3. 回收阶段
执行上下文出栈等待虚拟机回收执行上下文
三. 变量提升和this的指向
1.变量声明提升:
大部分编程语言都是先声明变量再使用,但在 JS 中,事情有些不一样:
console.log(a); // undefined var a = 10;
上述代码正常输出undefined而不是报错Uncaught ReferenceError: a is not defined,这是因为声明提升.
相当于:
var a; //声明 默认值是undefined “准备工作” console.log(a); a = 10; //赋值
2. 函数声明提升
创建函数的方法有两种,一种是通过函数声明function aa(){}
另一种是通过函数表达式var aa = function(){} ,那这两种在函数提升有什么区别呢?
console.log(f1); // function f1(){} function f1() {} // 函数声明 console.log(f2); // undefined var f2 = function() {}; // 函数表达式
接下来我们通过一个例子来说明这个问题:
function test() { aa(); // Uncaught TypeError "aa is not a function" bar(); // "this will run!" var aa = function() { // function expression assigned to local variable 'aa' alert("this won't run!"); }; function bar() { // function declaration, given the name 'bar' alert("this will run!"); } } test();
在上面的例子中,aa()调用的时候报错了,而 bar 能够正常调用。
我们前面说过变量和函数都会上升,遇到函数表达式 var aa = function(){}时,首先会将var aa上升到函数体顶部,然而此时的 aa 的值为 undefined,所以执行aa()报错。
而对于函数bar(), 则是提升了整个函数,所以bar()才能够顺利执行。
细节必须注意:当遇到函数和变量同名且都会被提升的情况,函数声明优先级比较高,因此变量声明会被函数声明所覆盖,但是可以重新赋值。
alert(a); //输出:function a(){ alert('我是函数') } function a() { alert("我是函数"); } // var a = "我是变量"; alert(a); //输出:'我是变量'
function 声明的优先级比 var 声明高,也就意味着当两个同名变量同时被 function 和 var 声明时,function 声明会覆盖 var 声明
这代码等效于:
function a() { alert("我是函数"); } var a; //hoisting alert(a); //输出:function a(){ alert('我是函数') } a = "我是变量"; //赋值 alert(a); //输出:'我是变量'
3. 确定this指向
// 情况1 function foo() { console.log(this.a) //1 } var a = 1 foo() // this->window // 情况2 function fn(){ console.log(this); } var obj={fn:fn}; obj.fn(); //this->obj // 情况3 function CreateJsPerson(name,age){ //this是当前类的一个实例p1 this.name=name; //=>p1.name=name this.age=age; //=>p1.age=age } var p1=new CreateJsPerson("尹华芝",48); // 情况4 function add(c, d){ return this.a + this.b + c + d; } var o = {a:1, b:3}; add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 // 情况5 <button id="btn1">箭头函数this</button> <script type="text/javascript"> let btn1 = document.getElementById('btn1'); let obj = { name: 'kobe', age: 39, getName: function () { btn1.onclick = () => { console.log(this);//obj }; } }; obj.getName(); </script>
结果:
1 this指向window;
2谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
3 在构造函数中,this 是当前类的一个实例
4call、apply 和 bind:this 是第一个参数
5箭头函数 this 指向:箭头函数没有自己的 this,看其外层的是否有函数,如果有,外层函数的 this 就是内部箭头函数的 this,如果没有,则 this 是 window。
下一篇:解决vant中 tab栏遇到的坑 van-tabs