数据类型
null 和 undefined 的区别
浮点数精度
请问对表达式 0.1 + 0.2 === 0.3
求值,结果是什么?
0.1 加上 0.2 ,数学上结果应该是 0.3 ,因此表达式的值应该是 true 。如果你这样回答,就错啦!正确答案应该是 false 。这是为什么呢?
众所周知,计算机内部以二进制处理数据,浮点数也不例外。准确来说,计算机内部采用二进制版的科学计数法来表示浮点数。因为在二进制中,1 不能被 10 整除,十进制的 0.1 无法用二进制小数精确表示。
类比 $\frac{1}{3}$ 在十进制中,只能用无穷无尽的小数 0.3333…… 不断逼近;$\frac{1}{10}$ 在二进制中也是一个无限循环的小数,也只能不断逼近。但由于计算机表示浮点数小数部分的位数是有限的,这样就会有误差。
误差在计算的过程中会不断积累,以致出现 0.1 + 0.2 不等于 0.3 的现象,使用不当就会酿成大祸。为了避免浮点数精度不足的坑,我们可以采用高精度的十进制类库( decimal )。
更多关于浮点数精度问题,可以参考这篇文章:IEEE-754浮点数那些坑 。
数组去重有哪些方法
- 双重循环(低效)
- 利用indexOf include findIndex等方法判断(低效)
- 利用Set进行去重(需要注意顺序)
- 先排序,在遍历
数组的常用方法有哪些
- forEach
- map
- filter
- reduce
- every
- some
- shift
- unshift
- push
- pop
- splice
Set的常用方法有哪些
- add
- has
- delete
- clear
- entries
- forEach
- values
如何深拷贝对象
- JSON.parse(JSON.stringify(xxx))
- Object.assign(target, source1, source2)
- 递归拷贝(环形应用)
如何将对象属性设置为不可修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
const obj = {
foo: 1,
};
Object.defineProperty(obj, 'foo', {
writable: false, // 不可写
});
console.log(obj.foo);
obj.foo = 2;
console.log(obj.foo);
delete obj.foo;
console.log(obj.foo);
|
- writable:是否可以写,false 表示不能修改,但可以删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
const obj = {
foo: 1,
};
Object.defineProperty(obj, 'foo', {
writable: false, // 不可写
configurable: false, // 不可配置
});
console.log(obj.foo);
obj.foo = 2;
console.log(obj.foo);
delete obj.foo;
console.log(obj.foo);
// 报错
Object.defineProperty(obj, 'foo', {
writable: true, // 不可写
});
|
- configurable:是否可以配置,false表示不能调整属性,也不能删除
- enumerable:是否可以被遍历到
语法
for in VS for of
请问 for in 循环和 for of 循环有什么区别?
- for in 循环用于遍历对象属性;
- for of 循环用于遍历可迭代对象,即遍历容器对象中保存的元素;
for in 循环以任意顺序遍历一个对象的可枚举属性,包括继承的可枚举属性,Symbol除外:
1
2
3
4
5
6
7
8
9
10
11
|
const me = {
name: 'fasionchan',
org: '小菜学编程',
}
for (let field in me) {
console.log(`${field}=${me[field]}`)
}
// name=fasionchan
// org=小菜学编程
|
for of 循环用于遍历可迭代对象,包括 Array ,Map ,Set ,String ,TypedArray ,arguments 对象等等。
1
2
3
4
5
6
7
8
9
|
const numbers = [10, 20, 30];
for (let number of numbers) {
console.log(number)
}
// 10
// 20
// 30
|
let var const 区别
- let: 局部作用域
- var: 全局作用域
- const: 常量
这个例子,变量 i 在循环外申明,生命周期覆盖整个循环。因此,改动对闭包函数可见:
1
2
3
4
5
6
7
8
9
10
11
12
|
const buttons = [];
let i = 0;
while (i<5) {
buttons.push(<Button
onClick={() => {
console.log(`点击按钮${i}`)
}}
>{`按钮${i}`}</Button>)
i++
}
// 不管点哪个按钮都是输出5
|
而这个例子,变量 i 在循环中申明,具有块级作用域。它的生命周期仅限于当前循环体,在后续循环中的改变,对当前循环体中的闭包函数不可见:
1
2
3
4
5
6
7
8
9
10
|
const buttons = [];
for (let i=0; i<5; i++) {
buttons.push(<Button
onClick={() => {
console.log(`点击按钮${i}`)
}}
>{`按钮${i}`}</Button>)
}
// 点某个按钮输出它的序号
|
1
2
3
4
5
|
const buttons = new Array(5).fill(0).map((_, i) => <Button
onClick={() => {
console.log(`点击按钮${i}`)
}}
>{`按钮${i}`}</Button>)
|
箭头函数和普通函数的区别
- this指向不同:箭头函数 this 指向外层代码块的 this ;而普通函数的 this 指向函数调用者;call 、apply 和 bind 可以修改普通函数的 this 指向,但不能修改箭头函数的 this 指向;
- 箭头函数没有原型,不能用作构造函数,而普通函数可以;
代码题
1
2
3
4
5
6
7
8
9
10
11
|
function foo() {
let id = 10;
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 20;
foo();
foo.call({ id: 30 });
|
- foo 第一次调用,this 指向的是 windows 对象,输出 20 ;
- foo 第二次调用,this 指向的是对象
{id : 30 }
,输出 30 ;
Promise
以下两个程序分别输出什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function later(ms) {
return new Promise(r => setTimeout(r, ms));
}
async function demo() {
const start = new Date();
const t1 = later(1000);
const t2 = later(1000);
await t1;
await t2;
// 这里会输出多少耗时?
console.log(new Date() - start);
}
demo();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function later(ms) {
return new Promise(r => setTimeout(r, ms));
}
async function demo() {
const start = new Date();
await later(1000);
await later(1000);
// 这里会输出多少耗时?
console.log(new Date() - start);
}
demo();
|
【小菜笔记】系列文章首发于公众号【小菜学编程】,敬请关注: