前端面试知识点大全——JS篇(五)

总纲:前端面试知识点大全

目录

1.图片懒加载

1.1 什么是懒加载

1.2 手动实现懒加载

2.各种高、宽度

2.1 网页、屏幕的宽高

2.2 浏览器窗口可视区域大小

2.3 滚动大小

2.4 客户区大小

2.5 偏移量:

2.6 坐标

2.7 offsetParent及偏移大小

2.8 页面偏移量

3.实现页面加载进度条

3.1 初级方法

3.2 真实的获取内容,实现加载进度条

4.箭头函数ES5如何实现

5.JS单线程的原因

6.ArrayBuffer

7.SharedArrayBuffer

8.Atomics对象

8.1 方法和属性

8.2 运算方法

8.3 其他方法

9.JS 原型与原型链

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属性)是父类的实例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值