前言
小滴课堂,旨在让编程不在难学,让技术与生活更加有趣。 随着互联网+的时代,在线教育技术越来越便捷,小滴课堂依托在线教育时间以及空间上的便利,为广大IT从业者提供了更为方便、快捷的学习交流途径、提供大量高质量的IT在线课程。更多教程请访问xdclass.net(添加VX:xdclass99)
一、说说你对JS中的原型链的理解
回答
(一)思路
- 原型( prototype )
(1)是function对象的⼀个属性,它定义了构造函数制造出的对象的公共祖先,通过该构造函数产⽣的对象,可以继承该原型的属性和⽅法,原型也是对象
function Person() {}
Person.prototype.name = '⼤钊';
Person.prototype.a = function () {
console.log(11);
};
var person1 = new Person();
console.log(person1.name);
person1.a();
(2)作用
- 构造函数实例化出来的对象可以使⽤公共的属性或者⽅法
-
函数对象才有 prototype 属性
-
原型链
(1)js里万物皆对象,所以⼀直访问 proto 属性就会产⽣⼀条链条
(2)链条的尽头是null (Object.prototype. proto )
(3)当js引擎查找对象的属性时,会先判断对象本身是否存在该属性
(4)不存在的属性就会沿着原型链往上找
function Car() {}
Car.prototype.name = '⼤钊';
var car = new Car();
- 总结
(1)原型主要是解决继承问题
(2)每个对象拥有⼀个原型对象,通过 proto 指针指向其原型对象,并从中继承⽅法和属性
(3)同时原型对象也可能拥有原型,这样⼀层⼀层,最终指向 null(Object.proptotype.proto指向的是null)
(4)上述的关系被称为原型链,通过原型链⼀个对象可以拥有定义在其他对象中的属性和⽅法
二、说说你对重绘和重排的理解和如何避免?
回答
(一)思路
- 重绘
- 当元素的外观、背景、颜⾊等改变,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观叫做重绘
- 重排
- 当渲染树⼀部分或者全部因为⼤⼩或者边距⽽改变,需要渲染树重新计算的过程叫做重排
- 重绘不⼀定需要重排,重排必然导致重绘
- 避免
- 在元素的显示隐藏上尽量用 opacity 替代 visibility(重绘)
- 元素定位时使用 transform 代替top、left(重排)
- 尽量不使用 table 布局,因为⼀个⼩的改动会造成整个 table 重新布局(重排)
- 减少直接操作DOM元素(重排)
- 为元素添加类,样式都在类中改变(重绘)
三、说下vue的响应式原理是如何实现的?
回答
(一)思路
- 原理( Object.defineProperty() )
- 通过⼀个对象代理另⼀个对象属性的读写
Object.defineProperty('代理对象', '代理属性', {
get() { // getter 当读取’⽬标对象‘的’代理属性‘时,get函数/getter
就会被调⽤,且返回代理属性的值
return xxx;
},
set(value) { // setter 当修改’⽬标对象‘的’代理属性‘时,set函 数/setter就会被调⽤,且收到修改的值
xxx;
},
});
let obj1 = {
a: 111,
};
let obj2 = {};
Object.defineProperty(obj2, 'a', {
get() {
console.log('obj2被读取了');
return obj1.a;
},
set(value) {
console.log('obj2被修改了');
obj1.a = value;
},
});
- Vue中应⽤的数据代理
(1)通过vm对象属性代理 _data 中属性的读写
(2)能更加⽅便的读写vue中data的数据
(3)通过 Object.defineProperty() 把 data 中的属性添加到vm对象上,每个属性都有setter/getter
- 连环问
- 说下vue3的响应式原理是如何实现的,为什么?
(1)Object.defineProperty
- 无法监听新增属性和删除属性,使⽤this.$set
- 深层对象的劫持需要递归
- 劫持数组时需要重写数组原⽣操作⽅法
- 只是对对象的属性进行劫持
(2)Proxy
-
概述
- 正如Proxy的英译"代理"所示,Proxy是ES6为了操作对象引入的API。它不直接作⽤在对象上,而是作为⼀种媒介,如果需要操作对象的话,需要经过这个媒介的同意。
-
使用方式
let p = new Proxy(target, handler)
//target: 目标对象
//handler: 对对象进行拦截操作的函数,如set、get
- 使用场景
const house = {
name: '张三',
price: '1000',
phone: '18823139921',
id: '111',
state: '**',
};
const houseProxy = new Proxy(house, {
// 读取代理
get: function (target, key) {
switch (key) {
case 'phone':
return '抱歉,不能告知'
default:
return Reflect.get(target,key);
}
},
// 设置代理
set: function (target, key, value) {
if (key === 'id') {
return Reflect.get(target,key);
} else if (key === 'state') {
return Reflect.set(target,key,value);
}
},
});
console.log(houseProxy.price);
console.log(houseProxy.phone);
houseProxy.id = '222';
houseProxy.state = '****';
console.log(houseProxy.id);
console.log(houseProxy.state);
- Reflect(映射)
- 规范化、语义化
- ES6规范中为了操作对象更加趋向于编程式(函数式)
- 减少异常的抛出,因为对象的属性有可能是个getter或者setter
- Reflect对象的方法与Proxy对象的⽅法⼀⼀对应
四、谈谈对Promise的理解
回答
(一)前置知识
- 同步
- 同步的思想是:所有的操作都做完,才返回给用户。这样⽤户在线等待的时间太长,给用户⼀种卡死了的感觉,这种情况下,用户不能关闭界面,如果关闭了,程序就中断了。
- 异步
-
将⽤户请求放⼊消息队列,并反馈给⽤户,程序已经启动,你可以关闭浏览器了。这就是异步。但是⽤户没有卡死的感觉,会告诉你,你的请求程序已经响应了。你可以关闭界⾯了。
-
通俗的解释就是打电话和发短信的区别。
-
打电话时,对方挂了电话,通信也就中断了,而发短信时,无需考虑对⽅在不在线,都能进行通信。
(二)概述
- Promise是⼀种⽤于解决异步问题的思路、⽅案或者对象⽅式。
(三)原理
-
在Promise的内部,有⼀个状态管理器的存在,有三种状态:pending、fulfifilled、rejected。
- Promise 对象初始化状态为 pending。
- 当调用resolve(成功),会由pending => fulfifilled。
- 当调用reject(失败),会由pending => rejected.
-
需要记住的是注意promsie状态 只能由 pending => fulfifilled/rejected, ⼀旦修改就不能再变(记住,⼀定要记住,下⾯会考到)。
-
当状态为fulfifilled(rejected反之)时,then的成功回调函数会被调⽤,进⽽进⾏操作。Promise.then⽅法每次调⽤,都返回⼀个新的Promise对象 所以可以链式写法(无论resolve还是reject都是这样)。
(四)总结
-
⾸先,Promise是⼀个对象,如同其字面意思⼀样,代表了未来某时间才会知道结果的时间,不受外界因素的印象。Promise⼀旦触发,其状态只能变为fulfifilled或者rejected,并且已经改变不可逆转。Promise的构造函数接受⼀个函数作为参数,该参数函数的两个参数分别为resolve和reject,其作⽤分别是将Promise的状态由pending转化为fulfifilled或者rejected,并且将成功或者失败的返回值传递出去。then有两个函数作为Promise状态改变时的回调函数,当Promise状态改变时接受传递来的参数并调用相应的函数。then中的回调的过程为异步操作。catch⽅法是对.then(null,rejectFn)的封装(语法糖),用于指定发生错误时的回掉函数。⼀般来说,建议不要再then中定义rejected状态的回调函数,应该使用catch⽅法代替。all和race都是竞速函数,all结束的时间取决于最慢的那个,其作为参数的Promise函数⼀旦有⼀个状态为rejected,则总的Promise的状态就为rejected;而race结束的时间取决于最快的那个,⼀旦最快的那个Promise状态发⽣改变,那个其总的Promise的状态就变成相应的状态,其余的参数Promise还是会继续进行的。
-
当然在es7时代,也出现了await/async的异步方案,这会是我们以后谈论的。
五、谈谈你对css盒子模型的理解
回答
- css盒子模型分为标准W3C盒子模型和IE盒子模型。
- css盒子模型组成由外边距(margin)、边框(border)、内边距(padding)和内容(content)。
(一)标准W3C盒⼦模型:
- 如上图,在W3C盒⼦模型中:
- css设置的宽(width)=内容(content)的宽
- css设置的高(height)= 内容(content)的高
- 也就是我们正常给标签设置的宽高,这个时候是标准的盒子模型。举个例⼦:
<div style="width:100px;height:100px;padding:10px;border:1px solid
#000;margin:10px;">helloworld</div>
- 此时div的实际大小应该是这样:
盒子总宽度/高度=width/height+padding+border+margin。
- 从上图可以看到蓝色部分(content内容)的宽⾼就是我们设置的css宽高。
(二)IE盒子模型:
- 如上图,在IE模型中:
- css设置的宽(width)=内容(content)的宽 + 左右padding + 左右border
- css设置的高(height)=内容(content)的⾼高+ 上下padding + 上下border
- 在这里content的宽是<css设置的宽度的,在同样的例子下:
//这里需要另外加上box-sizing:border-box将标准盒子模型转换为IE盒子模型
<div style="width:100px;height:100px;padding:10px;border:1px solid
#000;margin:10px;box-sizing:border-box">helloworld</div>
- 此时div的实际大小应该是:
盒子总宽度/高度=width/height + margin = 内容区宽度/高度 + padding + border + margin
- 如上图,这个时候可以看到content的内容宽高变成了78✖78,⽽不是我们设置的width和height,content⼤⼩可通过上面计算公式那样计算出来。
(三)总结:
- 讲到这里⼤家应该对两个盒⼦模型的区别有了⼀定了解,具体使用情况可以根据⾃⼰需要进⾏调整,⽐如想在div宽高不变的情况下调整padding,这样可以避免影响到布局。
- 最后,我们如何转换标准盒⼦和IE盒⼦,这⾥可以运用box-sizing属性来定义,如下图: