一、ES6+整体篇上


1、块级作用域

<script>
    // 1、值得记录的块级作用域
    // 用let、const声明得变量会有会计作用域,而var不会有可以对比一下下面两个for循环打印得结果
    for(var j = 0; j<=3; j++) {
      console.log(j);
    }
    console.log(j)// 这里打印4

    for(let i = 0; i<=3; i++) {
      console.log(i);
    }
    console.log(i);// 这里报错
</script>

2、垃圾回收机制

<script>
    // 2、垃圾回收机制(GC),内存的分配和回收
    // 2.1、内存分配:当我们在生命变量、函数、对象的时候,系统会自动分配内存
    // 2.2、内存使用:在使用变量和函数的时候会读写内存
    // 2.3、使用完毕,由垃圾回收器自动回收不再使用内存

    // 注意:全局变一般不会回收;局部变量会在不用的时候被自动回收
    

    // 2.4、垃圾回收算法:引用计数法和标记清除法
    // 2.4.1、引用计数法:内存不在使用,也就是看一个对象是否有指向它的引用,没有引用就回收对象
    // 首先记住很重要的一点,引用计数法是IE以前用的方法,现在已经没人用了
    // 基本流程是:
        // 1、跟踪记录被引用的词书;
        // 2、如果被引用了一次,就记录一次,多次引用会累加次数;
        // 3、如果减少一个就减一;
        // 4、如果引用次数为0,则释放内存。

    // 内存泄漏:程序中分配的内存由于有种原因程序未释放或无法释放
    // 下面这种情况就是相互引用,会造成内存泄漏,这种情况下,引用计数法是没法回收的
    // 还有要注意的,不是说这个函数没被使用时就不会开辟空间创建里面的变量,没有被使用时,变量也是被创建的
    // 还有一个问题,就是这种情况下,fn在被使用时内存是不会爆的,只是两个指针互相指向而已
    function fn() {
      let o1 = {};
      let o2 = {};
      o1.a = o2;
      o2.a = o1;
    }

    // 2.4.2、标记清楚法:将不在使用的对象改成无法到达的对象
    // 基本流程是:
        // 1、定时扫描;
        // 2、从根部(JS中就是全局对象)出发开始扫描内存中的对象;
        // 3、凡是能从根部到达的对象,都还是需要使用的;
        // 4、无法由根本出发触及到的对象被标记为不再使用,稍后进行回收。
    // 理解:一种从根本出发,然后变量之间形成一种树状或网状结构,被使用的变量都是相互连接的,
    // 而如果有几个变量游离在这个网状之外,没有任何连接,那么就会被回收,就比如上面那个函数,当他不被使用时,内部即便再怎么
    // 循环都是在网状之外自己玩,和网状主体没有任何连接,也就是说当这个函数不被使用时,从根部的全局无法进入到这个函数,函数就会
    // 被回收。而之前引用计数法不会管你有没有使用,它完全就是你在创建变量时就会计算引用次数,索引循环引用无法被消除。
</script>

标记清除法的感观图

3、闭包

<script>
    // 1、什么时闭包
    // 说白了就是内层函数中访问(使用)外层函数的作用域下的变量即:闭包 = 内层函数 + 外层函数的变量

    // 2、看下简单的闭包
    function outer() {
      const a = 1;
      function inner() {
        consoel.log(a);
      }
      inner()
    }
    outer();

    // 3、闭包的作用:封闭数据,提供操作,外部也可以访问函数内部的变量;下面是使用情况
    // 以前函数内部的变量外部是无法访问的,闭包的方法可以解决该问题
    function test() {
      let i = 1;
      function fn() {
        console.log(i);
      }
      return fn;
    }
    const fun = test();
    fun();

    // 4、闭包的应用:实现数据的私有
    // 4.1、比如,做一个统计函数,实现对函数调用次数的统计
    let i = 0;
    let times = 0;
    function total() {
      i++;
      return i;
    }
    for(let j = 0; j<5; j++) {
      times = total();
    }
    console.log(times);

    // 4.2、缺点,这里的i是全局变量,所有人都可以访问到,所以我们来用闭包解决
    let time  = 0;
    function total2() {
      let i = 0;
      function inner() {
        i++;
        return i;
      }
      return inner;
    }
    const count = total2();
    for(let j = 0; j<10; j++) {
      time = count();
    }
    console.log(time);

  	// 5、这个很好玩,我们来看下整个流程:count是全局变量只有关闭的时候才会被回收,那么count引用了total2
    // 里面的inner函数,所以inner函数被引用了,不会被回收。inner函数里用了i变量,所以i虽然是局部变量,但是也不会被回收
    // 所以这就形成了一个从根部开始的树状结构,也就是标记清除法。同时!!!!!这也就是闭包的不好的一点,内存泄露!!!!!

