js常见的坑


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常见的坑)