js常見(jiàn)的坑


title: JS常見(jiàn)的‘坑’

ES6小坑

1.ES6為參數(shù)提供了默認(rèn)值,在定義函數(shù)的同時(shí)初始化了這個(gè)參數(shù),以便在參數(shù)沒(méi)有被傳遞進(jìn)去使用

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

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

相對(duì)于ES5,arg = arg || 100這樣設(shè)置參數(shù)默認(rèn)值的時(shí)候,傳入0時(shí),就會(huì)輸出false

2.ES6重構(gòu)代碼

第一題:
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.‘...’

  • 組裝對(duì)象或者數(shù)組

    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'}

  • 獲取數(shù)組或者對(duì)象中除了前幾項(xiàng)的其他項(xiàng)或者除了某項(xiàng)中的其他項(xiàng)

    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' }

  • 組合新對(duì)象,當(dāng)兩個(gè)對(duì)象合并有重復(fù)的時(shí)候,右面的屬性名覆蓋左面屬性名

    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)
```

控制臺(tái)輸出:2,3,5,4,1

  • 這道題主要為js的運(yùn)行機(jī)制,首先setTimeout,設(shè)置了一個(gè)定時(shí),定時(shí)結(jié)束后才會(huì)將這個(gè)函數(shù)放到任務(wù)隊(duì)列里面,所以最后輸出

  • promise的函數(shù)均是立即執(zhí)行,所以直接輸出2,3

  • then放到當(dāng)前的tick最后,所以先輸出5后在輸出4,最后的定時(shí)的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)
    })
    

控制臺(tái)輸出:then:success1

  • promise只會(huì)識(shí)別一種狀態(tài),當(dāng)識(shí)別到了resolve狀態(tài)時(shí),后面的狀態(tài)就忽略了,resolve代表成功,所以走的是then,輸出success1

JS小坑

1.循環(huán),閉包,加延時(shí)

```
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

  • 因?yàn)閒or循環(huán)是一次性走完的,但是添加了異步的操作,當(dāng)for循環(huán)結(jié)束后,i的作用域還存在,而且在他的內(nèi)部還可以訪問(wèn),所以就等于5了

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

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

  • 添加了閉包操作,沒(méi)執(zhí)行一次i循環(huán),就會(huì)一秒后輸出i值

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

輸出:5,5,5,5,5

  • 添加了閉包操作,但是()內(nèi)沒(méi)有添加閉包值,沒(méi)有對(duì)i保持引用,所以還是輸出5

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

立即輸出,沒(méi)有延時(shí):0,1,2,3,4

  • setTimeout這里傳遞了一個(gè)立即執(zhí)行函數(shù),所以后面的延時(shí)時(shí)間不進(jìn)行考慮,所以立即輸出0,1,2,3,4

2.循環(huán),閉包

(1)循環(huán)
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的時(shí)候只是定義了函數(shù),在執(zhí)行時(shí),i已經(jīng)變成了4
  • f2() => 16
  • f3() => 16

(2)將(1)中的循環(huán)改成閉包

```
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時(shí),var的作用域是在count下,let轉(zhuǎn)換成塊級(jí)作用域,所以在for循環(huán){}下,所以當(dāng)我們?cè)趘ar的基礎(chǔ)上執(zhí)行函數(shù)count,作用域變成了結(jié)束循環(huán)的i值,而let的作用域保持在每一次循環(huán)的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.延時(shí)setTimeout

(1)隊(duì)列執(zhí)行

```
var flag = true
setTimeout ( () => {
    flag = false
}, 1000)
while(flag) {}
alert('1')
```
  • 永遠(yuǎn)不會(huì)彈出1,因?yàn)樵趙hile中是死循環(huán),雖然setTimeout只延時(shí)了一秒執(zhí)行,但是主隊(duì)列中的while會(huì)永遠(yuǎn)的執(zhí)行下去,所在settimeout的隊(duì)列永遠(yuǎn)不會(huì)被執(zhí)行,代碼阻塞在while循環(huán)這面
    (2)內(nèi)存泄漏
```
setTimeout (function test1 () {
    var a = 1
    console.log(`a:$(a)`)
},0)

