1. 如何实现瀑布流?
瀑布流布局的原理:
1) 瀑布流布局要求要进行布置的元素等宽, 而后计算元素的宽度, 与浏览器宽度之比,获得须要布置的列数;
2) 建立一个数组,长度为列数, 里面的值为已布置元素的总高度(最开始为0);
3) 而后将未布置的元素依次布置到高度最小的那一列, 就获得了瀑布流布局;
4) 滚动加载, scroll事件获得scrollTop, 与最后盒子的offsetTop对比, 符合条件就不断滚动加载。 瀑布流布局核心代码:
/** * 实现瀑布流的布局 * @param {string}parentBox * @param {string}childBox */
function waterFull(parentBox, childBox) {
// 1. 求出父盒子的宽度 // 1.1 获取全部的子盒子 var allBox = $(parentBox). getElementsByClassName(childBox); // 1.2 求出子盒子的宽度 var boxWidth = allBox[0].offsetWidth; // 1.3 获取窗口的宽度 var clientW = document. documentElement.clientWidth; // 1.4 求出总列数 var cols = Math.floor(clientW / boxWidth); // 1.5 父盒子居中 $(parentBox).style.width = cols * boxWidth + 'px';
// 2. 子盒子定位 $(parentBox).style.margin = '0 auto'; // 2.1 定义变量 var heightArr = [], boxHeight = 0, minBoxHeight = 0, minBoxIndex = 0;
// 2.2 遍历全部的子盒子 for (var i = 0; i < allBox.length; i++) {
// 2.2.1 求出每个子盒子的高度 boxHeight = allBox[i].offsetHeight; // 2.2.2 取出第一行盒子的高度放入高度数组中 if (i < cols) { // 第一行 heightArr.push(boxHeight); } else { // 剩余行的盒子 // 2.2.3 取出数组中最矮的高度 minBoxHeight = _.min(heightArr);
// 2.2.4 求出最矮高度对应的索引 minBoxIndex = getMinBoxIndex(heightArr, minBoxHeight); // 2.2.5 盒子定位 allBox[i].style.position = 'absolute';
// 2.2.6 更新最矮的高度 allBox[i].style.left = minBoxIndex * boxWidth + 'px'; allBox[i].style.top = minBoxHeight + 'px'; heightArr[minBoxIndex] += boxHeight; } } }
/** * 根据内容取出在数组中对应的索引 * @param {object}arr * @param {number}val * @returns {boolean} */
function getMinBoxIndex(arr, val) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === val) return i; } }
/** * 判断是否具有加载子盒子的条件 * @returns {boolean} */
function checkWillLoadImage() {
// 1. 获取最后一个盒子
var allBox = $('main').getElementsByClassName('box');
var lastBox = allBox[allBox.length - 1];
// 2. 求出高度
var lastBoxDis = lastBox.offsetHeight * 0.5 + lastBox.offsetTop;
// 3. 求出窗口的高度
var clientH = document.documentElement.clientHeight;
// 4. 求出页面滚动产生的高度
var scrollTopH = scroll().top;
// 5. 对比
return lastBoxDis <= clientH + scrollTopH; }
2. 原生JS都有哪些方式能够实现两个页面间的通讯?
1) 经过url地址栏传递参数;
例如:点击列表页中的每一条数据,
咱们跳转到同一个详细页面,
可是根据点击的不同能够看到
不一样的内容,这样的话咱们就能够
在URL中传递不一样的值来区分了;
2) 经过本地存储 cookie、localeStorage、
sessionStroage...,例如:京东的登陆,
咱们在登陆页登陆完成后,
把用户信息存储到本地,
而后在其它页面中若是须要使用的话,
咱们直接从本地的存储数据中拿
出来用便可;
3) 使用iframe在A页面中嵌入B页面,
这样的话,在A中能够经过一些属性
和方法实现和B页面的通讯;
4) 利用postMessage实现页面间通讯,
父窗口往子窗口传递信息,
子窗口往父窗口传递信息。
3. 原生JS动态向一个div中插入1000个div标签,如何实现?
此题主要考性能!
1) 能够用JS中的createElement建立div,
每当建立一个就把它添加到div中,
但会形成引起回流的次数太多;
2) 使用字符串拼接的方式,
把1000个div都拼接完成后,
统一的添加到页面中,
但会对div原有的元素标签产生影响:
原来标签绑定的事件都消失了
3) 综合1和2可使用文档碎片方式来处理。
追问:若是是建立1000万个呢?
可采用的方案: 数据分批异步加载
1) 首先把前两屏幕的数据量
(例如:300条)先获取到,
而后使用字符串拼接或者文档碎片
的方式绑定到页面中;
2) 当浏览器滚动到指定的区域的
时候在加载300条...以此类推。
4. 程序出现bug了,你是如何调试的?
1) 在控制台加断点, F10->逐过程 F11->逐语句;
2) 在代码重点的位置加入
console.log输出对应的值来进行调试;
3) debugger调试;
4) 代码分割还原调试;
5) 异常捕获机制, 记录运行日志;
6) 单元测试。
5. 开发中是如何进行性能优化的?
如今框架(vue, react,...)、构建工具(webpack, ...)
已经给咱们解决掉大部分的性能优化问题,
面试时, 能够就你了解的框架来深刻剖析,
但此题应该是考原生JS的范畴,
参考答案以下:
1) 雅虎35条性能优化黄金定律;
2) JS代码优化: a. 项目中的JS/CSS文件最好一个页面只用一个, 须要把JS/CSS进行合并压缩, 而且减小页面中的垃圾冗余代码。 项目的资源文件在服务器上最好 作一下GZIP压缩。 b. 解除文件缓存; 咱们修改代码并上传, 若是以前页面访问过该网站, 颇有可能不能当即见效; 咱们在引入CSS/JS文件的时候, 在文件名的后面加上版本号(加时间戳), 好比:
<script src='itlike.com.js?_=202001...'></script>; 当咱们上传新的文件后 把时间戳改一下就能够清除缓存了。 c. 移动端尽可能少用图片: icon能用svg画的不用图片; 静态资源图:作布局的时候就能肯定下来的图片, 好比:
1) css sprite图片合并 (针对于小图片)
2) 作图片延迟加载 (针对于大图片 头部的长条图片、背景大图...),
开始给一张默认的小的图片 (最好维持在10kb之内)
3) base64 (存在问题: 页面的代码太臃肿了,之后维护很差操做); 若是项目中因为图片太大实在解决不了, 改为base64就解决了 d. 动态数据图: 经过ajax从后台读取回来的图片 , 图片懒加载; e. 音视频文件的优化: 加载页面的时候,尽可能不要加载音视频文件, 当页面中的其余资源加载完成后, 再开始加载音视频文件; 目前移动端常常给音视频作的优化是: 走直播流文件(音频后缀名是m3u8格式); f. 减小页面资源请求的次数: 若是当前只是一个宣传页, 或者是一个简单的页面, 使用的css和js能够采用内嵌式开发; g. ajax数据请求分批请求, 例如:一次要请求10000条数据的话, 咱们每一次只请求100条,第一屏幕确定能看全了, 当页面滚动到对应的其它屏幕的时候, 在加载下一个100条... h. 作数据的二次缓存, 能用CSS3作动画的绝对不用JS, 能使用transform尽可能使用, 能用animation的进行不用transition... 尽可能减小同步操做,多用异步操做; 能使用原生JS本身编写的, 绝对不用插件或者框架;