</script>

4、变量提升

<script>
    // 1、认识变量提升
    // 变量提升不是什么好东西,它是js的一个缺陷,它允许在变量声明之前被访问到

    // 2、例如:不会报错而是打印undefined;
    console.log(num1);
    var num1 = 10;
    // 所以变量提升就是会把var声明的变量提升到当前作用域,注意是当前作用域的最前面,先声明,这样都可以访问到,只不过是undefined;
    // 其次当执行到赋值代码的时候才会赋值;

    // 3、正应为如此,ES6引用了会计作用域,let 和 const 就不会有变量提升,会报错,所以已经不建议用var了
    console.log(num2);
    let num2 = 10;
    console.log(num3);
    let num3 = 10;
</script>

5、函数提升

<script>
    // 1、认识函数提升
    // 函数提升不是缺陷,而是一种机制,比如
    fn();
    function fn() {
      console.log('我是好人!')
    }
    // 函数提升会把所有函数的声明提升到当前作用域的最前面,这里的声明指函数的所有内容,而不是一个undefined

    // 2、其他情况
    console.log(fun);
    fun();
    var fun = function fn2() {
      console.log('我不知道是不是好人!')
    }
    // 这里就是说函数提升了,var声明的变量也提升了,但是函数赋值给fun的操作还没执行,所以打印了undefined
    // 而在调用函数时会报错,因为还没赋值;

</script>

6、函数参数与展开运算符

<script>
  // 1、剩余参数,这里的...是剩余参数运算符
  function fn(...args) {
    // 这里的args是数组
    console.log(args)
    let result = 0;
    args.forEach(item => {
      result += item;
    })
    return result;
  }
  console.log(fn(1,2,3))
  console.log(fn(1,2,3,4))

  // 2、动态参数:arguments
  function fn() {
    // arguments是伪数组
    console.log(arguments);
    let result = 0;
    for(let i = 0; i<arguments.length; i++) {
      result += arguments[i];
    }
    return result;
  }
  console.log(fn(1,2));
  console.log(fn(1,2,3,4,5));

  // 3、剩余参数的其他情况,完全解释了为什么叫剩余参数:
  // args装的就是剩余的参数
  function fn(a,b,...args) {
    console.log(a,b,args)
  }
  fn(1); // 1 undefined []
  fn(1,2,3); // 1 2 [3]
  fn(1,2,3,4); // 1 2 [3,4]

  // 4、注意哦:以后的箭头函数没有arguments,所以以后要常用...args

  // 5、展开运算符
  // 注意也是...但是和剩余参数运算符不是一个东西,看下面
  // 5.1、展开运算符展开数组
  const arr = [1,2,3];
  // 我们很少直接展开,因为没有什么用,所以不要去理解展开后返回的是什么类型的东西,而是直接拿来用
  console.log(...arr);

  // 5.2、拼接数组
  let x = [100,20,500];
  let y = ['lili','jerry'];
  let z = [...x,...y];
  console.log(z);

  // 5.2、求数组最大值
  // Math.max(1,2,3); 不能接收数组,用展开运算符可以解决
  const w = [4,3,7,9,5,7,8];
  console.log(Math.max(...w));

  // 5.3、拼接对象
  const obj1 = {
    a: 1,
    c: 3
  }
  const obj2 = {
    b: 2,
    c: 4
  }
  // 相同的属性,后面的覆盖前面的
  console.log({...obj1,...obj2});
  console.log({...obj2,...obj1});

  // 5.4、合并数组和其他
  let o = ['elva','tom',100,200];
  let p = 'lili';
  let q = [...x,y];
  console.log(q);
</script>

7、箭头函数

