面的不好,很多都知道自己看过,但临场手写就写不对了,手写真耗时间,写着写着,查查错,面试就快结束了,自己承认知识掌握不扎实,仍需努力夯实基础。再次梳理一遍,对自己还是有帮助的。已凉,不过也让我认识到了自己的不足。
自我介绍
怎么学习前端的
你用过vue,知道vue的key是用来做什么的吧
key是为Vue中虚拟DOM标记的唯一id,通过这个key,我们的父节点可以更准确、更快速定位子节点.
讲一讲vue和react的区别
vue双向数据绑定,react单向,需要setState实现medel-view的转换
拓展:
-
数据绑定:Vue实现了双向的数据绑定,react数据流动是单向的
-
数据渲染:大规模的数据渲染,react更快
-
使用场景:React配合Redux架构适合大规模多人协作复杂项目,Vue适合小快的项目
-
开发风格:react推荐做法jsx + inline style把html和css都写在js了,vue是采用webpack +vue-loader单文件组件格式,html, js, css同一个文件
Vue怎么实现双向绑定的
Vue2.X通过 Object.defineProperty() 来劫持各个属性的setter,getter,新版本通过Proxy劫持
要想深入讲解知识点:
-
Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty() 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue追踪依赖,在属性被访问和修改时通知变化。
-
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
拓展:代码(没写,自己总结面经顺便复习)
// 数据 const data = { text: 'default' }; const input = document.getElementById('input'); const span = document.getElementById('span'); // 数据劫持 Object.defineProperty(data, 'text', { // 数据变化 --> 修改视图 set(newVal) { input.value = newVal; span.innerHTML = newVal; } }); // 数据变化 --> 修改视图 input.addEventLisener('keyup', function(e) { data.text = e.target.value; });
// 数据 const data = { text: 'default' }; const input = document.getElementById('input'); const span = document.getElementById('span'); // 数据劫持 const handler = { set(target, key, value) { target[key] = value; // 数据变化 --> 修改视图 input.value = newVal; span.innerHTML = newVal; return value; } }; const proxy = new Proxy(data, handler); // 视图更改 --> 数据变化 input.addEventLisener('keyup', function(e) { proxy.text = e.target.value; });
每个对象都有prototype(原型),当我们访问一个对象的属性时,在找不到的情况再会在其原型上查找是否有这个属性,再找不到会去自己 [__proto__] 关联的 prototype 对象上去找,顺着原型链知道undefined。语言组织太差,有种有货道不出的感觉。
Function.prototype.a = () => { console.log(1); } Object.prototype.b = () => { console.log(2); } function A() {} const obj = new A(); obj.a() // Error obj.b() // 2 A.a() // 1 A.b() // 2
解释了通过函数声明可以同时继承Function和Object原型上的方法,通过new继承只能继承Object的方法
面试官问第一个Error是什么Error,脑抽了说是引用Error(ReferenceError),实际上是TypeErro,打印的是这个结果
Uncaught TypeError: obj.a is not a function
说到继承,你写一下你知道的继承方式
function person() { this.kind="person"; } person.prototype.eat = function (food) { console.log(this.name+" is eating "+food); } function student() { }
student.prototype.__proto__ === person.prototype
student.prototype = new person();
缺点:原型是所有子类实例共享的,改变一个其他也会改变。
该打,我没写完全对
function student() { person.call(this); }
缺点:不能继承父类原型,函数在构造函数中,每个子类实例不能共享函数,浪费内存。
这个倒是完全写对了
function student() { person.call(this); } student.prototype = new Person();
缺点:person的构造函数会多执行了一次
哭了,又没有完全写对
student.prototype = Object.create(person.prototype);
又搞错了
student.prototype = Object.create(person());
Object.prototype.myCreate(proto) { function f() {}; f.prototype = proto; return new f(); }
口述的方法倒是对的
student.prototype = Object.create(person);
面试官小姐姐温柔地说,没关系,我可能太紧张了
function student() { person.call(this); } student.prototype = new person(); // 或者student.prototype = Object.create(person.prototype);
我的错误跟上面的一样,
这种继承方法父类原型和子类原型是同一个对象,无法区分子类真正是由谁构造。
function student() { person.call(this); } student.prototype = new person(); // 或者student.prototype = Object.create(person.prototype); student.prototype.constructor = student;
面试官问我知不知道防抖和节流
我回答知道,防抖在定时器定时期间再次触发,关掉旧的定时器,开始新的定时器。将多次执行变为最后一次执行,典型应用,提交按钮的点击事件。
节流在一段时间,只执行一次事件。等到这段之后再根据这段时间内由出发执行,将多次执行变成每隔一段时间执行,典型应用,拖拽事件。 我们用 leading 代表首次是否执行,trailing 代表结束后是否再执行一次
function debounce(func, wait = 1000) { let timer = null; return function(...args) { if (timer) { timer = null; } timer = setTimeout(func(...args), wait); } }
function debounce(func, wait = 1000) { let timer = null; return function(...args) { if (timer) { clearTimeout(timer); } timer = setTimeout(func(...args), wait); } }
function throttle(func, wait = 1000) { let last = 0; return function(...args) { let now = +new Date(); if ((now - last) > wait) { last = now; func(...args); } } }
const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("success"); console.log("timer1"); }, 1000); console.log("promise1里的内容"); } ); const promise2 = promise1.then(() => { throw new Error("error!!!"); }); console.log("promise1", promise1); console.log("promise2", promise2); setTimeout(() => { console.log("timer2"); console.log("promise1", promise1); console.log("promise2", promise2); }, 2000);
手写输出的顺序
promise1里的内容
promise1: Proimse{<pending>}
promise2: Proimse{<pending>}
timer1
Error: error!!!
timer2
promise1: Proimse{<fulfilled>:'success'}
promise2: Proimse{<rejected>:Error'error'}
写完之后讲了讲思路,讲了promise是微任务,setTimeout是宏任务,事件循环机制:执行一个宏任务,执行微任务列表,经过循环再执行宏任务,再执行微任务列表。并提出老版本node循环机制不同把当前时刻宏任务、微任务执行顺序有不同
JS有哪些宏任务、微任务
宏任务setTimeout、setInterval(漏了setImmediate)
补充优先级:主代码块 > setImmediate > MessageChannel > setTimeout / setInterval
微任务包括:优先级:process.nextTick > Promise > MutationObserver
微任务提到了async、await,await后面的看作是promise.then后面的
CSS
position有哪些:
只说出来absolute、relative、fixed,不常用的背过忘了,抄一遍菜鸟上的加强记忆
值 | 描述 |
---|---|
absolute | 生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。 |
fixed | 生成绝对定位的元素,相对于浏览器窗口进行定位。元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。 |
relative | 生成相对定位的元素,相对于其正常位置进行定位。因此,"left:20" 会向元素的 LEFT 位置添加 20 像素。 |
static | 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。 |
inherit | 规定应该从父元素继承 position 属性的值。 |
提了父相子绝,被问如果父元素没有设置position:relative,子节点设置position:absolute怎么办,我说会一个个找父节点直到找到设置position:relative的,错了,应该是找到position:static以外的第一个父元素。
反问
全部评论
(5) 回帖