【阶段面试】个人思考及指导性建议

193 阅读11分钟

A公司(C轮 100-499 一面)

vue keep-alive内置组件

watch和computed区别及应用场景

隐藏DOM的几种方式 position:stick

原型怎么理解

JS基本数据类型,typeof可以有哪几种结果

null和undefined区别

app唤起链接怎么做

混合包的一些问题

B公司(已上市 10000人以上 一面)

笔试:

浏览器跨域

for...in和for...of区别

ie盒子和标准盒子模型及差别

一行代码实现数组去重

arr = Array.from(new Set(arr))
arr = [...new Set(arr)]
str = Array.from(new Set(str)).join('')//字符串去重
str = [...new Set(str)].join('')

冒泡排序

一面:

vue-router

导航守卫

route与$router

vuex使用

组件通信

  1. 父子组件
  2. 兄弟组件
  3. 跨级组件

深拷贝,浅拷贝

参考

  1. 浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

    //数组使用concat或slice或[...arr],Array.from等
    a1 = [1, 2];
    a2 = a1.concat();//ES5
    a2 = [...a1];//ES6
    a3 = [[1,2], 2];
    a4 = [...a1];
    a4[0][0] = 2;
    console.log(a3)
    //对象使用Object.assign(target,...source)或{...obj}
    
  2. 深拷贝

    //方法1:转成对象JSON或者数组JSON,函数,undefined,symbol经过JSON.strify会丢失
    //方法2:使用第三方库,jquery的$.extend和lodash的_.cloneDeep
    obj = {
     o:{
        a:1,    b:2,   },
     a:1,
    }
    obj2 = JSON.parse(JSON.stringify(obj));
    obj2.o.a=2;
    console.log(obj2,obj)
    

Set和Map使用

  1. Set

ES6新数据结构。构造函数参数可接受数组或具有iterable接口的其他数据结构用来初始化。通过Array.from()Set结构转化为Array结构,这样数组的一些方法Set结构就可以间接使用。

set = new Set([1, 2, 3, 4, 4]);
typeof set //'object'
set instanceof Set//true
set//{1, 2, 3, 4}

操作方法:add,delete,has,clear方法。

遍历方法:keys,values,entries,foreach方法,Set数据结构支持for...of直接遍历(默认遍历器生成函数就是values),size属性。

使用Set实现交/并/差集。

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
  1. Map