<script>
  // 1、当参数只有一个的时候,括号可以省略,如下
  const fn = value => {
    console.log(value);
  }
  fn('wujunjie');

  // 2、只有一行代码的时候,可以省略大括号
  const fn2 = value => console.log(value);
  // 只有返回值得一行代码都不用写return
  const fn3 = value => 2*value;
  fn2('jjwu');
  console.log(fn3(3));

  // 3、还可以直接返回一个对象,但是要用小括号抱着
  const fn4 = name => ({name: name});
  console.log(fn4('jiayou'));

  // 4、箭头函数的参数
  // 有趣的是箭头函数没有动态参数arguments,但是有剩余参数...args
  const fn5 = (...args) => {
    args.forEach(item => {
      console.log(item);
    })
  }
  fn5(1,2,3,4,5);

  // 5、this指向问题
  // 5.1、正常情况:谁调用的函数,this就指向谁
  console.log(this)  //window

  function fn6() {
    console.log(this) //window            kjm 
  }
  fn6()//这里的fn()实际上就是window.fn()

  const obj = {
    name: 'wujunjie',
    sayHello() {
      console.log(this) // obj
    }
  }
  obj.sayHello();

  // 5.2、箭头函数
  // 值得注意的是,箭头函数没有this,那么他会去上一层作用于找this
  const obj2 = {
    name: 'wujunjie',
    sayHello: () => {
      console.log(this) // obj
    }
  }
  obj.sayHello();

  // 5.3、双层嵌套
  const obj3 = {
    name: 'wujunjie',
    sayHi: function() {
      let i = 1;
      const fn = () => {
        console.log(this) // obj
      }
      return fn;
    }
  }
  obj3.sayHi()();

	// 5.4、DOM监听事件
  btn.addEventListener('click', () => {
    console.log(this) // window
  })

  btn.addEventListener('click', function() {
    console.log(this) // btn
  })
</script>

8、数组解构

<script>
  // 1、简单解构实现
  const [max,avg,min] = [10,8,6];
  console.log(max,avg,min);

  // 2、有意思的玩法:交换变量
  let a = 1, b = 2;
  [a, b] = [b, a];
  console.log(a, b);

  // 3、搭配剩余参数运算符
  const [c, d, ...other] = [1,2,3,4,5];
  console.log(c,d,other);

  // 4、多维数组解构
  const [[o, k], [i, j]] = [['o','k'], ['i', 'j']];
  console.log(o,k,i,j);
  
</script>

9、对象解构

<script>
  // 1、解构时的重命名
  const obj = {
    name: 'wujunjie',
    age: 18
  }
  const name = 'huaxia';
  // 我们在解构的时候会提示变量名重复了,所以需要重新给一个变量名
  // const { name, age } = obj;
  const { name: newName, age } = obj;
  console.log(newName,name,age);

  // 2、数组对象解构
  const objArr = [
    {
      name1: 'wujunjie',
      age1: 18
    },
    {
      name1: 'huaixa',
      age1: 17
    }
  ]

  const [{ name1, age1}, {name1: name2, age1:age2}] = objArr;
  console.log(name1,age,name2,age2);

  // 3、多级对象解构
  const pig = {
    name: 'wujunjie',
    family: {
      girlFriend: 'huaxia'
    }
  }

  const { family: { girlFriend } } = pig;
  console.log(girlFriend);

  // 4 有意思的用在函数里
  function render({ data: myData }) {
    console.log(myData);
  }

  const dataObj = {
    data: [
      {
        name: 'wujunjie',
        age: 18
      },
      {
        name: 'huaxia',
        age: '17'
      }
    ]
  }
  render(dataObj);
</script>

10、创建对象方式

<script>
  // 1、第一种方式
  const o = {
    name: 'wujunjie'
  }
  console.log(o);

  // 2、第二种方式,也是创建一个空对象,1创建对象的本质就是2这种方式
  const j = new Object();
  j.name = 'huaxia';
  console.log(j);

  // 3、第三种方式
  const k = new Object({name: 'xiaoxia'});
  console.log(k);

</script>

文章作者: 吴俊杰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吴俊杰 !
 上一篇
二、ES6+整体篇下 二、ES6+整体篇下
ES6+有趣且重要,如果你不懂它,那你一定写不出优雅的代码,你也一定看不懂别人写的代码。它是基础也是基石,学起来吧......
2024-12-03
下一篇 
四、js正则第 4 部分 四、js正则第 4 部分
因为AI的存在,导致js在正则并不是很重要,AI会给你一个完美的正则写法,当然你需要做到的就是能看懂,所以还是做些了解吧......
2024-12-03
  目录