面经八股刷

177 阅读10分钟

刷面经

day1( 商汤科技):


var a = function() {console.log(11)};
var b = function() {console.log(11)};
console.log( a==b ); //false
 
console.log( {}=={} ); //false
console.log( []==[] ); //false


堆、栈:

栈:自动分配内存空间,系统自动释放,一级缓存,被调用时处于存储空间,调用完释放 堆:动态内存分配,大小不定不会自动释放,存放二级缓存,生命周期由垃圾回收算法决定。

js中基本数据类型和引用数据类型

基本类型:存放栈内存,数据大小确定,内存空间可分配 Undefined Null Boolean Number String 按值存放,可以直接访问 引用类型: 存放堆内存,每个空间大小不一样,特定分配 访问引用数据类型(对象、数组、函数),首先从栈中获得该对象的地址指针,再从堆内存中获得所需数据。

var a = function(){}; var b = function(){}; a==b 为false

变量a实际保存的是指向堆内存对象的一个指针,b保存的另一个对象的指针,虽然两个对象值一样,但是他们是两个独立对象,占两份内存空间

如果

var a = {}; var b = a

它们都指向堆内存中同一个对象;所以 a==b 为 true。

传值与传址

var a = [1,2,3,4,5];
var b = a;
var c = a[0];
console.log(b);  // [1,2,3,4,5]
console.log(c);  // 1
 
b[4] = 6;
c = 7;
console.log(a[4]);  //6
console.log(a[0]);  //1

b变a变,c变a不变,这就是传值与传址的区别。a是数组属于引用类型,a传给b的是栈中地址不是堆内对象,c仅是a堆内存的一个数值保存在栈中。 b修改时会根据地址取a堆内存修改,c直接在栈中修改。

浅拷贝

对于基本数据类型的拷贝,并没有深浅拷贝的区别 如果属性是对象或数组时,这时我们传递的只是一个地址,子对象访问该属性的时候会根据地址回溯到父对象指向的堆内存中,二者关联,两者属性值会指向同一内存

Object.assign

Object.assign 是Es6中Object的一个方法,该方法用于JS对象合并,可以实现浅拷贝

target 指的是目标对象,sources指的是源对象 Object.assign(target,...sources)

  • 如果拷贝 基本数据类型 拷贝的就是基本数据类型的值--老死不相往来
  • 如果拷贝引用类型 拷贝的是内存地址--关系依然存在

扩展运算符

扩展运算符可以在构造字面量对象的时候,进行属性的拷贝

let obj1 = {a:1 , b:{c:1}}
let obj2 = {...obj1}

数组浅拷贝

Array.prototype.slice slice()方法是JS数组方法,可以从已有数组中返回选定元素,不改变原数组,只会返回一个浅拷贝了原数组的元素的一个新数组,两个数组不受影响

//两个参数都不写,就可以实现一个数组的浅拷贝:
const animals = [.....]
console.log(animals.slice())
let arr = [1, 2, 3, 4];
let newArr = arr.slice()
console.log(arr.slice() === arr); // false
arr.push(5) ​
console.log(arr); // [1, 2, 3, 4, 5]  
console.log(newArr); // [1, 2, 3, 4]

Array.prototype.concat合并两个或多个数组,此方法不会更改原始数组,而是返回一个新数组如果省略了所有参数,则会返回调用此方法的现存数组的一个浅拷贝(新数组)

let arr = [1,2,3,4]
let newArr = arr.concat([5])
console.log(arr.concat() === arr); // false

手写实现浅拷贝

function shallowCopy(params) {
   if (!params || typeof params !== 'object') {
       return params
   }
   let newObject = Array.isArray(params) ? [] : {}
   for (let key in params) {
       if (params.hasOwnProperty(key)) {
           newObject[key] = params[key]
       }
   }
   return newObject
}
let params = { a: 1, b: { c: 1 } }
let newObj = shallowCopy(params)



console.log(params)
console.log(newObj)
params.a = 2222
params.b.c = 666
// 拷贝对象中---基本类型老死不相往来,引用类型藕断丝连
console.log(params)
console.log(newObj)

