前端面试题
window 对象 和 document 对象
window 对象:代表浏览器中一个打开的窗口
document 对象:代表整个 HTML 文档,可以用来访问页面中的所有元素,且每个载入浏览器的 HTML 文档都是 document 对象
document 对象是 window 对象的一部分,可以有 window. document 来访问
javascript 中的 this 指的对象是什么?
查看 JavaScript 中 this 指向的对象,this 指向的一句话法则:
永远指向其所在函数的所有者,如果没有所有者时,指向 window。
理解 this 的要点:关键在于将函数与函数名分开看待。同一个函数,在不同的执行方法下,会有不同的效果。
1)全局函数中的 this 指向
2)对象方法中的 this 指向
3)绑定函数时的 this
4)绑定函数时的 this
5)鼠标单击事件等进行函数的绑定时,this 的指向
6)setTimeout 等传参形式的 this 指向
7)改变 this 的方法:call,apply
计时器
setTimeout
Promises
setInterval
setImeediate
requestIdleCallback
requestdleCallback
setTimeout
和setInterval
setTimeout
将任务排在 x 毫秒之后运行,setInterval
每隔 x 毫秒运行一次任务。
promises
和microtasks
一个Promise
回调也被称为”microtask”,它以与 MutationObserver 回调相同的频率运行。
浅拷贝,深拷贝
- 浅拷贝是创建一个新对象,这个对象有着原始对象属性值得一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
- 深拷贝就是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟出一个新的区域存放新的对象,且修改新对象不会影响原对象。
let a1 = { b: { c: {} } };
let a2 = shallowClone(a1); //浅拷贝方法
a2.b.c === a1.b.c; //true 新旧对象还是共享同一块内存
let a3 = deepClone(a3); //深拷贝方法
a3.b.c === a1.b.c; // false 新对象跟原对象不共享内存
// 浅拷贝
let obj1 = {
name: '浪里行舟',
arr: [1, [2, 3], 4],
};
let obj3 = shallowClone(obj1);
obj3.name = '阿浪';
obj3.arr[1] = [5, 6, 7]; // 新旧对象还是共享同一块内存
// 这是个浅拷贝的方法
function shallowClone(source) {
var target = {};
for (var i in source) {
if (source.hasOwnProperty(i)) {
target[i] = source[i];
}
}
return target;
}
console.log('obj1', obj1); // obj1 { name: '浪里行舟', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3', obj3); // obj3 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
// 深拷贝
let obj1 = {
name: '浪里行舟',
arr: [1, [2, 3], 4],
};
let obj4 = deepClone(obj1);
obj4.name = '阿浪';
obj4.arr[1] = [5, 6, 7]; // 新对象跟原对象不共享内存
// 这是个深拷贝的方法
function deepClone(obj) {
if (obj === null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== 'object') return obj;
let cloneObj = new obj.constructor();
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
}
console.log('obj1', obj1); // obj1 { name: '浪里行舟', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4', obj4); // obj4 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
浅拷贝的实现方式
Object.assign()
Object.assign()方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
let obj1 = { person: { name: 'kobe', age: 41 }, sports: 'basketball' }; let obj2 = Object.assign({}, obj1); obj2.person.name = 'wade'; obj2.sports = 'football'; console.log(obj1); //{ person: { name: 'wade', age: 41 }, sports: 'basketball' }
函数库 lodash 的_.clone 方法
展开运算符
let obj1 = { name: 'Kobe', address: { x: 100, y: 100 } }; let obj2 = { ...obj1 }; obj1.address.x = 200; obj1.name = 'wade'; console.log('obj2', obj2); // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }
Array.prototype.concat()
深拷贝的实现方式
JSON.parse(JSON.stringify())
let arr = [ 1, 3, { username: ' kobe', }, ]; let arr4 = JSON.parse(JSON.stringify(arr)); arr4[2].username = 'duncan'; console.log(arr, arr4);
函数库 lodash 的_.cloneDeep 方法
手写递归方法
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
function deepClone(obj, hash = new WeakMap()) { if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作 if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); // 可能是对象或者普通的值 如果是函数的话是不需要深拷贝 if (typeof obj !== 'object') return obj; // 是对象的话就要进行深拷贝 if (hash.get(obj)) return hash.get(obj); let cloneObj = new obj.constructor(); // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身 hash.set(obj, cloneObj); for (let key in obj) { if (obj.hasOwnProperty(key)) { // 实现一个递归拷贝 cloneObj[key] = deepClone(obj[key], hash); } } return cloneObj; } let obj = { name: 1, address: { x: 100 } }; obj.o = obj; // 对象存在循环引用的情况 let d = deepClone(obj); obj.address.x = 200; console.log(d);
继承
1.原型链继承
2.借用构造函数继承
3.组合继承
4.原型式继承
防抖、节流
函数防抖
在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时
只执行最后一个被触发的,清除之前的异步任务,核心在于
清零
。
例子:页面滚动处理事件,搜索框输入联想
// 防抖
function debounce(fn, delay) {
var timer = null;
return function () {
var that = this;
var args = arguments;
clearTimeout(timer); // 清除重新计时
timer = setTimeout(function () {
fn.apply(that, args);
}, delay || 500);
};
}
// 使用
function clickHandler() {
console.log('防抖click!');
}
const handler = debounce(clickHandler);
document.getElementById('button').addEventListener('click', handler);
函数节流
只在开始执行一次,未执行完成过程中触发的忽略,核心在于开关锁。
例如:多次点击按钮提交表单,第一次有效
//节流
function throttle(fn, delay) {
let timer = null;
return function () {
if (timer) {
return false;
}
let that = this;
let args = arguments;
fn.apply(that, args);
timer = setTimeout(function () {
clearTimeout(timer);
timer = null;
}, delay || 500);
};
}
//使用
function clickHandler() {
console.log('节流click');
}
const handler = throttle(clickHandler);
document.getElementById('button').addEventListener('click', handler);
节流第一次有效,防抖反之
ajax
什么是 ajax?ajax 作用是什么
异步的javascript和xml AJAX 是一种用于创建快速动态网页的技术。 ajax用来与后台交互
原生 ajax 的五步请求
//1.创建XMLHttpRequest对象
let ajax = new XMLHttpRequest();
//2.规定请求的类型、URL以及是否异步处理请求。
ajax.open("GET",url,true);
//3.发送信息至服务器时内容编码类型
ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//4.发送请求
ajax.send(null);
//5.接受服务器响应数据
ajax.onreadystatechange = function (){
if(obj.readyState === 4 && (obj.status === 200 || obj.status === 304))
}