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