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] = numberrest => 2, 3, 4, 5
const user = { name: 'pig', age: '18', sex: 'female'} const { name, ...rest } = userrest => { 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