ES6新数据结构,相对于传统对象的键名只能是字符串,Map的键名可以是各种类型(包括对象的,是一种更完善的hash结构实现。作为构造函数,Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。这就是说,SetMap都可以用来生成新的 Map。只有对同一个对象的引用,Map 结构才将其视为同一个键。常用方法和属性同Set,此外,还有set,get方法。Map 的遍历顺序就是插入顺序。Map的默认遍历器实现是使用entries方法。

m = new Map()
typeof m;//'object'
m instanceof Map;//true
m instanceof Object;//true

图片懒加载

微服务

总结:部门业务主要是PC端,考察多涉及

Vue全家桶,ES6

,其他主要浏览器兼容JS执行原理基础CSS。可以看出来小姐姐考察点很全面,部门代码质量相对比较高喜欢

C公司(C轮 500-999 二面)

一面:

BFC盒子

参考

可以通过创建BFC盒子,解决margin折叠问题。

常见的创建条件为:

  • overflow:hidden;
  • float:left/right;
  • position:absolute;
  • display:inline-block;
  • display:flex;

手写实现bind函数

call,bind,apply都是JS内置的API,可以改变this指向。

三者区别:

//都只有一个参数时,该参数即为指定的this,类似于执行了this = obj;
obj = {
    a:1,
};
function f(){
    this.a = 2;
    console.log(obj.a)
};
f.call(obj);//直接执行
f.apply(obj);//直接执行
newF = f.bind(obj);//返回一个新函数
newF();

//多参数使用,第一个参数均为指定this,其余参数为函数参数,call和bind支持多参数,apply第二个参数为数组
let obj1 = {
    num: 1
};
let obj2 = {
    num: 2
}

function fn (x,y) {
    console.log(x+y+this.num);
}
fn.call(obj1,1,2); // 4
fn.apply(obj1,[1,2]); // 4

//bind支持函数柯里化:我们在调用_fn之前传入了一部分参数,调用时参入了剩余参数
//方式1
fn.bind(obj1,1,2)();
//方式2
fn.bind(obj1,1)(2);
//方式3
let _fn = fn.bind(obj1,1);_fn(2); // 4

PS:函数柯里化,一个函数传入部分参数,返回一个新函数处理剩余参数。

function f(x){
    return function(y){
        console.log(x+y);
    }
}
f(1)(2);//方式1
_f = f(1);//方式2
_f(2);
  • bind

    特点: 1.改变this指向 2.返回一个新函数 3.支持函数柯里化 let obj1 = { num: 1 }; function fn (x,y) { console.log(x,y,x+y+this.num); } Function.prototype.myBind = function(context,...args){ let fn = this;//构造函数原型上的函数this指向实例,即调用myBind的fn args = args?args:[]; return function newFn(...newArgs){ if(this instanceof newFn){ return new fn(...args,...newArgs);//args为闭包 } return fn.apply(context,[...args,...newArgs]) } } fn.myBind(obj1,1,2)(); // fn.myBind(obj1,1)(2); // _fn = fn.myBind(obj1,1,2); // test = new _fn();

  • call

    特点: let obj1 = { num: 1 }; function fn (x,y) { console.log(x,y,x+y+this.num); } let obj1 = { num: 1 }; function fn (x,y) { console.log(x,y,x+y+this.num); } Function.prototype.myApply = function(context,...args){//!!!!区别在这里呀,也可以使用arguments类数组 context = context || window; args = args?args:[]; const key = Symbol(); context[key] = this;//调用实例 let result = contextkey;//通过对象调用方式改变this指向 delete context[key] return result; } fn.myApply(obj1,[1,2]);

  • apply

    特点:第二个参数为数组 let obj1 = { num: 1 }; function fn (x,y) { console.log(x,y,x+y+this.num); } let obj1 = { num: 1 }; function fn (x,y) { console.log(x,y,x+y+this.num); } Function.prototype.myApply = function(context,args){ context = context || window; args = args?args:[]; const key = Symbol(); context[key] = this;//调用实例 let result = contextkey;//通过对象调用方式改变this指向 delete context[key] return result; } fn.myApply(obj1,[1,2]);

手写防抖、节流

针对持续触发的监听事件,如resize,scroll,touchmove,mousemove等做函数防抖(操作结束后一定时间执行一次)和节流(操作进行过程中固定时间执行多次)。无法控制DOM事件的触发次数,但可以控制DOM监听函数的执行频率。

  • 防抖

    function debounce(fn,wait){ console.log(1) let timeout;//形成了闭包 return function(){ let content = this;//this指向与debounce保持一致 存疑... let args = arguments;//args[0]为event,仍然可以获取event对象 if(timeout) clearTimeout(timeout); timeout = setTimeout(()=>{ fn.apply(content,args); },wait) } }; function handle(){ console.log(2) } //window.onscroll = debounce;只是挂载debounce函数并没有执行 window.onscroll = debounce(handle,1000);//考虑到需要传递行参挂载其他函数,使用return新的function地形式;则执行了debounce函数并挂载了匿名function

  • 节流

    //使用时间戳 function throttle(fn,wait){ console.log(1) let previous = 0; return function(){ let content = this;//this指向与debounce保持一致 存疑... let args = arguments;//args[0]为event,仍然可以获取event对象 let now = new Date(); if(now - previous>wait){ fn.apply(content,args) previous = now; } } }; function handle(){ console.log(2) } window.onscroll = throttle(handle,1000);

    //使用定时器 function throttle(fn,wait){ console.log(1) let timeout; return function(){ let content = this;//this指向与debounce保持一致 存疑... let args = arguments;//args[0]为event,仍然可以获取event对象 // if(timeout){ clearTimeout(timeout)} if(!timeout){ timeout = setTimeout(()=>{ timeout = null; fn.apply(content,args) },wait);
    } } }; function handle(){ console.log(2) } window.onscroll = throttle(handle,1000);

this

原型

深浅拷贝

vue源码

双向绑定原理,监听订阅是怎么实现的,具体如何监听的

设计模式使用

闭包使用

box-sizing

width的宽度即为设置的两种值,content-boxborder-box

日常学习方式

完成最好的一次项目经历

职业规划

总结:主要围绕日常开发比较常用的轮子考察,JS基础CSS基础框架较少,以及个人特质的考察。

D公司 (未融资 一面)

笔试:

类型转换

目标类型只有boolean,string,number三种。

px,em,rem区别及如何适配

参考

参考

如何减少页面加载时间

使用this的三个场景

cookies,sessionStorage,localStorage的区别

  • cookies
  1. 大小4KB,比较小
  • sessionStorage
  • 随请求发送
  • 可设置过期时间等
  1. 大小5MB,较大
  2. 关闭页面或者浏览器
  • localStorage
  1. 大小5MB,较大
  2. 手动clear()

判断字符串中出现次数最多的字符及对应次数

ajax的优缺点及原理

总结:CSS基础JS基础AJAX等。

E公司(已上市 1000-9999 二面)

一面:

px,em,rem优缺点

个别机型表现存在差异。

DOM原生事件绑定的几种方法

参考

  • DOM0级(on+type)

只可注册绑定一个,后者会覆盖前者;只支持冒泡阶段。参考

  • DOM2级(addEventListener)

同个DOM可注册多个监听器;第三个options选项可灵活控制各场景,对于touchmove,touchstart手动设置选项passivetrue可组织默认连带的滚动行为,使页面触摸更流畅;最好手动原样去除DOM事件removeEventListener。参考。IE9以下的IE浏览器不支持 addEventListener()和removeEventListener(),使用 attachEvent()与detachEvent() 代替,因为IE9以下是不支持事件捕获的,所以也没有第三个参数,第一个事件名称前要加on。

  • html(on事件)

阻止默认事件兼容

function preventDef(e){
	var g = e || window.event;
	if(g.preventDefault){
		g.preventDefault();
	}else if(g.returnValue){
		g.returnValue = false;
	}
	return false;
}

事件流和事件委托/代理

捕获阶段,目标阶段(目标元素身上不区分冒泡捕获,按绑定的前后顺序来执行。),冒泡阶段。

利用事件冒泡的机制,事件最终都会冒泡到祖先元素,只需要给祖先绑定事件就可以监听所有子元素的事件,而不用给每个DOM都绑定事件。同时可以在listener中进行区别处理。

* 优点: * 可以大量节省内存占用,减少事件注册。 * 可以实现当新增子对象时,无需再对其进行事件绑定,对于动态内容部分尤为合适* 缺点: * 事件代理的常用应用应该仅限于上述需求,如果把所有事件都用事件代理,可能会出现事件误判。即本不该被触发的事件被绑定上了事件。

event.currentTarget,event.target

参考

`event.target`指向引起触发事件的元素,而`event.currentTarget`则是事件绑定的元素,只有被点击的那个目标元素的`event.target`才会等于`event.currentTarget`。**也就是说,`event.currentTarget`始终是监听事件者,而`event.target`是事件的真正发出者**。

原型

new的原理(this关系挂载,原型关系挂载)

function Animal(type) {
    this.type = type;
}
Animal.prototype.say = function() {
    console.log('say')
}

function mockNew() {
    let Constructor = [].shift.call(arguments); // 取出构造函数

    let obj = {}   // new 执行会创建一个新对象

    obj.__proto__ = Constructor.prototype //原型关系挂载

    Constructor.apply(obj, arguments)//this关系挂载
    return obj
}
let animal = mockNew(Animal, 'dog')

console.log(animal.type) // dog
animal.say() // say作者:JOKER_链接:https://juejin.cn/post/6844903782803832845来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

原型链

for in,Objects.keys(),for of区别

  • for in
  1. 对象/数组本身及其原型链上的可枚举属性
  2. 对于自定义数组可枚举属性也可以遍历
  3. 返回的结果是string类型的对象key和数组索引
  4. 某些情况下可能按随机顺序遍历数组,不推荐使用此方法遍历数组
  • Objects.keys()
  1. 对象自身及自定义的可枚举属性,不会遍历原型链及Symbol属性
  2. 返回的结果是对象key组成的数组
  3. 对数组的遍历与for in一致
  4. 此外,Object.entries()返回的是[key,value]二级数组。
  • for of
  1. 支持对数组,类数组对象(NodeList),字符串,Set,Map的遍历
  2. 对数组的遍历不支持自定义属性(类对象属性)和原型属性
  3. 不支持遍历普通对象

参考

深浅拷贝

event loop

promise.finally

浏览器跨域

参考

同源策略:相同网络协议,域名,端口号;浏览器安全;

非同源存在的问题:

  1. 数据:无法获取非同源网页的 cookie、localstorage 和 indexedDB。
  2. DOM:无法访问非同源网页的 DOM (iframe)。
  3. 请求:无法向非同源地址发送 AJAX 请求 或 fetch 请求(可以发送,但浏览器拒绝接受响应)。

解决方式:

  1. 数据:
  • window.postMessage :一个 HTML5 的 api,允许两个窗口之间进行跨域发送消息。
  1. DOM:
  • document.domain:
  • window.name:
  1. 请求:
  • CORS:服务器端配置。相关响应头信息Access-Control-Allow-Origin
  • JSONP:前后端配合完成,使用简单且兼容性不错,但是只限于 get 请求。

原理就是利用 <script> 标签的 src 属性没有跨域的限制,通过指向一个需要访问的地址,由服务端返回一个预先定义好的 Javascript 函数的调用,并且将服务器数据以该函数参数的形式传递过来

//定义获取数据的回调方法
function getData(data) {
  console.log(data);
}

// 创建一个script标签,并且告诉后端回调函数名叫 getData
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.type = 'text/javasctipt';
script.src = 'demo.js?callback=getData';
body.appendChild(script);

//script 加载完毕之后从页面中删除,否则每次点击生成许多script标签
script.onload = function () {
  document.body.removeChild(script);
}

协商缓存与强制缓存

总结:考察JS原理熟练掌握及灵活应用浏览器相关

个人项目介绍

vue

angular

微信小程序

node

他说两年

  • 项目工程化,项目集成:框架集成,npm包发布,node_module包调试
  • 重数据量/访问量下的项目优化:懒加载,动态加载各种
  • TS,React:大厂必备,杭州互联网必备
  • 数据结构,树,排序等:大厂必备,需求必备!!!(想想之前的业务场景哪里用得到...)
  • 前后端并重:而不只是熟练使用各种全家桶(如果只是会使用各种全家桶就完了...听到这句话我当时就愣了,毕竟目前还不是说熟练使用全家桶),如何原生实现更重要。