• 【面筋烧烤手册】JavaScript结构专题


    Javascript结构专题

    1、作用域 / 链

    • 规定变量和函数的可使用范围称作作用域
    • 每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链。

    2、执行上下文和执行栈

    • 执行上下文分为:

    • 全局执行上下文
      创建一个全局的window对象,并规定this指向window,执行js的时候就压入栈底,关闭浏览器的时候才弹出

    • 函数执行上下文
      每次函数调用时,都会新创建一个函数执行上下文

    • 执行上下文分为创建阶段和执行阶段

    • 创建阶段:函数环境会创建变量对象:arguments对象(并赋值)、函数声明(并赋值)、变量声明(不赋值),函数表达式声明(不赋值);会确定this指向;会确定作用域

    • 执行阶段:变量赋值、函数表达式赋值,使变量对象编程活跃对象

    • 执行栈:

    • 首先栈特点:先进后出

    • 当进入一个执行环境,就会创建出它的执行上下文,然后进行压栈,当程序执行完成时,它的执行上下文就会被销毁,进行出栈。

    • 栈底永远是全局环境的执行上下文,栈顶永远是正在执行函数的执行上下文
      只有浏览器关闭的时候全局执行上下文才会弹出

    3、this

    1. 指向不确定,可以动态改变,call/apply 可以改变this的指向(到调用的函数的this里)
    2. 一般指向函数拥有者
    3. 闭包 / 自执行函数(setInterval setTimeout) 指向window对象或上一层对象
    4. dom 回调函数this指向该元素

    4、闭包

    为什么闭包

    1. 自己作用域外不能访问内部变量,内部可以访问外部
    2. 使闭包要用的局部变量一直存在内存里
    3. 封装私有属性
    4. 匿名空间立即执行,防止全局污染,立即回收

    this指向window或上一层闭包拥有者

    结构

    function xxx(){
    	let xxx
    	function xxxx(){
    		xxxx = xxx //xxx不会被回收
    	}
    }
    

    ((a,b)=>{
    	
    })(3,4)
    
    (()=>{
    
    })()
    

    5、箭头函数

    场景

    • 简化写法的地方
    • 用于本来需要匿名函数的地方
    • 解构和rest的地方
    • 简化回调的地方

    解决了什么

    1. 写es6之前的函数很麻烦
    2. 匿名函数实现闭包
    3. ({…, …})=>{ } 要解构的地方
    4. 简化回调函数:a.sort((c, d)=>(c - d))
    5. 传rest参数:
    const headAndTail = (head, ...tail) => [head, tail]
    headAndTail(1,2,3,4,5) === [1,[2,3,4,5]]
    

    不适用场景

    1. 对象的方法如果有this,如果()=>的话,会指向this指向对象所在作用域(外面或者是window)
    2. addEventListener('click',()=>{this.xxx}) this指向addEventListener外面

    注意点

    1. 无原型prototype,所以不能做构造函数,不能new,new关键字内部需要把新对象的_proto_指向函数的prototype
    2. 不能用arguments,因为不是标准函数,在函数体内不存在,可用rest参数代替
    3. 不能用yield,不能做generator函数
    4. 不能用arguments,super,new,target这些指向外层函数作用于
    5. 没有自己的this,无call apply bind绑定

    实现尾递归

    1. 实现fib函数
    function Fib(n, ac1 = 1, ac2 = 1){
    	if(n<=1) return ac2;
    	return Fib(n-1, ac2, ac1 + ac2)
    }
    
    1. 实现阶乘函数
    function factorial(n, total){
    	if(n===1) return total;
    	return factorial(n-1, total * n)
    }
    
    1. 柯里化

    6、内存泄漏

    种类

    1. 意外的全局变量

    1. 未声明的变量直接绑到window,如function Fn(){a=...} 等同于 function Fn(){window.a = ...}
    2. function Fn(){this.a = ...} 等同于 function Fn(){window.a = ...}
      使用use stict让this指向undefined

    2.被遗忘的计时器或者回调函数

    1. 定时器:a = setInterval(function, 500) 如果function里面有外面的依赖数据,那么定时器不停,依赖的内存就一直不回收
      解决:clearInterval()

    2. btn.addEventListener('click', onclock, false) 一旦不再需要就必须 bin.removeEventListener('click', onclick)不然要一直监听

    3. 脱离DOM的引用,例如:var elements = {btn : document.getElementById(...)}
      后面又remove了这个btn
      但是elements里面还是存在,elements.btn = null;解决

    4. 闭包
      在这里插入图片描述

    避免内存泄漏

    1. 及时销毁,null / clearInterval / removeEventlistener
    2. 避免频繁创建过多对象
    3. 避免生命周期长的对象

    7、垃圾回收机制的策略

    1. 标记清除法
      垃圾回收机制获取根并标记他们,然后访问并标记所有来自它们的引用,然后在访问这些对象并标记它们的引用…如此递进结束后若发现有没有标记的(不可达的)进行删除,进入执行环境的不能进行删除
    2. 引用计数法
      当声明一个变量并给该变量赋值一个引用类型的值时候,该值的计数+1,当该值赋值给另一个变量的时候,该计数+1,当该值被其他值取代的时候,该计数-1,当计数变为0的时候,说明无法访问该值了,垃圾回收机制清除该对象
    3. 新生代旧生代(V8引擎)
      新生代:To(空闲区)From(判断是否晋升)
      晋升:是否经历过回收?From变成老生代并清除:From变成To然后To和From交换;不存活直接被释放

    8、 var let const

    1. var挂到window上
    2. var有变量提升,非use strict可以先使用(默认挂到window)在声明
    3. const let 形成块作用域 var挂到window
    4. 同一作用域 const let不能声明多次 var可以
    5. 暂存死区:
        let temp = 'global temp';  // 全局作用域下声明的 temp 变量
        if (true) {
          // if 包含的块级作用域中的 temp 引用,由于还没声明所以错误。
          // 这里还涉及到变量绑定的问题,
          // 在 ES 6 中如果一个块级作用域中有存在 let 命令,
          // 它所声明的变量就 '绑定' 在这个区域,不受外部的影响。
          // 这就解释了为什么这里的 temp 没有引用全局的 temp = 'global temp' 
          temp = 'abc';  // ReferenceError
    
          // 不存在变量声明提示,所以 temp 在该作用域类的声明不会像 ES 3 中一样被放到作用域最开始的地方声明并赋值为 undefined 
          let temp;
        }
        // ReferenceError: Cannot access uninitialized variable.
    

    9、Argument对象的实例arguments

    作为当前函数的实参

    • arguments.length 实参个数
    • arguments.callee 引用函数本身
    • arguments.caller 引用调用函数的父函数 外层函数

    传参

    function argTest(a, b, c){
    	arguments.length //实际传参(不确定)
    	argTest.length //期望传参 3 (a, b, c)
    }
    

    应用场景

    1. 方法重载
      一个类中定义多个同名方法,具有不同参数类型和个数
    • 普通形式(不知道传入的有多少长度和类型)
    function Test(a, b, c){
    	if(a b c) 
    	else if
    	else
    }
    
    • arguments实现(因为有arguments并且知道长度 所以可以遍历)
    for (var i = 0; i < arguments.length; i++){}
    
    • es6 解构实现(…nums将参数生成了一个新的数组nums)
    function test(...nums){
    	for(var i = 0; i < nums.length; i++){}
    }
    
    1. 递归调用
    • 实现fib函数

    普通版:

    function Fib(n, ac1 = 1, ac2 = 1){
    	if(n<=1) return ac2;
    	return Fib(n-1, ac2, ac1 + ac2)
    }
    

    三目运算版

    function f(num)
    {
        if(num<=0)
       {
          console.log('请输入大于0的正整数');
          return ;
        }
      return num<=2 && num>0 ? 1 : f(num-1)+f(num-2);
    }
    

    arguments版:

    function f(num)
    {
    
        if(num<=0)
       {
          console.log('请输入大于0的正整数');
          return ;
        }
    
      return num<=2 && num>0 ? 1 : arguments.callee(num-1)+arguments.callee(num-2);
    }
    
    
    • 实现阶乘函数

    普通版:

    function factorial(n, total){
    	if(n===1) return total;
    	return factorial(n-1, total * n)
    }
    

    arguments实现:

    function del(num){
    	if(num<=1){
    		return 1
    	}else{
    		return num*arguments.callee(num-1)
    	}
    }
    
    1. 不定参问题
      arguments.length遍历解决
  • 相关阅读:
    ​Docker 数据卷的管理及自动构建docker镜像
    写代码有这16个好习惯,可以减少80%非业务的bug
    启动Docker“Got permission denied while trying to connect to the Docker daemon socket“问题(亲测可用)
    Docker从入门到干活,看这一篇足矣 [建议收藏]
    docker技术入门与精通(2020.12笔记总结)
    MySQL相关 死锁的发生和避免
    使用docker运行zabbixserver
    Cloudflare 是谁?
    扛得住的MySQL数据库架构
    好未来第一届PHP开源技术大会资料分享
  • 原文地址:https://www.cnblogs.com/SiriusZHT/p/14365042.html
Copyright © 2020-2023  润新知