总结: 看到所有浅拷贝只能拷贝一层对象。存在对象嵌套,浅拷贝就无能为力,深拷贝就是为了解决多层对象嵌套问题,实现彻底拷贝。

深拷贝

JSON.parse(JSON.stringify(obj)) 原理:利用JSON.stringify 将 JavaScript 对象转化为JSON字符串,并将对象里面的内容转换成字符串,再使用JSON.parse来反序列化,将字符串生成一个新的 JavaScript对象.

简单粗暴,但也存在一些问题,如果拷贝对象中有 function,undefined,symbol 当使用过JSON.stringify()进行处理的时候,都会消失。

  • 无法拷贝不可枚举的属性;
  • 无法拷贝对象的原型链;
  • 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj)。
const originObj = {    name: 'test',
age: undefined,
func: function () {
console.log('Hello World');   }, 
key: Symbol('一个独一无二的key') } const cloneObj = JSON.parse(JSON.stringify(originObj)); console.log(cloneObj); // 只剩下 {name: "test"}

函数库lodash

该函数库也有提供_.cloneDeep用来做深拷贝,可以直接引入并使用:

粗略手写实现深拷贝

使用for in来遍历传入参数的属性值 如果值是基本类型就直接复制 如果是引用类型就进行递归调用该函数

function deepClone(source) {
   if (source instanceof Object === false) return source
   let target = Array.isArray(source) ? [] : {}
   for (let i in source) {
       if (source.hasOwnProperty(i)) {
           if (typeof source[i] === 'object') {
               target[i] = deepClone(source[i])
           }
           else {
               target[i] = source[i]
           }
       }
   }
   return target;
}

const obj = {
   info: { c: { d: 1 } },
   age: undefined,
   func: function () {
       console.log('hello world')
   },
   key: Symbol('key')
}

const resultA = deepClone(obj)
console.log(111111, obj);
console.log(222222, resultA);
let resultB = [1, [2, 3], [4, [5]]]
let resultC = deepClone(resultB)
resultB[1][1] = 7
console.log(333333, resultB);

console.log(444444, resultC);  

  • 存在环引用问题(存在循环引用,拷贝会直接爆栈)

总结

  1. 赋值运算符=实现的是浅拷贝(引用类型),拷贝的是对象的引用地址;
  2. JavaScript 中数组或者对象自带的拷贝方法都是首层浅拷贝;
  3. JSON.stringify实现的是深拷贝,但是对目标对象的部分拷贝结果存在问题; 官方loadsh库已经做得非常完美了,用起来不香?实际工作中建议直接使用;

day2

typeof null // "Object" -- Null 表示一个空对象引用。

NaN --表示不是数字,是JS的特殊值,出现原因是将字符串解析成数字时出现错误,虽然表示不是数字,但仍是数值类型(number) 需要注意的是NaN不等于任何值,包括它本身。 console.log(NaN===NaN); //结果为false

八股

说说cookie localstorage sessionstorage

Cookie(4KB)(保存在客户端)--由于HTTP是无状态协议,不能保存每一次请求的状态,所以给客户端增加Cookie来保存客户端状态。主要作用于用户识别和状态管理(网页里常见的记住密码)--传输管理Cookie时要确保 Cookie 的 安全性,不被窃取,Cookie 往往用来 保存用户的登录状态

HTML5 提供了两种在客户端存储数据的新方法 : localstorage -sessionStorage 挂载到window对象下。 localStorage--生命周期是永久性的,localStorage存储的数据,即使关闭浏览器也不会数据消失,除非主动删去,想设置失效时间,需自行封装

sessionStorage -- 生命周期实在浏览器相关,关闭浏览器或者页面,sessionStorage就会失效,页面刷新不会消除数据,只有在当前页面打开的链接才能访问 seesionStorage的数据,使用window.open打开页面和改变localtion.href方式都可以获 取到sessionStorage内部的数据;

image.png

说说同源策略,跨域的解决办法

跨域 --请求如果触发了同源策略就会出现跨域问题。 A网页设置的 Cookie,B网页不能打开 同源就是--协议 ,域名,端口相同。

