目录
1. 以下代码的输出结果是什么
var a = 1;
console.log(a++ * (++a + a++));
解析:
a++和++a的区别:
- a++ 返回原来的值,++a 返回加1后的值。
- a++ 不能作为左值,而++a可以。
- a++前者是先赋值,然后再自增;++a后者是先自增,后赋值。
a=1; a++ = 1; a=2; ++a = 3; a=3; a++ = 3;
a++ * (++a + a++)
= 1 * ( 3+3 ) = 6
答案:6
2. 最新JS数据类型有哪些?
- 在ES5的时候,我们认知的数据类型确实是 6种:
Number
、String
、Boolean
、undefined
、object
、Null
- ES6 中新增了一种
Symbol
。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。 - 谷歌67版本中还出现了一种
bigInt
。是指安全存储、操作大整数。
目前JS的数据类型共有8种,按照类型来分,可分为基本数据类型和引用数据类型:
基本数据类型:Number
、String
、Boolean
、undefined
、Null
、Symbol
、bigInt
;
引用数据类型:Object
(Object是个大类,包含Array数组、function函数、Date日期等)
基本数据类型与引用数据类型区别
1. 声明变量时内存分配不同
- 基本数据类型:由于占据的空间大小固定且较小,会被存储在
栈
当中,也就是变量访问的位置 - 引用数据类型:存储在
堆
当中,变量访问的其实是一个指针,它指向存储对象的内存地址
2. 因为内存分配不同,在复制变量时结果也不一样
-
基本数据类型复制后,2个变量是独立的互不影响,因为是把值拷贝了一份
-
引用数据类型则是复制了一个指针,2个变量指向的值是该指针所指向的内容,一旦一方修改,另一方也会受到影响
这里实际上涉及了JS的深拷贝和浅拷贝
3. 参数传递不同
- 基本数据类型:把变量里的值传递给参数,之后参数和这个变量互不影响
- 引用数据类型(这里拿函数举例):虽然函数的参数都是按值传递的,但是引用值传递的值是一个内存地址,实参和形参指向的是同一个对象,所以函数内部对这个参数的修改会体现在外部
3.深拷贝与浅拷贝
1.基本数据类型
// 基本数据类型
// 赋值(不是深拷贝也不是浅拷贝)(非要说就是深拷贝)(互不影响)
let a = 5;
let b = a;
b = 3;
console.log(a,b);// 5,3
2.引用数据类型
- 数组和对象的赋值
// 数组与对象的赋值
// 浅拷贝 (藕断丝连)
let arr = [1,2,3];
let newarr = arr;
newarr.push(4);
console.log(arr); // [1,2,3,4]
console.log(newarr); // [1,2,3,4]
- 解构赋值
// 解构赋值
// 针对一维数组和对象可以看作是深拷贝,多维就是浅拷贝
let arr = [1,2,3];
let newarr = [...arr];
newarr.push(4);
console.log(arr); // [1,2,3]
console.log(newarr); // [1,2,3,4]
let arr2 = [[1,2,3],[4,5,6]];
let newarr2= [...arr2];
newarr2.push(888)
console.log(arr2); // [[1,2,3],[4,5,6],888]
console.log(newarr2); // [[1,2,3],[4,5,6],888]
总结:
- 深拷贝:指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响,两个变量相互独立互不影响;(引用数据类型一维的数组和对象的解构赋值)(基本数据类型赋值)
- 浅拷贝:指源对象与拷贝对象之间藕断丝连;(引用数据类型的赋值)
练习题:下面代码输出结果是什么?
var a = {
n: 0,
}
var b = a;
a.n = a = {
n:1};
console.log(b.n);
答案:{ n: 1 }
4. 深拷贝的用法
4.1 利用 JSON内置的方法
// 深拷贝的用法
let list = [
{
id:1,name:'aaa',sex:'01'},
{
id:2,name:'bbb',sex:'02'},
{
id:3,name:'ccc',sex:'01'},
]
// let newlist = list; // 对象赋值是浅拷贝
let newlist = JSON.parse(JSON.stringify(list)); // 利用JSON.parse(JSON.stringify())转化为深拷贝
newlist.push({
id:44});
console.log(list === newlist); // false
该方法是用JSON.stringify将对象序列化为字符串,然后在用JSON.parse将json字符串解析为对象,解析的时候会自己去构建新的内存地址存放新对象。
缺点:
- 会忽略 undefined;
- 会忽略symbol;
- 如果对象的属性为Function,因为JSON格式字符串不支持Function,在序列化的时候会自动删除;
- 诸如 Map, Set, RegExp, Date, ArrayBuffer 和其他内置类型在进行序列化时会丢失;
- 不支持循环引用对象的拷贝。
4.2 标准的深拷贝(递归版深拷贝)
// 标准的数据类型 =>引用数据类型(数组和对象)
function deepCopy(obj) {
// 创建一个新对象
let result = {
}
let keys = Object.keys(obj),
key = null,
temp = null;
for (let i = 0; i < keys.length; i++) {
key = keys[i];
temp = obj[key];
// 如果字段的值也是一个对象则递归操作
if (temp && typeof temp === 'object') {
result[key] = deepCopy(temp);
} else {
// 否则直接赋值给新对象
result[key] = temp;
}
}
return result;
}
let obj={
str:'name',
num:123,
arr:[1,2,3,4],
obj:{
id:1,student:'小米'}
}
let newobj=deepClone(obj);
newobj.str="新值";
console.log(obj === newobj); // false
4.3 终极完美方案:lodash
lodash的_.cloneDeep()支持循环对象、大量的内置类型,对很多细节都处理的比较不错,推荐使用。
var _ = require('lodash');
var obj1 = {
a: 1,
b: {
f: {
g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
那么,lodash是如何解决循环引用这个问题的?查阅源码:
会发现lodash是用一个栈记录了所有被拷贝的引用值,如果再次碰到同样的引用值的时候,不会再去拷贝一遍,而是利用之前已经拷贝好的。
综上,在生产环境,我们推荐使用lodash的cloneDeep()。
5. 从哪些点做性能优化?
- 页面加载性能
- 减少http请求(精灵图,文件的合并)
- 减少文件大小(资源压缩,图片压缩,代码压缩)
- CDN(引用第三方库,大文件,大图)
- SSR 服务端渲染,预渲染
- 懒加载
- 分包
- 动画与操作性能
- 减少dom操作,避免回流,文档碎片
未完待续,持续更新中,点赞收藏加关注,方便下次回顾哦~