总纲:前端面试知识点大全
目录
10.ES6 Class类的prototype属性和__proto__属性(继承链)
1.图片懒加载
可以参考个人博客:前端学习系列——(十二)JS获取元素大小及懒加载应用实现
1.1 什么是懒加载
所谓图片懒加载就是到可视区域再加载或者叫做延迟加载。
第一种:纯粹的延迟加载,使用setTimeout加载。若用户在加载前就离开了页面,那么就不会加载,体验很差
第二种:条件加载,符合某些条件或触发了某些事件才开始异步下载
第三种:就是第二种的具体版,且是目前主流方法,就是可视区加载。即仅加载用户可以看到的区域,这个主要由监控滚动条来实现的,一般会在距离用户看到某图片前一定距离便开始加载,这样能保证用户拉下时,正好看到图片。
目前有两款懒加载插件,一个是基于jQuery的lazyload插件,一个是echo.js插件。echo很小100多行代码。核心也就几十行。
1.2 手动实现懒加载
思路:将页面里所有img属性src属性用data-xx代替,当页面滚动直至此图片出现在可视区域时,用js取到该图片的data-xx的值赋给src。关键:img.offsetTop<clientHeight+scrollTop
如何判断img元素进入可视区域了,这里提供两种方法:
方法一:
①通过document.documentElement.clientHeight获取屏幕可视窗口高度
②通过element.offsetTop获取元素相对于文档顶部的距离
③通过document.documentElement.scrollTop获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
然后判断③-②<①是否成立,如果成立,元素就在可视区域内。
方法二(推荐):
(function () {
var imgList = [], // 页面所有img元素集合
delay, // setTimeout 对象
offset, //偏移量,用于指定图片距离可视区域多少距离,进行加载
time, // 延迟载入时间
_selector; // 选择器 默认为 .m-lazyload
function _isShow(el) {
var coords = el.getBoundingClientRect();
return ( (coords.top >= 0 && coords.left >= 0 && coords.top) <= (window.innerHeight || document.documentElement.clientHeight) + parseInt(offset));
}
function _loadImage() {
for (var i = imgList.length; i--;) {
var el = imgList[i];
if (_isShow(el)) {
el.src = el.getAttribute('data-src');
el.className = el.className.replace(new RegExp("(\\s|^)" + _selector.substring(1, _selector.length) + "(\\s|$)"), " ");
imgList.splice(i, 1);
}
}
}
function _delay() {
clearTimeout(delay);
delay = setTimeout(function () {
_loadImage();
}, time);
}
function ImageLazyload(selector, options) {
var defaults = options || {};
offset = defaults.offset || 0;
time = defaults.time || 250;
_selector = selector || '.m-lazyload';
this.getNode();
_delay();//避免首次加载未触发touch事件,主动触发一次加载函数
if (defaults.iScroll) {
defaults.iScroll.on('scroll', _delay);
defaults.iScroll.on('scrollEnd', _delay);
} else {
window.addEventListener('scroll', _delay, false);
}
}
ImageLazyload.prototype.getNode = function () {
imgList = [];
var nodes = document.querySelectorAll(_selector);
for (var i = 0, l = nodes.length; i < l; i++) {
imgList.push(nodes[i]);
}
};
})();
参考链接https://segmentfault.com/a/1190000004656348
2.各种高、宽度
2.1 网页、屏幕的宽高
网页可见区域宽:document.body.clientWidth
网页可见区域高:document.body.clientHeight
网页可见区域宽(包括边线的宽):document.body.offsetWidth
网页可见区域高(包括边线的高):document.body.offsetHeight
网页正文全文宽:document.body.scrollWidth
网页正文全文高:document.body.scrollHeight
网页被卷去的高:document.body.scrollTop
网页被卷去的左:document.body.scrollLeft
网页正文部分上:window.screenTop
网页正文部分左:window.screenLeft
屏幕分辨率的高:window.screen.height
屏幕分辨率的宽:window.screen.width
屏幕可用工作区高度:window.screen.availHeight
2.2 浏览器窗口可视区域大小
网页可见区域宽高,不包括工具栏和滚动条
对于IE9+、chrome、firefox、Opera、Safari:
window.innerHeight:浏览器窗口的内部高度;
window.innerWidth:浏览器窗口的内部宽度;
对于IE8.7.6.5:
document.documentElement.clientHeight:表示HTML文档所在窗口的当前高度;
document.documentElement.clientWidth:表示HTML文档所在窗口的当前宽度;
兼容写法:
var w = document.documentElement.clientWidth || document.body.clientWidth;
var h = document.documentElement.clientHeight || document.body.clientHeight;
2.3 滚动大小
scrollHeight: 在没有滚动条的情况下,元素内容的总高。和document.body.clientHeight得到的值相近。
scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
scrollTop:被隐藏的内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。
scrollHeight和scrollWidth主要用于确定元素内容的实际大小
2.4 客户区大小
客户区大小是指元素内容即其内边距所占据的空间大小。clientWidth和clientHeight。滚动条占用的空间不计算在内。
元素内部的空间大小:document.documentElement.clientWidth
获取浏览器窗口大小:document.documentElement.clientHeight(表示HTML文档所在窗口的当前高度)。还有一种方式是window.innerHeight
2.5 偏移量:
offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、(可见的)水平滚动条的宽度、上边框高度和下边框高度;
offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、(可见的)垂直滚动条的宽度、左边框宽度和右边框宽度;
offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离;包含元素为offsetParent。
offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离;包含元素为offsetParent。
offsetParent:与当前元素最近的经过定位(不为static)的父级元素。
2.6 坐标
event.clientX 相对文档的水平座标
event.clientY 相对文档的垂直座标
event.offsetX 相对容器的水平坐标
event.offsetY 相对容器的垂直坐标
document .documentElement.scrollTop 垂直方向滚动的值
event.clientX+document .documentElement.scrollTop 相对文档的水平座标+垂直方向滚动的量
2.7 offsetParent及偏移大小
重点讲讲定位父级offsetParent及偏移大小
定位父级offsetParent:与当前元素最近的经过定位(position不为static)的父级元素,
1)元素自身有fixed定位,offsetParent的结果null
2)元素自身无fixed定位,且父级元素都未经过定位,offsetParent的结果为<body>
3)元素自身无fixed定位,且父级元素存在经过定位的元素,offsetParent的结果为离自身元素最近的经过定位的父级元素
4)<body>元素的parentNode是null
偏移量:offsetHeight、offsetWidth、offsetLeft、offsetTop
offsetWidth:元素在水平方向上占用的空间大小;无单位(以像素px计)
offsetWidth = border-left-width + padding-left-width + width + padding-right-width + border-right-width
offsetHeight:元素在垂直方向上占用的空间大小;无单位(以像素px计)
offsetHeight = border-top-height + padding-top-height + height + padding-bottom-height + border-bottom-height
PS:若存在垂直滚动条,offsetWidth也包括垂直滚动条的宽度;若存在水平滚动条,offsetHeight也包括水平滚动条的高度。
offsetTop:表示元素的上外边框至offsetParent元素的上内边框之间的像素距离
offsetLeft:表示元素的左外边框至offsetParent元素的左内边框之间的像素距离
2.8 页面偏移量
某个父元素在页面上的偏移量,将这个元素的offsetLeft和offsetTop与其offsetParent的相同属性相加,并加上offsetParent的相应方向的边框,如此循环直到根元素,皆可以得到元素到页面的偏移量。
注意点:
1、所有偏移量的属性都是只读的
2、若元素设置display:none,则它的偏移量属性都为0
3、每次访问偏移量属性都需要重新计算,因此尽量避免重复访问这些属性
3.实现页面加载进度条
页面加载进度条都是假的,因为本身API提供的状态就是假的。所以都是模拟加载,只是模拟方式各有不同
3.1 初级方法
利用setTimeout每次更新一点,检测到load事件直接取消clearTimeout,然后跳至100%
3.2 真实的获取内容,实现加载进度条
要实现根据真实内容,来加载进度条,下面要介绍两个知识点:
document.onreadystatechange 页面加载状态改变时的事件
document.readyState 返回当前文档的状态
1. uninitialized - 还未开始载入
2. loading - 载入中
3. interactive - 已加载,文档与用户可以开始交互
4. complete - 载入完成
document.onreadystatechange=function(){
if(document.readyState=="complete"){
$(".loading").fadeOut();
}
}
4.箭头函数ES5如何实现
箭头函数是匿名函数,可以简化函数的定义。
例如let f = (a,b)=>a+b 等同于let f = function(a,b){return a+b;}
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。例如let obj = a => ({a:a})
注意点:
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象0(this指向的固定化)。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
(5)除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target。
(6)由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。
(7)箭头函数没有原型属性。
箭头函数this指向固定化的原因:箭头函数本身没有this,导致内部的this就是外层代码块的this。比如:
function foo() {
setTimeout(()=>{ console.log('id:', this.id) }, 100);
}
//等同于ES5中
function foo() {
var _this = this;
setTimeout(function() {
console.log("id:", _this.id);
}, 100);
}
5.JS单线程的原因
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
6.ArrayBuffer
ArrayBuffer对象、TypedArray视图和DataView视图是 JavaScript 操作二进制数据的一个接口。虽然被称为二进制数组,但是并不是真正的数组,而是类似数组的对象。
用途:
这个接口的原始设计目的,与 WebGL 项目有关。所谓 WebGL,就是指浏览器与显卡之间的通信接口,为了满足 JavaScript 与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。文本格式传递一个 32 位整数,两端的 JavaScript 脚本与显卡都要进行格式转化,将非常耗时。这时要是存在一种机制,可以像 C 语言那样,直接操作字节,将 4 个字节的 32 位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提升。
(1)ArrayBuffer对象:代表内存之中的一段二进制数据,可以通过“视图”进行操作。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。
(2)TypedArray视图:共包括 9 种类型的视图,比如Uint8Array(无符号 8 位整数)数组视图, Int16Array(16 位整数)数组视图, Float32Array(32 位浮点数)数组视图等等。
(3)DataView视图:可以自定义复合格式的视图,比如第一个字节是 Uint8(无符号 8 位整数)、第二、三个字节是 Int16(16 位整数)、第四个字节开始是 Float32(32 位浮点数)等等,此外还可以自定义字节序。
简单说,ArrayBuffer对象代表原始的二进制数据,TypedArray 视图用来读写简单类型的二进制数据,DataView视图用来读写复杂类型的二进制数据。
ArrayBuffer对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指定格式解读二进制数据。
可以学习阮一峰大神的教程:http://es6.ruanyifeng.com/#docs/arraybuffer
7.SharedArrayBuffer
ES2017 引入SharedArrayBuffer,允许 Worker 线程与主线程共享同一块内存。SharedArrayBuffer的 API 与ArrayBuffer一模一样,唯一的区别是后者无法共享。
postMessage参数传递SharedArrayBuffer即可,直接操作共享内存。当然直接操作内存也是可以,但是容易出问题。这属于多线程操作,所以也提出Atomics对象,保证操作的原子性。
8.Atomics对象
多线程共享内存,最大的问题就是如何防止两个线程同时修改某个地址,或者说,当一个线程修改共享内存以后,必须有一个机制让其他线程同步。SharedArrayBuffer API 提供Atomics对象,保证所有共享内存的操作都是“原子性”的,并且可以在所有线程内同步。
8.1 方法和属性
1、Atomics.store(SharedBuffer, index, value)
store()方法用来向共享内存写入数据。store方法接受三个参数:SharedBuffer 的视图、位置索引和值,返回sharedArray[index]的值。
2、Atomics.load(SharedBuffer, index)
load()方法用来从共享内存读出数据。load方法只接受两个参数:SharedBuffer 的视图和位置索引,也是返回sharedArray[index]的值。
3、Atomics.wait(),Atomics.wake()
Atomics对象提供了wait()和wake()两个方法用于等待通知。这两个方法相当于锁内存,即在一个线程进行操作时,让其他线程休眠(建立锁),等到操作结束,再唤醒那些休眠的线程(解除锁)。
Atomics.wait(sharedArray, index, value, time(毫秒))
Atomics.wait用于当sharedArray[index]不等于value,就返回not-equal,否则就进入休眠,只有使用Atomics.wake()或者time毫秒以后才能唤醒。被Atomics.wake()唤醒时,返回ok,超时唤醒时返回timed-out。
Atomics.wake(sharedArray, index, count)
Atomics.wake用于唤醒count数目在sharedArray[index]位置休眠的线程,让它继续往下运行。
8.2 运算方法
1、Atomics.add(sharedArray, index, value),用于将value加到sharedArray[index],返回sharedArray[index]旧的值。
2、Atomics.sub(sharedArray, index, value),用于将value从sharedArray[index]减去,返回sharedArray[index]旧的值。
3、Atomics.and(sharedArray, index, value),用于将value与sharedArray[index]进行位运算and,放入sharedArray[index],并返回旧的值。
4、Atomics.or(sharedArray, index, value),用于将value与sharedArray[index]进行位运算or,放入sharedArray[index],并返回旧的值。
5、Atomics.xor(sharedArray, index, value),用于将vaule与sharedArray[index]进行位运算xor,放入sharedArray[index],并返回旧的值。
8.3 其他方法
1、Atomics.compareExchange(sharedArray, index, oldval, newval):如果sharedArray[index]等于oldval,就写入newval,返回oldval。
2、Atomics.exchange(sharedArray, index, value):设置sharedArray[index]的值,返回旧的值。
3、Atomics.isLockFree(size):返回一个布尔值,表示Atomics对象是否可以处理某个size的内存锁定。如果返回false,应用程序就需要自己来实现锁定。
9.JS 原型与原型链
原型:每个函数都一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象包含了可以由特定类型的所有实例共享的属性和方法。字面理解,就是prototype通过调用构造函数而创建的那个对象实例的原型对象。
原型链:每个构造函数都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针(constructor),而实例对象都包含一个指向构造函数的原型对象的内部指针(__proto__)。如果让原型对象等于另一个类型的实例,例如AType.prototype = new BType(),那么原型对象将会包含一个指向另一个原型对象的指针(__proto__),即AType的原型对象指向了Btype的原型对象,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。如果另一个原型又是额外一个类型的实例,于是就形成了实例与原型的链条,也就是原型链。
10.ES6 Class类的prototype属性和__proto__属性(继承链)
在ES5中,每个对象都有一个__proto__属性,指向对应的构造函数的prototype。
Class作为构造函数的语法糖,同时具有prototype属性和__proto__属性,因此同时存在两条继承链:
1、子类的__proto__属性表示构造函数的继承,总是指向父类(直接指向父类,构造函数继承)
2、子类的prototype属性的__proto__属性表示方法的继承,总是指向父类的prototype属性(原型链继承?)
class A {
}
class B extends A {
}
B.__proto__ === A; //true
B.prototype.__proto__ === A.prototype; //true
原因:
//B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
//B的实例继承A的静态属性
Object.setPrototypeOf(B, A);
这两条继承链可以理解为:作为一个对象,子类B的原型(__proto__属性)是父类A;作为一个构造函数,子类B的原型(prototype属性)是父类的实例。