前端面试题总结
一、JavaScript:
1. setTimeOut准时吗?
不一定准时,他只是把时间到了放进时间队列里
2. JS快速打乱一个数组
先上代码
sort + Math.random()
var arr=[1,2,3,4,,5,6,7,8,9,10];
arr.sort(function(){
return Math.random()-0.5
})
解析:
random( )方法返回一个0-1之间的随机数,用随机数减去 0.5,可以实现随机返回一个正负数字完成乱序了
3. JS 预编译
js预编译实现过程:
1.创建GO/AO对象
2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
3.将实参值和形参统一
4.在函数体里面找函数声明,值赋予函数体
4. JS ajax
//步骤一:创建异步对象
var ajax = new XMLHttpRequest();
//步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数,动态的传递参数starName到服务端
ajax.open('get','getStar.php?starName='+name);
//步骤三:发送请求
ajax.send();
//步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
if (ajax.readyState==4 &&ajax.status==200) {
//步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
console.log(ajax.responseText);//输入相应的内容
}
}
5. 节流函数(常考点)
/**
* 节流:动作绑定事件,动作发生后一段时间后触发事件,
* 在这段时间内,如果动作有发生了,则无视该动作,直到时间执行完后,才能重新触发
* 原理:在每次函数执行前先判断是否存在定时器,存在则跳过本次执行,否则设置定时器
*/
function throttle(fn, wait) {
var pre = Date().now();
return function () {
var now = Date.now();
if (now - prev >= wait) {
fun.apply(this, arguments);
pre = Date.now();
}
};
}
6. 防抖函数
/**
* 动作绑定事件,
* 动作发生后在一定时间内触发事件,
* 在这段时间内,如果动作发生了,则重新等待一定时间在触发事件
*
* 原理:在每次函数执行前先清空上一次设置的定时器
* */
function debounce(fn, wait) {
var timer;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait);
};
}
7. JS bind 实现
if (!Function.prototype.bind) {
Function.prototype.bind = function () {
var self = this, // 保存原函数
context = [].shift.call(arguments), // 保存需要绑定的this上下文
args = [].slice.call(arguments); // 剩余的参数转为数组
return function () { // 返回一个新函数
self.apply(context,[].concat.call(args, [].slice.call(arguments)));
}
}
}
8. JS map 实现
Array.prototype.map = function () {
var arr = this, result = [];
var [fn, thisValue] = Array.prototype.slice.call(arguments);
for (var i = 0; i < arr.length; i++) {
result.push(fn.call(thisValue, arr[i], i, arr))
}
return result;
}
9. IndexOf的实现
function ArrayIndexOf(arr,value,n){
var i=isNaN(n)?0:n;//有第三参时
i=(i<0)?arr.length+i:i;//第三参为负数时
for(i;i<arr.length;i++){
if(arr[i]===value){return i;}
}return -1;
}
10. 懒加载
let lazyImages = [...document.querySelectorAll('.lazy-image')]
let inAdvance = 300 // 自定义一个高度,当距离300px到达图片时加载
function lazyLoad() {
lazyImages.forEach(image => {
if (image.offsetTop < window.innerHeight + window.pageYOffset + inAdvance) { // 距离xxpx时加载图片
image.src = image.dataset.src
image.onload = () => image.classList.add('loaded')
}
})
}
lazyLoad()
window.addEventListener('scroll', _.throttle(lazyLoad, 16)) // 用到了lodash的节流函数
window.addEventListener('resize', _.throttle(lazyLoad, 16))
11. JS实现promise
class PromiseClone {
constructor (process) {
this.status = 'pending'
this.msg = ''
process(this.resolve.bind(this), this.reject.bind(this))
return this
}
resolve (val) {
this.status = 'fulfilled'
this.msg = val
}
reject (err) {
this.status = 'rejected'
this.msg = err
}
then (fufilled, reject) {
if(this.status === 'fulfilled') {
fufilled(this.msg)
}
if(this.status === 'rejected') {
reject(this.msg)
}
}
12. Jsonp跨域
function Jsonp(url, param, callback) {
var script = document.createElement('script')
param = {...param, callback}
var arr = [];
for(var key in param) {
arr.push(`${key}=${param[key]}`)
}
script.src=`${url}?${arr.join('&')}`
document.body.appendChild(script)
window[callback] = function(data) {
console.log(data)
document.removeChild(script)
}
}
13. JS 获取url参数
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
或
export function getQueryStringByStr(data) {
const url = data; // 获取url中"?"符后的字串
const theRequest = {};
if (url.indexOf('?') !== -1) {
const str = url.substr(1);
const strs = str.split('&');
for (let i = 0; i < strs.length; i += 1) {
theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1]);
}
}
return theRequest;
}
14. JS发布订阅模式
vue.js是采用数据劫持结合发布者-订阅者的方式,通过object.defineProperty()来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图,
具体步骤:
第一步,我们首先需要给observer数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter,这样当我们给对象某个值赋值,会触发setter,那么就会监听到数据.
第二步:compile解析模板指令,将模板的所有变量转换为数据,然后初始化页面视图,将每个指令对应的节点绑定更新函数,添加监听订阅者,一但数据发生改变,收到通知,改变视图
第三步:watcher订阅者是observer和Complie之间通信的桥梁,主要做的有:
1.在自身实例化时往订阅器(dep)里添加自己
2.自身必须有一个updata方法
3.待属性变动dep.notice()通知时,能调用自身的update方法,并触发Complie中绑定的回调,完美退出
第四步:mvvm作为数据绑定的入口,整合observe,Comple和Watcher三者,observe,来监听自己的model数据变化,Comple来解析模板指令,Watcher用来搭起observe和Cmplie之间通信的桥梁
达到数据变化-视图更新,视图交互变化,数据model变更双向绑定效果