前端面试题JS篇(一)—— 知识点回顾

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种:NumberStringBooleanundefinedobjectNull
  • ES6 中新增了一种 Symbol 。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。
  • 谷歌67版本中还出现了一种 bigInt。是指安全存储、操作大整数。

目前JS的数据类型共有8种,按照类型来分,可分为基本数据类型和引用数据类型:
基本数据类型NumberStringBooleanundefinedNullSymbolbigInt
引用数据类型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操作,避免回流,文档碎片

未完待续,持续更新中,点赞收藏加关注,方便下次回顾哦~

猜你喜欢

转载自blog.csdn.net/qq_38970408/article/details/127773746