面的不好,很多都知道自己看过,但临场手写就写不对了,手写真耗时间,写着写着,查查错,面试就快结束了,自己承认知识掌握不扎实,仍需努力夯实基础。再次梳理一遍,对自己还是有帮助的。已凉,不过也让我认识到了自己的不足。
自我介绍
怎么学习前端的
你用过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) 回帖