Ajax 是什么? 如何创建一个Ajax?
AJAX全称是Asychronous JavaScript And Xml(异步的 JavaScript 和 XML)
它的作用是用来实现客户端与服务器端的异步通信效果,实现页面的局部刷新,早期的浏览器并不能原生支持ajax,可以使用隐藏帧(iframe)方式变相实现异步效果,后来的浏览器提供了对ajax的原生支持
其主要通过XMLHttpRequest(标准浏览器)、ActiveXObject(IE浏览器)对象实现异步通信效果
实现方式(gitee上的案例):
var xhr =null;//创建对象
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open(“方式”,”地址”,”标志位”);//初始化请求
xhr.setRequestHeader(“”,””);//设置http头信息
xhr.onreadystatechange =function(){}//指定回调函数
xhr.send();//发送请求
Ajax的优缺点
优点:
缺点:
一个页面从输入 URL 到页面加载显示完成,发生了什么?
JQuery一个对象为何可以同时绑定多个事件
低层实现方式是使用addEventListner或attachEvent兼容不同的浏览器实现事件的绑定,这样可以给同一个对象注册多个事件
对页面某个节点的拖曳
1. 给需要拖拽的节点绑定mousedown, mousemove, mouseup事件
2. mousedown事件触发后,开始拖拽
3. mousemove时,需要通过event.clientX和clientY获取拖拽位置,并实时更新位置
4. mouseup时,拖拽结束
5. 需要注意浏览器边界的情况
gitee上的案例
function mouseMove(ele, parent) {
ele.addEventListener('mousedown', moveHandler);
ele.style.position = 'absolute'
function moveHandler(e) {
if (e.type === 'mousedown') {
parent.ele = this;
parent.point = {
x: e.offsetX,
y: e.offsetY
}
parent.addEventListener('mousemove', moveHandler);
parent.addEventListener('mouseup', moveHandler);
} else if (e.type === 'mousemove') {
this.ele.style.left = e.x - this.point.x + "px";
this.ele.style.top = e.y - this.point.y + "px";
} else if (e.type === 'mouseup') {
parent.removeEventListener("mousemove", moveHandler);
parent.ele = null;
parent.point = null;
}
}
}
new操作符具体干了什么
以下是模拟操作:
new TestObj('str')=function(){
let obj={}; //创建一个空对象
obj.__proto__=TestObj.prototype;
//把该对象的原型指向构造函数的原型对象,就建立起原型了:obj->Animal.prototype->Object.prototype->null
return TestObj.call(obj,arguments);// 绑定this到实例化的对象上
}
前端开发的优化问题
(1) 减少http请求次数:CSS Sprites, JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。
(2) 前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数
(3) 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。
(4) 当需要设置的样式很多时设置className而不是直接操作style。
(5) 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。
(6) 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。
(7) 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。
(8) 避免在页面的主体布局中使用table,table要等其中的内容完全下载之后才会显示出来,显示比div+css布局慢。
fetch和Ajax有什么不同
如何编写高性能的Javascript
定时器setInterval有一个有名函数fn,setInterval(fn,500)与setInterval(fn(),500)有什么区别?
第一个是重复执行每500毫秒执行一次,后面一个只执行一次。
简述一下浏览器内核
浏览器内核又可以分成两部分:渲染引擎和 JS 引擎。它负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。JS 引擎则是解析 Javascript 语言,执行 javascript 语言来实现网页的动态效果。
JavaScript的属性元数据有哪些?
懒加载(瀑布流)的实现原理
意义:懒加载的主要目的是作为服务器前端优化,减少请求数或延迟请求数实现原理:先加载一部分数据,当触发某个条件时利用异步加载剩余的数据,新得到的数据,不会影响有数据的显示,同时最大程度上减少服务器的资源消耗
实现方式:
(1)延迟加载,使用setTimeOut或setInterval进行加载延迟
(2)符合某些条件,或触发了某些事件才开始异步下载
(3)可视区加载
我的懒加载文章,以及源码地址
js实现数组去重
双层循环,外层循环元素,内层循环时比较值,如果有相同的值则跳过,不相同则push进数组
class MyArray extends Array {
constructor() {
super(...arguments)
}
distinct() {
var myArr = this,
list = []
for (var i = 0; i < myArr.length; i++) {
for (var j = i + 1; j < myArr.length; j++) {
if (myArr[i] === myArr[j]) {
j = ++i;
}
}
list.push(myArr[i]);
}
return list;
}
}
var _arr = new MyArray(4, 5, 6, 7, 7, 7, 1, 1, 1, 2, 2, 2, 5, 8, 5, 2, 4, 4, 4, 6, 9);
console.log(_arr.distinct()); //[7, 1, 8, 5, 2, 4, 6, 9]
利用对象的属性不能相同的特点进行去重
class MyArray extends Array {
constructor() {
super(...arguments)
}
distinct() {
var myArr = this,
list = [],
obj = {}
for (var i = 0; i < myArr.length; i++) {
obj[myArr[i]] || (obj[myArr[i]] = 1,
list.push(myArr[i])) //如果能查找到,证明数组元素重复了
}
return list;
}
}
var _arr = new MyArray(4, 5, 6, 7, 7, 7, 1, 1, 1, 2, 2, 2, 5, 8, 5, 2, 4, 4, 4, 6, 9);
console.log(_arr.distinct()); //[4, 5, 6, 7, 1, 2, 8, 9]
Set数据结构,它类似于数组,其成员的值都是唯一的
function dedupe(array) {
return Array.from(new Set(array));
}
console.log(dedupe([1, 1, 2, 3])) //[1,2,3]
实现快速排序和冒泡排序
快速排序:选取位置在数组中间的一个数,然后比它小的放在left[]的一个新数组里面,比他大的放在right[]的一个新数组里面,以此类推,重复执行这个过程,利用递归的思想,直至执行到left[]和right[]里面都只有一个数
冒泡排序:两两比较,前面的比后面的大,则换位。第一轮list.length-1次,挑出最大的;第二轮list.length-1-1次,挑出第二大的。以此往复
class MyArray extends Array {
constructor() {
super(...arguments)
}
quickSort(list) { //快速排序
var myArr = this,
listConfig = {
midItem: myArr[parseInt(myArr.length / 2)],
leftList: new MyArray(),
rightList: new MyArray()
}
if (myArr.length <= 1) {
return myArr
};
for (var i = 0; i < myArr.length; i++) {
myArr[i] < listConfig.midItem ? listConfig.leftList.push(myArr[i]) : myArr[i] > listConfig
.midItem ? listConfig.rightList.push(myArr[i]) : '';
}
return listConfig.leftList.quickSort().concat([listConfig.midItem], listConfig.rightList
.quickSort()); //递归
}
bubbleSort() { //冒泡排序
for (var i = 0; i < this.length - 1; i++) {
for (var j = 0; j < this.length - 1 - i; j++) {
if (this[j] > this[j + 1]) {
var item = this[j];
this[j] = this[j + 1];
this[j + 1] = item;
}
}
}
return this
}
}
var quickSortArray = new MyArray(19, 15, 18, 17, 11, 21, 14, 61, 13, 10, 25);
var bubbleSortArray = new MyArray(9, 5, 8, 7, 1, 2, 4, 6, 3, 10, 25);
console.log(quickSortArray.quickSort()); //[10, 11, 13, 14, 15, 17, 18, 19, 21, 25, 61]
console.log(bubbleSortArray.bubbleSort()); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 25]
谈谈节流和防抖,如何实现
节流:使频繁执行的函数,定时执行,高频率函数执行时,使执行率减少,每n秒才能执行一次,打个比方:每隔1秒钟,会执行5次滚动条滚动事件,我只让它每一秒执行一次(案例:网站中的返回顶部)
防抖:使频繁执行的函数,延时执行,高频率函数执行时,n秒内只执行一次,在事件内多次执行会延时,打个比方:用户在输入框中输入字符,当用户一直在输入时,我们做个延时,当用户输入完毕后会有一段时间停顿,若这个停顿时间大于我们的我们延时时间,我们就进行下一步操作,反之则不进行并且一直延时(案例:搜索引擎搜索输入框)
区别:对于高频率执行函数,节流是每隔规定时间都会执行一次,防抖是只在规定时间外的最后一次执行
实现过程:
var count = 0
class OptimizeEvent {
constructor() {}
throttle(fn, time) { //节流
var canDo = true
return function (e) {
if (!canDo) {
return false
}
canDo = false
setTimeout(() => {
fn.call(this)
canDo = true
}, time)
}
}
debounce(fn, time) { //防抖
var _timer = null
return function () {
if (_timer) {
clearTimeout(_timer)
_timer = null
}
_timer = setTimeout(fn, time)
}
}
}
var _event = new OptimizeEvent()
inputBox.addEventListener('input', _event.debounce(function () {
showBox.textContent = inputBox.value
}, 1000))
document.addEventListener('scroll', _event.throttle(function () {
console.log(count++)
}, 1000))
谈谈深拷贝的实现
深拷贝相对浅拷贝不同的是,深拷贝内所有引用类型属性值都是在新开辟的内存地址,被拷贝的原数据发生改变时不会影响复制后的对象。
常见方法
function deepClone(org, tag) {
var tag = tag || {}; //初始化要复制的对象
var name = Object.getOwnPropertyNames(org); //获取该对象的属性名,以字符串数组返回
for (var i = 0; i < name.length; i++) { //遍历对象
var desc = Object.getOwnPropertyDescriptor(org, name[i]); //获取对象的属性描述对象,无引用关系,返回另一个对象,改变时原对象不发生变化(复制的关键)
if (typeof desc.value === 'object' && desc.value !== null) { //若遍历的每一项非空且为对象,则为引用值,则进行下一步
var obj = desc.value.toString() === '[object Object]' ? {} : []; //判断是数组还是对象
Object.defineProperty(tag, name[i], { //设置对象属性值,前三个的值是返回true或false
configurable: desc.configurable, //是否可删除可替换
enumerable: desc.enumerable, //是否可枚举可遍历
writable: desc.writable, //是否可写入
value: obj //对象的值
});
copyObj(desc.value, obj); //再次执行函数
} else {
Object.defineProperty(tag, name[i], desc); //否则直接将该对象的属性值进行复制(原始值)
}
}
return tag;
}
常用的对象方法有哪些
添加或更改对象属性
Object.defineProperty(object, property, descriptor)
添加或更改多个对象属性
Object.defineProperties(object, descriptors)
访问属性
Object.getOwnPropertyDescriptor(object, property)
以数组返回所有属性
Object.getOwnPropertyNames(object)
以数组返回所有可枚举的属性
Object.keys(object)
访问原型
Object.getPrototypeOf(object)
阻止向对象添加属性
Object.preventExtensions(object)
如果可将属性添加到对象,则返回 true
Object.isExtensible(object)
防止更改对象属性(而不是值)
Object.seal(object)
如果对象被密封,则返回 true
Object.isSealed(object)
防止对对象进行任何更改
Object.freeze(object)
如果对象被冻结,则返回 true
Object.isFrozen(object)