美文网首页
js常见的坑

js常见的坑

作者: 送你一堆小心心 | 来源:发表于2018-09-28 15:02 被阅读0次

    title: JS常见的‘坑’

    ES6小坑

    1.ES6为参数提供了默认值,在定义函数的同时初始化了这个参数,以便在参数没有被传递进去使用

    ```
    function test (arg = 100) {
        console.log(num)
    }
    ```
    

    test(0) => 0
    test() => 100
    test(200) => 200

    相对于ES5,arg = arg || 100这样设置参数默认值的时候,传入0时,就会输出false

    2.ES6重构代码

    第一题:
    var jsonParse = require('body-parser').jsonParse
    

    import { jsonParse } from 'body-parser'

    第二题:
    var body = request.body
    var username = body.username
    var password = body.password
    

    const {body, body: { username , password} } = request

    3.‘...’

    • 组装对象或者数组

      const color = ['red', 'yellow', 'blue']
      const colorful = [...color, 'green', 'pink']
      

      colorful => ['red', 'yellow', 'blue', 'green', 'pink']

      const people = { name: 'pig', sex: 'female'}
      const newpeople = { ...people, age: '18'}
      

      newpeople => { name: 'pig', sex: 'female', age: '18'}

    • 获取数组或者对象中除了前几项的其他项或者除了某项中的其他项

      const number = [1, 2, 3, 4, 5]
      const [first, ...rest] = number
      

      rest => 2, 3, 4, 5

      const user = { name: 'pig', age: '18', sex: 'female'}
      const { name, ...rest } = user
      

      rest => { age: '18', sex: 'female' }

    • 组合新对象,当两个对象合并有重复的时候,右面的属性名覆盖左面属性名

      const first = { a: 1, b: 2, c: 3}
      const second = { c: 6, d: 4, e: 5}
      const tatol = { ...fisrt, ...second }
      

      total => { a: 1, b: 2, c: 6, d: 4, e: 5}

    4.promise

    ```
    setTimeout ( () => {
      console.log(1)
    },0)
    new Promise ( (resolve) => {
      console.log(2)
      for (var i = 0; i < 10000; i++) {
        i == 9999 && resolve()
      }
      console.log(3)
    }).then(() => {
      console.log(4)
    })
    console.log(5)
    ```
    

    控制台输出:2,3,5,4,1

    • 这道题主要为js的运行机制,首先setTimeout,设置了一个定时,定时结束后才会将这个函数放到任务队列里面,所以最后输出

    • promise的函数均是立即执行,所以直接输出2,3

    • then放到当前的tick最后,所以先输出5后在输出4,最后的定时的1

      const promise = new Promise((resolve, reject) => {
        resolve('success1')
        reject('error')
        resolve('success2')
      })
      promise.then((res) => {
        console.log('then: ', res)
      })
      .catch((err) => {
        console.log('catch: ', err)
      })
      

    控制台输出:then:success1

    • promise只会识别一种状态,当识别到了resolve状态时,后面的状态就忽略了,resolve代表成功,所以走的是then,输出success1

    JS小坑

    1.循环,闭包,加延时

    ```
    for (var i = 0; i < 5; i ++) {
        console.log(i)
    }
    ```
    

    输出:0,1,2,3,4

    • 正常输出值

      for (var i = 0; i < 5; i++) {
          setTimeout( () => {
              console.log(i)
          }, 1000 * i)
      }
      

    输出:5,5,5,5,5

    • 因为for循环是一次性走完的,但是添加了异步的操作,当for循环结束后,i的作用域还存在,而且在他的内部还可以访问,所以就等于5了

      for (var i = 0; i < 5; i++) {
          ((i) => {
              setTimeout(() => {
                  console.log(i)
              }, i * 1000)
          })(i)
      }
      

    输出:0,1,2,3,4

    • 添加了闭包操作,没执行一次i循环,就会一秒后输出i值

      for (var i = 0; i < 5; i++) {
          (() => {
              setTimeout(() => {
                  console.log(i)
              }, i * 1000)
          })(i)
      }
      

    输出:5,5,5,5,5

    • 添加了闭包操作,但是()内没有添加闭包值,没有对i保持引用,所以还是输出5

      for (var i = 0; i < 5; i++) {
          setTimeout(((i) => {
              console.log(i)
          })(i), i * 1000)
      }
      

    立即输出,没有延时:0,1,2,3,4

    • setTimeout这里传递了一个立即执行函数,所以后面的延时时间不进行考虑,所以立即输出0,1,2,3,4

    2.循环,闭包

    (1)循环
    test6 () { var results = this.count(); var f1 = results[0]; var f2 = results[1]; var f3 = results[2]; }, count() { var arr = []; for (var i = 1; i <= 3; i++) { arr.push(function () { return i * i; }); } return arr; }

    • f1 => function () { return i * i }
    • f2 => function () { return i * i }
    • f3 => function () { return i * i }
    • f1() => 16 push的时候只是定义了函数,在执行时,i已经变成了4
    • f2() => 16
    • f3() => 16

    (2)将(1)中的循环改成闭包

    ```
    test6 () {
        var results = this.count();
        var f1 = results[0];
        var f2 = results[1];
        var f3 = results[2];
    },
    count() {
        var arr = [];
        for (var i = 1; i <= 3; i++) {
            arr.push((function(n){
                return function () {
                    return n * n;
                }
            })(i));
            // console.log(i)
        }
        return arr;
    }   
    ```
    
    • f1 => function () { return n * n }
    • f2 => function () { return n * n }
    • f3 => function () { return n * n }
    • f1() => 1
    • f2() => 4
    • f3() => 9

    (3) 将(1)中的var改成let时,var的作用域是在count下,let转换成块级作用域,所以在for循环{}下,所以当我们在var的基础上执行函数count,作用域变成了结束循环的i值,而let的作用域保持在每一次循环的i

    ```
    test6 () {
        var results = this.count();
        var f1 = results[0];
        var f2 = results[1];
        var f3 = results[2];
        console.log(f1())
        console.log(f2())
        console.log(f3())
    },
    count() {
        var arr = [];
        for (let i = 1; i <= 3; i++) {
            arr.push(function () {
                return i * i;
            });
        }
        return arr;
    }   
    ```
    

    3.延时setTimeout

    (1)队列执行

    ```
    var flag = true
    setTimeout ( () => {
        flag = false
    }, 1000)
    while(flag) {}
    alert('1')
    ```
    
    • 永远不会弹出1,因为在while中是死循环,虽然setTimeout只延时了一秒执行,但是主队列中的while会永远的执行下去,所在settimeout的队列永远不会被执行,代码阻塞在while循环这面
      (2)内存泄漏
    ```
    setTimeout (function test1 () {
        var a = 1
        console.log(`a:$(a)`)
    },0)
    
    setTimeout ((function test2 () {
        var b = 1
        console.log(`b:${b}`)
    }).toString(), 0)
    ```
    
    • 两个定时延时,第一个传入的参数是函数,函数回调之后,test1()函数被摧毁,内存被释放
    • 第二传入的参数是字符串,会被解析成eval(string),他是一个全局变量函数,不存在被摧毁,始终占据内存,造成了内存泄漏。

    4.this

    (1)在return中返回

    ```
    function getName() {
        this.name = 1
        return {}
    }
    var a = new getName()
    console.log(a)
    console.log(a.name)
    ```
    

    控制台输出 {} , undifined

    • return返回的是一个空对象,所以new也是为空

      function getName () {
          this.name = 1
          return 2
      }
      var d = new getName()
      console.log(d.name)
      

    控制台输出 1

    • return返回的是一个非空对象,所以new实例getName()方法

    (2)this被谁调用就指向谁

    ```
    var x = 0 
    var foo = {
        x: 1,
        bar: {
            x: 2,
            baz: function () {
                console.log(this.x)
            }
        }
    }
    var a = foo.bar.baz
    foo.bar.baz()
    a()
    ```
    

    控制台输出 2 0

    • baz()是被bar调用,所以this指向bar中的x2

    • a()是被windows调用,所以指向windows下的x0

      var x = 0
      function foo () {
          var x = 1
          function baz(){
              console.log(this.x)
          }
          return baz
      }
      var a = foo()
      foo()()
      a()
      

    控制台输出 0 0

    • foo()()是函数调用,是全局的指向,所以是windows下的x0
    • a()是被windows调用,所以指向windows下的x0

    (3) new实例对象中的this

    ```
    var name = 'yang'
    function person() {
        this.name = 'zhu'
    }
    var a = new person()
    console.log(a.name)
    console.log(name)
    ```
    

    控制台输出 zhu yang

    • a重新实例了person对象,所以this指向当前对象下的值

    (4)apply

    ```
    var x = 0
    function test() {
        console.log(this.x)
    }
    var o = {}
    o.x = 1
    o.m = test
    o.m()
    o.m.apply()
    o.m.apply(o)
    ```
    

    控制台输出 1 0 1

    • o里面新添入x属性和m属性,m属性赋值一个函数,m属于o,当o.m()的时候,m是被o调用的,所以m里面的this指向是o里面的x,也就是1
    • o.m.apply(),apply的作用是给函数设置一个this指向,当传入的参数没有,或者为null或者undifined的时候,指向全局,所以相当于在windows下调用,所以this指向x0
    • apply()中参数存在,指向o中的x为1

    (5)闭包中的this

    ```
    var x = 0
    var foo =  {
        x: 1,
        bar: function (){
            console.log(this.x)
            var that = this
            return function () {
                console.log(this.x)
                console.log(that.x)
            }
        }
    }
    foo.bar()()      =》      let bar = foo() bar()
    ```
    

    控制台输出 1 0 1

    • 第一个()输出的值是foo调用,所以是1
    • 闭包返回的this调用的不是对象而是下一个函数,所以指向的是全局,所以是0
    • 闭包返回的that是接受了上一步的this,所以是1

    5.自执行

    ```
        var z = 10;
        function foo() {
            console.log(z)
        }
        (function (funArg){
            var z = 20;
            funArg ()
        })(foo)
    ```
    

    控制台输出: 10

    • 因为下面的自执行是传入的foo这个函数,然后里面调用了这个函数,他能拿到的作用域只有外部的,里面的z不管他的事儿

    6.变量

    ```
        var a = 100
        function function_name(argument) {
            var b = 2 * a
            var a = 200
            var c = a / 2
            alert(b)
            alert(c)
        }
        function_name()
    ```
    

    alert弹出,NaN , 100

    • 因为在函数内部,会在内部先找a,找不到a就会去外面寻找,当执行的时候,会查询当前作用域的变量,但不会立即赋值,简单来说:函数执行时,在{}内部产生一个作用域,查找该{}内的所有变量,函数名,常量这些,如果找不到,才会去外面找对应的,变量的提升就是,变量在申明之前也可以使用,但是值是undefined

    相关文章

      网友评论

          本文标题:js常见的坑

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