--跨域是由于浏览器的同源策略所导致的,浏览器的同源策略是指只有“协议、域名、端口”相同我们才能进行通信,当有一个不满足的时候,我们就无法进行跨域。 目的:同源政策保证用户信息安全,防止网站恶意窃取数据 限制范围 如果非同源,有三种行为受限。

  • Cookie ,LocalStorage,IndexDB无法读取
  • Dom 无法获得
  • AJAX 请求不能发送

解决方法

  • JSONP、CORS、WEBPACK配置
  • JSONP,需要前后端配合--利用了 script 标签不受浏览器的同源策略影响的性质来做的,当然还需要前后端的配合
  1. 客户端事先准备一个接收数据的全局函数
  2. 客户端解析道外联的Script标签,发出请求
  3. 服务端收到请求,返回函数的调用
  4. 客户端收到数据,执行回调。
  • CORS--实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
const express = require('express')
const cors = require('cors')
const server = express()
server.use(cors())
server.get('/list', (req, res) => {
    res.send({
        code: 1, msg: '请求成功'
    })
})
server.post('/info', (req, res) => { res.send({ code: 1, msg: '请求成功' }) })
server.listen('8081', () => console.log('接口服务器开启成功'))

判断变量类型的方法有哪些

ECMAScript 标准定义了 8 种数据类型:

Boolean Null undefined Number String Biglnt Symbol 除了Object 以外的类型(基本类型)都是不可以变的(值本身无法被改变)例如: js字符串不可变,js对字符串操作返回一个新的字符串,原始字符串没有改变,称这些类型值为原始值

BigInt 可以安全存储和操作大整数,常常通过末尾附加n或调用构造函数来构建
const a = BigInt('43243242424242424242342432')
// 43243242424242424242342432n

const b = 43243242424242424242342432n
Symbol

Es6新增的一种基本数据类型,可以通过内置函数Symbol()创建,这个函数会生成一个匿名,全局唯一的值。 Symbol函数栈不能用new 命令,是原始数据类型,不是对象。Symbol最大的用处就是:避免对象的键被覆盖。

typeof

对于基本数据类型来说,除了null返回的是object,其他都可返回正确的类型

  • null被认为是一个空对象,因此返回了object

  • null转为二进制则表示全为0,如果前三个均为0,js就会把它当作是对象,这是js早期遗留下来的bug

  • 适用于判断(除null)基础类型,

  • 判断引用类型,除了function 全返回object类型

instanceof

判断 变量的原型链上是否有构造函数的prototype属性 (两个对象是否属于原型链的关系) 不一定能获取对象的具体类型

[] instanceof Array 为true。--[].proto 的原型是指向 Array.prototype,说明两个对象属于同一条原型链,返回true person(函数) instanceof Object 也为true 参数使用了null。也就是说将null设置成了新创建对象的原型,自然就不会有原型链上的属性

constructor

每一个实例对象都可通过constructor 来访问它的构造函数 undefined和null是无效的对象,因此是没有constructor属性的,这两个值不能用这种方法判断.

'5'.__proto__.constructor === String // true [5].__proto__.constructor === Array // true

toString

Object.prototype.toString方法 返回对象的类型字符串,因此可以用来判断一个值的类型。实例对象有可能会自定义toString方法使用时加上call。所有数据类型都可以使用此方法检测,非常准确。

console.log(Object.prototype.toString.call('5'))
console.log(Object.prototype.toString.call(5))
console.log(Object.prototype.toString.call(555555555n))
console.log(Object.prototype.toString.call(new Function()))
console.log(Object.prototype.toString.call(undefined))
console.log(Object.prototype.toString.call(null))

总结:typeof 适合基本数据类型和function类型,无法判断null 和object

instanceof 适合自定义对象,也可以检测原生对象,注意Object.create(null) 对象的问题

constructor基本可以判断所有类型,除了null 和 undefined ,但是 constructor 容易被修改

tostring能判断所有类型,可将其封装为全能的DataType()判断所有数据类型