美文网首页
2019-01-07

2019-01-07

作者: 天空之城_2692 | 来源:发表于2019-01-07 01:11 被阅读1次

    闭包

    什么叫闭包?
    闭包的定义其实很简单:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。

    function f1(){
        var n = 999;
        nAdd = function(){
            n+=1;
        }
        function f2(){
            console.log(n)
        }
        return f2;
    }
    
    var result = f1();
    result(); //999
    nAdd();
    result(); //1000
    
    

    使用闭包的注意点
    由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

    for (var i = 1; i <= 5; i++) {
      setTimeout(function timer() {
        console.log(i)
      }, i * 1000)
    }//输出一堆6
    

    首先因为 setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆 6。
    let不不允许在相同作⽤用域内,重复声明同⼀一个变量量
    只要块级作⽤用域内存在let命令,它所声明的变量量就“绑定”这个区域,不不再受外部的影响,不不存在变 量量提升

    for (let i = 1; i <= 5; i++) {
      setTimeout(function timer() {
        console.log(i)
      }, i * 1000)
    }//1,2,3,4,5
    var a = [];
    for (var i = 0; i < 10; i++) {//每执行一遍for,都会重新声明一遍变量i
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 10
    
    a[0],…a[9]=ƒ () {
        console.log(i);
      }
    

    什么是浅拷贝?如何实现浅拷贝?什么是深拷贝?如何实现深拷贝?

    在我之前的文章中有关于对象类型在赋值的过程中其实是复制了地址,从而会导致改变了一方其他也都被改变的情况。通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个情况。

    let a = {
      age: 1
    }
    let b = a
    a.age = 2
    console.log(b.age) // 2
    

    浅拷贝

    首先可以通过 Object.assign 来解决这个问题,很多人认为这个函数是用来深拷贝的。其实并不是,Object.assign 只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。

    let a = {
      age: 1
    }
    let b = Object.assign({}, a)
    a.age = 2
    console.log(b.age) // 1
    

    另外我们还可以通过展开运算符 ... 来实现浅拷贝

    let a = {
      age: 1
    }
    let b = { ...a }
    a.age = 2
    console.log(b.age) // 1
    

    通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就可能需要使用到深拷贝了

    let a = {
      age: 1,
      jobs: {
        first: 'FE'
      }
    }
    let b = { ...a }
    a.jobs.first = 'native'
    console.log(b.jobs.first) // native
    

    浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到最开始的话题了,两者享有相同的地址。要解决这个问题,我们就得使用深拷贝了。

    深拷贝
    这个问题通常可以通过 JSON.parse(JSON.stringify(object)) 来解决。

    let a = {
      age: 1,
      jobs: {
        first: 'FE'
      }
    }
    let b = JSON.parse(JSON.stringify(a))
    a.jobs.first = 'native'
    console.log(b.jobs.first) // FE
    

    但是该方法也是有局限性的:

    • 会忽略 undefined
    • 会忽略 symbol
    • 不能序列化函数
    • 不能解决循环引用的对象
    let obj = {
      a: 1,
      b: {
        c: 2,
        d: 3,
      },
    }
    obj.c = obj.b
    obj.e = obj.a
    obj.b.c = obj.c
    obj.b.d = obj.b
    obj.b.e = obj.b.c
    let newObj = JSON.parse(JSON.stringify(obj))
    console.log(newObj)
    

    如果你有这么一个循环引用对象,你会发现并不能通过该方法实现深拷贝

    在遇到函数、 undefined 或者 symbol 的时候,该对象也不能正常的序列化

    let a = {
      age: undefined,
      sex: Symbol('male'),
      jobs: function() {},
      name: 'yck'
    }
    let b = JSON.parse(JSON.stringify(a))
    console.log(b) // {name: "yck"}
    

    你会发现在上述情况中,该方法会忽略掉函数和 undefined 。

    但是在通常情况下,复杂数据都是可以序列化的,所以这个函数可以解决大部分问题。

    如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel

    function structuralClone(obj) {
      return new Promise(resolve => {
        const { port1, port2 } = new MessageChannel()
        port2.onmessage = ev => resolve(ev.data)
        port1.postMessage(obj)
      })
    }
    
    var obj = {
      a: 1,
      b: {
        c: 2
      }
    }
    
    obj.b.d = obj.b
    
    // 注意该方法是异步的
    // 可以处理 undefined 和循环引用对象
    const test = async () => {
      const clone = await structuralClone(obj)
      console.log(clone)
    }
    test()
    

    当然你可能想自己来实现一个深拷贝,但是其实实现一个深拷贝是很困难的,需要我们考虑好多种边界情况,比如原型链如何处理、DOM 如何处理等等,所以这里我们实现的深拷贝只是简易版,并且我其实更推荐使用 lodash 的深拷贝函数。

    function deepClone(obj) {
      function isObject(o) {
        return (typeof o === 'object' || typeof o === 'function') && o !== null
      }
    
      if (!isObject(obj)) {
        throw new Error('非对象')
      }
    
      let isArray = Array.isArray(obj)
      let newObj = isArray ? [...obj] : { ...obj }
      Reflect.ownKeys(newObj).forEach(key => {
        newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
      })
    
      return newObj
    }
    
    let obj = {
      a: [1, 2, 3],
      b: {
        c: 2,
        d: 3
      }
    }
    let newObj = deepClone(obj)
    newObj.b.c = 1
    console.log(obj.b.c) // 2
    

    (function(j){})(i)

    今天在看代码时看到js语句是这么写的,看上去(function(j){})(i)比较神奇,其实很简单,第一个括号定义了一个匿名函数,后一个括号是指调用了这个函数,并传入参数i。当然这个匿名函数接受一个参数,命名为j。就是这么简单。

    相关文章

      网友评论

          本文标题:2019-01-07

          本文链接:https://www.haomeiwen.com/subject/tfhdrqtx.html