面试手写JS - 掘金 (juejin.cn) 面试手写JS-two - 掘金 (juejin.cn)
创建对象以及原型链
- 面向对象:
- 封装
- 继承
- 多态
- 创建对象的几种方式
- 字面量
- 通过构造函数
- Object.create
大多数情况下,proto__可以理解为“构造器的原型”,即__proto===constructor.prototype,但是通过 Object.create()创建的对象有可能不是, Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
这么理解吧 prototype 是原型 是函数才有的属性。提供的是属性和方法。而__proto__ 则完善的是JS的原型链顺序查找的机制。 而prototype完成的是继承机制
-
原型链 原型链的基本原理:任何一个实例,通过原型链,找到它上面的原型,该原型对象中的方法和属性,可以被所有的原型实例共享。
-
instanceof的原理
代码实现见文章开头的文章或github代码库
- instanceof的作用:用于判断实例属于哪个构造函数。
- instanceof的原理:判断实例对象的__proto__属性,和构造函数的prototype属性,是否为同一个引用(是否指向同一个地址)。 link
Object是原型链的顶端。 prototype 为Null
误区: instanceof 无法说明谁是由谁创建的实例 问题:问题:已知A继承了B,B继承了C。怎么判断 a 是由A直接生成的实例,还是B直接生成的实例呢?还是C直接生成的实例呢?
- foo.proto.constructor === M的结果为true,但是 foo.proto.constructor === Object的结果为false。
- 所以,用 consturctor判断就比用 instanceof判断,更为严谨。
面向对象
类的定义,实例化
- 用构造函数模拟类
function Animal1() {
this.name = 'smyhvae'; //通过this,表明这是一个构造函数
}
- 用class声明 (ES6)
class Animal2 {
constructor() { //可以在构造函数里写属性
this.name = name;
}
}
继承的方式
见文章顶部链接文章
DOM事件总结
- DOM事件的级别
- DOM事件模型,DOM 事件流
- Event对象的常见应用
- DOM事件的级别
DOM事件级别
DOM级别一共可以分为四个级别:DOM0级、DOM1级、DOM2级和DOM3级。而DOM事件分为3个级别:DOM 0级事件处理,DOM 2级事件处理和DOM 3级事件处理。由于DOM 1级中没有事件的相关内容,所以没有DOM 1级事件。
- DOM 0级事件
el.=function(){}
DOM0事件绑定,给元素的事件行为绑定方法,这些方法都是在当前元素事件行为的冒泡阶段(或者目标阶段)执行的。
- DOM 2级事件
el.addEventListener(event-name, callback, useCapture)
useCapture: 默认是false,代表事件句柄在冒泡阶段执行
- DOM 3级事件
在DOM 2级事件的基础上添加了更多的事件类型。
DOM事件模型
-
捕获阶段:事件从window对象自上而下向目标节点传播的阶段;
-
目标阶段:真正的目标节点正在处理事件的阶段;
-
冒泡阶段:事件从目标节点自下而上向window对象传播的阶段。
理解此的用处: 可以进行事件委托以及事件代理
DOM事件捕获
捕获阶段,事件依次传递的顺序是:window --> document --> html--> body --> 父元素、子元素、目标元素。
Event 对象
感觉这一部分的内容跟 好像都跟事件绑定有关系。找不到实际用处
Event对象的常见 api 方法
用户做的是什么操作(比如,是敲键盘了,还是点击鼠标了),这些事件基本都是通过Event对象拿到的。这些都比较简单,我们就不讲了。我们来看看下面这几个方法:
event.preventDefault();
阻止默认事件 阻止冒泡 设置事件优先级
event.currentTarget //当前所绑定的事件对象。在事件委托中,指的是【父元素】。
event.target //当前被点击的元素。在事件委托中,指的是【子元素】。
感觉就是很多API感觉目前用还不到
自定义事件
var myEvent = new Event('clickTest');
element.addEventListener('clickTest', function () {
console.log('smyhvae');
});
setTimeout(function () {
element.dispatchEvent(myEvent); //注意,参数是写事件对象 myEvent,不是写 事件名 clickTest
}, 1000);
从 Event Loop 看 JS 的运行机制
- 总的来说有以下重点
- JS引擎线程只执行执行栈中的事件(执行栈中包含同步事件和异步事件)
- 异步事件的回调函数会被放入执行栈中
- 执行栈中的代码执行完毕,就会读取事件队列中的事件
- 事件队列中的回调事件,是由各自线程插入到事件队列中的
- 如此循环
什么是宏任务
- 我们将执行栈中所要执行的代码当做是一个宏任务
- 每一个宏任务会从头到尾执行完毕,不会执行其他。
// 宏任务-->渲染-->宏任务-->渲染-->渲染..
主代码块,setTimeout,setInterval等,都属于宏任务
第一个例子:
我们可以将这段代码放到浏览器的控制台执行以下,看一下效果:
我们会看到的结果是,页面背景会在瞬间变成白色,以上代码属于同一次 宏任务,所以全部执行完才触发 页面渲染,渲染时 GUI线程会将所有UI改动优化合并,所以视觉效果上,只会看到页面变成灰色
第二个例子:
我会看到,页面先显示成蓝色背景,然后瞬间变成了黑色背景,这是因为以上代码属于两次 宏任务,第一次 宏任务执行的代码是将背景变成蓝色,然后触发渲染,将页面变成蓝色,再触发第二次宏任务将背景变成黑色
什么是微任务
- 我们已经知道 宏任务结束后,会执行渲染,然后执行下一个 宏任务,
- 而微任务可以理解成在当前 宏任务执行后立即执行的任务。
- 也就是说,当 宏任务执行完,会在渲染前,将执行期间所产生的所有 微任务都执行完。
第一个例子
控制台输出 1 3 2 , 是因为 promise 对象的 then 方法的回调函数是异步执行,所以 2 最后输出 页面的背景色直接变成黑色,没有经过蓝色的阶段,是因为,我们在宏任务中将背景设置为蓝色,但在进行渲染前执行了微任务,在微任务中将背景变成了黑色,然后才执行的渲染
第二个例子
上面代码共包含两个 setTimeout ,也就是说除主代码块外,共有两个 宏任务, 其中第一个 宏任务执行中,输出 1 ,并且创建了 微任务队列,所以在下一个 宏任务队列执行前,先执行 微任务,在 微任务执行中,输出 3 ,微任务执行后,执行下一次 宏任务,执行中输出 2
总结
- 执行一个宏任务(若队列中没有则从事件队列中获取)
- 执行过程中出现微任务,则将它添加到微任务队列
- 宏任务执行完毕之后,立即执行当前微任务队列中所有的微任务
- 执行完毕之后,开始检查渲染。而后GUI线程接管渲染
- 渲染完毕后, JS线程继续接管,开始下一个 宏任务(从事件队列中获取)
CSS 盒模型
- 基本概念:content、padding、margin
- 标准盒模型、IE盒模型的区别。不要漏说了IE盒模型,通过这个问题,可以筛选一部分人
- CSS如何设置这两种模型(即:如何设置某个盒子为其中一个模型)?如果回答了上面的第二条,还会继续追问这一条。
- JS如何设置、获取盒模型对应的宽和高?这一步,已经有很多人答不上来了。
- 实例题:根据盒模型解释边距重叠。
- BFC(边距重叠解决方案)或IFC。
标准盒模型和IE盒子模型 (也叫做怪异盒模型)
这个大家都知道,就不多讲了
-
在标准盒子模型中,width 和 height 指的是内容区域的宽度和高度。增加内边距、边框和外边距不会影响内容区域的尺寸,但是会增加元素框的总尺寸。
-
IE盒子模型中,width 和 height 指的是内容区域+border+padding的宽度和高度。
CSS如何设置
/* 设置当前盒子为 标准盒模型(默认) */
box-sizing: content-box;
/* 设置当前盒子为 IE盒模型 */
box-sizing: border-box;
JS如何设置、获取盒模型对应的宽和高
window.getComputedStyle(element).width/height;
margin塌陷/margin重叠
标准文档流中,竖直方向的margin不叠加,只取较大的值作为margin(水平方向的margin是可以叠加的,即水平方向没有塌陷现象)。
PS:如果不在标准流,比如盒子都浮动了,那么两个盒子之间是没有margin重叠的现象的。
margin这个属性,本质上描述的是兄弟和兄弟之间的距离; 最好不要用这个marign表达父子之间的距离。
所以,如果要表达父子之间的距离,我们一定要善于使用父亲的padding,而不是儿子的`margin。
BFC 的原理/BFC的布局规则
BFC(Block Formatting Context):块级格式化上下文。你可以把它理解成一个独立的区域。
BFC的渲染规则:
- BFC 内部的子元素,在垂直方向,边距会发生重叠。
- BFC在页面中是独立的容器,外面的元素不会影响里面的元素,反之亦然。
- BFC区域不与旁边的float box区域重叠。(可以用来清除浮动带来的影响)。
- 计算BFC的高度时,浮动的子元素也参与计算。(稍后看举例3)
如何生成BFC
- 方法1:overflow: 不为visible,可以让属性是 hidden、auto。【最常用】
- 方法2:浮动中:float的属性值不为none。意思是,只要设置了浮动,当前元素就创建了BFC。
- 方法3:定位中:只要posiiton的值不是 static或者是relative即可,可以是absolute或fixed,也就生成了一个BFC。
- 方法4:display为inline-block, table-cell, table-caption, flex, inline-flex
BFC的应用
举例1:解决 margin 重叠 比如:针对下面这样的一个结构
<div class="father">
<p class="son">
</p>
</div>
上面的div结构中,如果父元素和子元素发生margin重叠,我们可以给子元素创建一个 BFC,就解决了:
<div class="father">
<p class="son" style="overflow: hidden">
</p>
</div>
第二条:BFC区域是一个独立的区域,不会影响外面的元素。
举例二: BFC区域不与float区域重叠
代码见github仓库
举例三: 清除浮动
有高度的盒子,才能关住浮动
如果想要清除浮动带来的影响,方法一是给父亲设置高度,然后采用隔墙法。方法二是 BFC:给父亲增加 overflow=hidden属性即可, 增加之后,效果如下: