[深入01] 执行上下文

2021-07-24  本文已影响0人  woow_wu7

导航

[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数

[react] Hooks

[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染

执行上下文的概念

执行上下文是:javaScript代码解析和执行时所在的环境,javascript中运行的所有代码都在执行上下文中执行

需要理解的一些名词

执行上下文的类型

执行上下文分为三种类型:

全局执行上下文:

函数执行上下文:

Eval执行上下文:

执行上下文栈(函数调用栈)

image

执行上下文的生命周期

创建阶段:(预编译阶段)

执行阶段:

变量对象

<font color=red>变量对象VO </font> ,<font color=red>活动对象AO </font>

变量对象的分类

全局执行上下文中的变量对象

函数执行上下文中的变量对象

<table><tr><td bgcolor=orange>(1) 函数执行上下文的,进入函数执行上下文阶段(预编译阶段)代码还未真正执行,此时AO被创建,此时包含的属性有:</td></tr></table>

函数执行上下文
- 创建阶段(进入执行上下文阶段,或者说预编译阶段),即进入函数执行上下文时,AO被创建,代码并未真正执行
- 此时AO中的属性有:arguments, 形参,函数声明,变量声明,this
- 代码示例:


function a(name, age) {
    var b = 1
    function c() {}
    var d = function()()
    (function e () {}); // 注意e函数的写法
    var f = function g(){}
}
a('woow') // 注意这里没有传实参age


------------
预编译阶段的AO如下:
AO(a function execution context) = {
    arguments: {
        ......
    },
    name: 'woow',
    age: undefined,
    b: undefined,
    c: reference to function c(){},
    d: undefined
    f: undefined
}
// 注意:
// e函数表达式不在AO中
// g函数不在AO中


------------
执行阶段的AO如下:
AO(a function execution context) = {
    arguments: {
        ......
    },
    name: 'woow',
    age: undefined,
    b: 1,
    c: reference to function c(){},
    d: reference to FunctionExpression "d",
    f: reference to FunctionExpression "f"
}

(2)活动对象AO和变量对象VO的区别:

变量提升

<table><tr><td bgcolor=orange>优先级: 函数形参 > 函数声明 > 变量声明</table></tr></td> </br>
<table><tr><td bgcolor=yellow>函数名已经存在,则新的覆盖旧的</table></tr></td> </br>
<table><tr><td bgcolor=yellow>变量名已经存在,直接跳过变量声明</table></tr></td> </br>

变量提升
- 优先级:形参 > 函数声明 > 变量声明 
- 函数名已经存在,则新的覆盖旧的
- 变量名已经存在,则跳过变量声明(注意:只是跳过变量的声明,赋值是正常的赋值)

(例1)
function a(name, age) {
    console.log(name) // -------------------------- 'wang'
    console.log(age) // --------------------------- undefined
    var name = 'woow_wu7'
    console.log(name) // -------------------------- 'woow_wu7'
}
a('wang')

实际执行的代码如下:
function a(name, age) {
    var name = 'wang'
    var age = undefined // ------------------------ 优先级:形参 > 变量声明,所以形参声明并被赋值
    // var name = undefined // -------------------- 变量名已经存在了,跳过变量声明,即这行代码不执行
    console.log(name) // -------------------------- 'wang'
    console.log(age) // --------------------------- undefined, 未传入实参
    name = 'woow_wu7' // -------------------------- 执行阶段被重新赋值
    console.log(name) // -------------------------- 'woow_wu7'
}
a('wang')

(例2)
function a(name, age) {
    console.log(name) // -------------------------- function name(){....}
    console.log(age) // --------------------------- undefined
    var name = 'woow_wu7'
    function name() {
        console.log('name()')
    }
    console.log(name) // --------------------------- 'woow_wu7'
}
a('wang')

实际执行的代码如下:
function a(name, age) {
    var name = 'wang' // -------------------------- 优先级:形参 > 函数声明 > 变量声明
    function name() {...} // ---------------------- 优先级:函数声明 > 变量声明;函数名name已经存在,则新的覆盖旧的
    // var name = undefined // -------------------- 变量名已经存在,则跳过变量声明,相当于该行不执行
    console.log(name) // -------------------------- function name(){....}
    console.log(age) // --------------------------- undefined
    name = 'woow_wu7' // -------------------------- 执行时重新被赋值
    function name() {
        console.log('name()')
    }
    console.log(name) // --------------------------- 'woow_wu7'
}
a('wang')

我的语雀:https://www.yuque.com/woowwu/msyqpd/ph3qtd
https://juejin.im/post/6844904033308655623
https://juejin.im/post/6844903704466833421
https://juejin.im/post/6844903577899499534
https://juejin.im/post/6844903473528602637
https://www.jianshu.com/p/330b1505e41d

复习

手写new


手写new
- new命令执行构造函数,返回实例对象
- 构造函数中有return,如果后面跟一个对象new命令会返回这个对象,如果后面不是对象,就会返回this对象
- new命令生成的实例对象可以继承构造函数的prototype属性 => 即实例可以访问构造函数的prototype上的属性和方法
- new命令生成的实例可以访问构造函数中的属性和函数


过程:
1. 新建一个空对象
2. 将空对象的隐式原型指向构造函数的显示原型 => 空对象就能访问构造函数的prototype上的属性和方法
3. 将构造函数中的this指向这个空对象 => this上绑定的属性和方法实际上就绑在了空对象上
4. 执行构造函数 => 如果有return,并返回一个对象,就返回这个对象,如果不是对象,就返回这个空对象
5. 如果构造函数的返回值是一个对象,就返回这个对象。否则返回这个空对象即this对象


代码:
function Constructor(name) {
  this.name = name
  this.age = 20
  return this
}
Constructor.prototype.address = 'chongqing'

function _new() {
  const obj = {}
  // 还可以通过 const obj = new Object()来生成

  const paramsConstructor = Array.prototype.shift.call(arguments)
  // 获取传入new函数的构造函数
  // 等于 [].prototype.shift.call(arguments)
  // 等于 [].prototype.shifig.apply(arguments)

  obj.__proto__ = paramsConstructor.prototype
  // 将空对象的隐式原型指向构造函数的显示原型
  // 这样paramsConstructor.prototype就成了obj的原型对象,obj可以访问原型对象上的属性和方法
  // 注意:Object.create() 该方法可以以参数对象为原型,返回一个实例对象
  // 所以:空对象的生成,加空对象的隐式原型的绑定可以一行代码搞定:
  //!!!!!!!可以:const obj = Object.create(paramsConstructor.prototype)
  //!!!! 还可以:const obj = Object.setPrototypeOf({}, paramsConstructor.prototype)

  const res = paramsConstructor.apply(obj, arguments)
  // 将构造函数中的this绑定在空对象上,并执行构造函数
  // 注意:这里arguments是shift后剩下的值,因为apply可以自己转化类数组的对象,所以无需额外操作

  return Object.prototype.toString.call(res).includes('Object') ? res : obj
  // 如果构造函数返回的是一个对象,就返回这个对象,否则返回这个空对象
  // 等于 typeof res === 'object' ? res : obj
}
const res = _new(Constructor, 'woow_wu7')
console.log(res, 'res11')
上一篇下一篇

猜你喜欢

热点阅读