6. 如何实现电商网站中的楼层效果?
1) 封装缓动动画函数;
2) 点击切换, 滚动切换, 联动处理; 核心代码以下:
// 3. 监听GPS上的li的点击
for (var j = 0; j < olLis.length; j++) { (function (index) {
var olLi = olLis[index]; olLi.onmousedown = function () { isClick = true;
// 3.1 排他 for (var m = 0; m < olLis.length; m++) { olLis[m].className = '' } addClass(this, 'current');
// 3.2 让楼层滚动起来 buffer( document.documentElement, {'scrollTop': index * client().height},
function () { isClick = false; }) } })(j) }
// 4. 监听文档的滚动
window.onscroll = function (ev1) {
// 4.1 没有点击产生的滚动
if (!isClick) {
// 4.2 获取页面产出的头部滚动的高度
var roll = Math.ceil(scroll().top);
console.log(roll);
// 4.3 遍历
for (var i = 0; i < olLis.length; i++) {
// 4.4 判断
if (roll >= ulLis[i].offsetTop) {
for (var m = 0; m < olLis.length; m++) { olLis[m].className = '' } addClass(olLis[i], 'current'); } } } } 缓动动画函数:
/** * 缓动动画(撩课学院) * @param {object}obj * @param {object}json * @param {function}fn */
function buffer(obj, json, fn) {
// 1.1 清除定时器 clearInterval(obj.timer);
// 1.2 设置定时器 var begin = 0, target = 0, speed = 0; obj.timer = setInterval(function () {
// 1.3.0 旗帜 var flag = true;
for (var k in json) {
// 1.3 获取初始值 if ("opacity" === k) {
// 透明度 begin = parseInt(parseFloat(getCSSAttrValue(obj, k)) * 100); target = parseInt(parseFloat(json[k]) * 100); } else if ("scrollTop" === k) { begin = Math.ceil(obj.scrollTop); target = parseInt(json[k]); } else {
// 其余状况 begin = parseInt(getCSSAttrValue(obj, k)) || 0; target = parseInt(json[k]); }
// 1.4 求出步长 speed = (target - begin) * 0.2;
// 1.5 判断是否向上取整 speed = (target > begin)? Math.ceil(speed): Math.floor(speed);
// 1.6 动起来 if ("opacity" === k) {
// 透明度// w3c的浏览器 obj.style.opacity = (begin + speed) / 100;
// ie 浏览器 obj.style.filter = 'alpha(opacity:' + (begin + speed) + ')'; } else if ("scrollTop" === k) { obj.scrollTop = begin + speed; } else if ("zIndex" === k) { obj.style[k] = json[k]; } else { obj.style[k] = begin + speed + "px"; }
// 1.5 判断 if (begin !== target) { flag = false; } }
// 1.3 清除定时器 if (flag) { clearInterval(obj.timer);
// 判断有没有回调函数 if (fn) { fn(); } } }, 20); }