javascript 进阶之 - 执行上下文

引言

执行上下文 = 执行 + 上下文 ;

也就是说执行上下文, 必然是伴随这执行的发生.

那么怎么才能执行?

  1. 浏览器解析到 <script> 标签时或者成功加载外部脚本后, 会逐行执行, 也叫全局执行.
  2. 在逐行读到函数调用时, 也会执行, 这是函数的执行.
  3. 还有逐行读到比较特殊的 eval 语句, 也会执行 , 暂且称作 eval 执行.

相应的执行则会生成不同的上下文, 什么叫做上下文? 在做阅读理解的时候 , 理解一句话的含义的时候, 就通常要结合上下文 , 那么程序要正确执行的时候 , 也要结合程序执行时的上下文 , 也叫做程序执行环境. 不同环境不同的执行效果.

  1. 全局执行 => 全局执行上下文
  2. 函数执行 => 局部执行上下文
  3. eval 执行 => eval 执行上下文

运行流程

上面说 , 逐行读取执行, 并不完全正确.
因为 js 在执行之前, 是会预编译的. 也就是:

  • 全局环境

<script> 代码加载完毕 => 预编译 => 收集所有声明(变量和函数) => 无语法错误 => 生成全局执行上下文 => 开始执行 => 逐行执行( 跳过声明 ) => 执行完毕

其中执行完毕 , 但是全局执行上下文却并未销毁, 只有在关闭网页或者浏览器的时候, 全局执行上下文才会销毁.

  • 函数局部环境

而上面流程中的 , 逐行执行( 跳过声明 )中 , 如果是纯赋值, 直接赋值, 然后跳到下一行. 但是如果碰到函数执行, 则会进入函数体 , 等函数执行完毕并返回 , 才能继续下一行 .
那么进入函数体 , 流程基本和全局类似:

调用函数 => 函数体内预编译 => 收集所有声明(变量和函数) => 无语法错误 => 生成函数局部执行上下文 => 开始执行 => 逐行执行 => 执行完毕 => 返回结果 , 跳出函数

执行上下文是什么?

  • 产生条件?

代码执行即产生执行上下文. 每次执行都会重新生成.

1. 浏览器刷新 => 全局上下文重新生成
2. 函数每次调用 => 函数执行上下文重新生成
  • 本质是什么? 包括哪些?

执行上下文本质是一个对象 .
该对象大致包括了三个内容:

1. 参数表
2. this
3. 局部变量和局部函数声明
  • 执行上下文中的 VO/AO ?

VO 和 AO 为不同时期的同一对象

1. VO 也叫变量对象, 是预编译时,  参数表 + 局部变量和局部函数声明
2. AO 也叫活动对象, 是执行时 , 和 VO 为同一个对象. 
  • 生成执行上下文时确定作用域链?

作用域链就是定义了查找变量查找的规则 , 变量查找由两部分组成 , 一部分为局部变量, 也就是 VO/AO ; 还有一部分 , 则是函数上的 [[Scopes]] 属性 , 在预编译阶段生成.

  • 浏览器看执行上下文

函数执行上下文

VO/AO

这里写图片描述

[[Scopes]]

这里写图片描述

this 指向

  • 简单函数内为 window
function test(){
    console.log(this);
    inner();
    function inner(){
        console.log(this);
    }
}
test(); // window , window
  • 严格模式下的普通函数 this 为 undefined
function test(){
    'use strict';
    console.log(this);
    inner();
    function inner(){
        console.log(this);
    }
}
test(); // undefined , undefined

// 如果显示指定方法拥有者
window.test(); // window , undefined
  • 显式绑定, bind , call , apply , 绑定后 this 指向绑定的对象
var name = 'jack'

var obj = {
    name : 'xx'
}

function test(){
    console.log(this.name);
}

// bind 
var bindTest = test.bind(obj);
bindTest(); // 'xx'

// apply
test.apply(obj); // 'xx'

// call
test.call(obj); // 'xx'

// 当 apply 传入 null , undefined 时, 非严格模式下,this 指向全局对象,严格模式指向传入的值( null / undefined )
test.apply(null); // 'jack'
  • new 操作时的 this 指向实例
function CreateObj(name,age){
    this.name = name ;
    this.age = age;
}

var obj = new CreateObj('xx',18);
console.log(obj.name); // 'xx'
  • 对象中的方法内的 this 指向方法拥有者
var name = 'jack'

var obj = {
    name:'john',
    test:function(){
        'use strict';
        console.log(this.name);
    }
}

obj.test(); // 'john'

var testFn = obj.test;
window.testFn(); // 'jack'
testFn(); // this 为undefined , this.name 查找报错
  • 函数里面的函数调用 , 非严格模式下 , this 指向 window
// 1. 具名函数
function test(){
    function inner(){
        console.log(this);
    }
    inner(); 
}
test(); // window

// 2. 匿名函数
function test2(){
    (function(){
        console.log(this);
    })()
}
test2(); // window

// 3. 对象里面的函数的函数

var obj = {
    test:function(){
        console.log(this);
        function inner(){
            console.log(this);
        }
        inner(); 
    }
}
obj.test(); // obj , window
  • 箭头函数的 this, 箭头函数的 this 沿用外一层的 this 指向
var obj = {
    test:function(){
        console.log(this);
        var inner = ()=>{
            console.log(this);
        }
    }
}
obj.test(); // obj , obj
  • setTimeout / setInterval 的回调的 this , 非严格模式指向 window
var obj = {
    test:function(){
        console.log(this);
        setTimeout(function(){
            console.log(this);
        },1000)
    }
}
obj.test(); // obj , window
  • dom 事件绑定的 this, 指向触发元素
<button id="click-test">click</button>

<script>
    document.querySelector('#click-test').onclick = function () {
       console.log(this); // button 元素
    }
</script>

总结: this 之所以诸多变化, 那是因为在函数执行时, 执行上下文都是重新生成的. 而执行上下文的变化 , 则是根据调用的不同, 而赋值不同.

而最简单粗暴的, 就是 this 的指向始终指向 函数方法 的拥有者. 也就是方法前面的点的对象是谁, 比如 obj.test(), 那么 test 函数的执行上下文的 this 指向 obj ;

在非严格模式下 , 全局函数的 this 指向 window , 严格模式下为 undefined , 但是严格模式下通过 window.方法 , 又能将 this 指向 window` ;

在非严格模式下 , 不能明确找到函数方法拥有者 , 比如函数内部匿名函数或局部函数的 this 指向 window , 严格模式指向 undefined .

DOM 绑定的方法, 则认为拥有者为 DOM 元素.

call , apply 临时改变了拥有者;

bind 永久改变了拥有者.

所以 , 方法拥有者就是 this 指向的值. ( 接受反驳 );

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值