setTimeout ((function test2 () {
    var b = 1
    console.log(`b:$`)
}).toString(), 0)
```
  • 兩個(gè)定時(shí)延時(shí),第一個(gè)傳入的參數(shù)是函數(shù),函數(shù)回調(diào)之后,test1()函數(shù)被摧毀,內(nèi)存被釋放
  • 第二傳入的參數(shù)是字符串,會(huì)被解析成eval(string),他是一個(gè)全局變量函數(shù),不存在被摧毀,始終占據(jù)內(nèi)存,造成了內(nèi)存泄漏。

4.this

(1)在return中返回

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

控制臺(tái)輸出 {} , undifined

  • return返回的是一個(gè)空對(duì)象,所以new也是為空

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

控制臺(tái)輸出 1

  • return返回的是一個(gè)非空對(duì)象,所以new實(shí)例getName()方法

(2)this被誰(shuí)調(diào)用就指向誰(shuí)

```
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()
```

控制臺(tái)輸出 2 0

  • baz()是被bar調(diào)用,所以this指向bar中的x2

  • a()是被windows調(diào)用,所以指向windows下的x0

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

控制臺(tái)輸出 0 0

  • foo()()是函數(shù)調(diào)用,是全局的指向,所以是windows下的x0
  • a()是被windows調(diào)用,所以指向windows下的x0

(3) new實(shí)例對(duì)象中的this

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

控制臺(tái)輸出 zhu yang

  • a重新實(shí)例了person對(duì)象,所以this指向當(dāng)前對(duì)象下的值

(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)
```

控制臺(tái)輸出 1 0 1

  • o里面新添入x屬性和m屬性,m屬性賦值一個(gè)函數(shù),m屬于o,當(dāng)o.m()的時(shí)候,m是被o調(diào)用的,所以m里面的this指向是o里面的x,也就是1
  • o.m.apply(),apply的作用是給函數(shù)設(shè)置一個(gè)this指向,當(dāng)傳入的參數(shù)沒(méi)有,或者為null或者undifined的時(shí)候,指向全局,所以相當(dāng)于在windows下調(diào)用,所以this指向x0
  • apply()中參數(shù)存在,指向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()
```

控制臺(tái)輸出 1 0 1

  • 第一個(gè)()輸出的值是foo調(diào)用,所以是1
  • 閉包返回的this調(diào)用的不是對(duì)象而是下一個(gè)函數(shù),所以指向的是全局,所以是0
  • 閉包返回的that是接受了上一步的this,所以是1

5.自執(zhí)行

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

控制臺(tái)輸出: 10

  • 因?yàn)橄旅娴淖詧?zhí)行是傳入的foo這個(gè)函數(shù),然后里面調(diào)用了這個(gè)函數(shù),他能拿到的作用域只有外部的,里面的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

  • 因?yàn)樵诤瘮?shù)內(nèi)部,會(huì)在內(nèi)部先找a,找不到a就會(huì)去外面尋找,當(dāng)執(zhí)行的時(shí)候,會(huì)查詢當(dāng)前作用域的變量,但不會(huì)立即賦值,簡(jiǎn)單來(lái)說(shuō):函數(shù)執(zhí)行時(shí),在{}內(nèi)部產(chǎn)生一個(gè)作用域,查找該{}內(nèi)的所有變量,函數(shù)名,常量這些,如果找不到,才會(huì)去外面找對(duì)應(yīng)的,變量的提升就是,變量在申明之前也可以使用,但是值是undefined
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些閱讀 2,144評(píng)論 0 2
  • 工廠模式類(lèi)似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡(jiǎn)單...
    舟漁行舟閱讀 8,131評(píng)論 2 17
  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,258評(píng)論 0 38
  • 最近工作相對(duì)輕松,鑒于Swift 3.0與iOS10的發(fā)布,準(zhǔn)備著手學(xué)習(xí)一下Swift3.0在iOS開(kāi)發(fā)當(dāng)中的應(yīng)用...
    sam0723閱讀 483評(píng)論 0 0
  • 昨晚看了兩段Daily Dictation后,又做了10個(gè)雅思作文句子的回譯練習(xí)。 回譯也就是中英互譯,先把英文翻...
    Cliff的自由之路閱讀 15,175評(píng)論 8 3

友情鏈接更多精彩內(nèi)容