四川省建设网站平台,合肥高端网站设计,新手做网站起步教程,公司起名网站实现观察者模式 观察者模式#xff08;基于发布订阅模式#xff09; 有观察者#xff0c;也有被观察者 观察者需要放到被观察者中#xff0c;被观察者的状态变化需要通知观察者 我变化了 内部也是基于发布订阅模式#xff0c;收集观察者#xff0c;状态变化后要主动通知观…实现观察者模式 观察者模式基于发布订阅模式 有观察者也有被观察者 观察者需要放到被观察者中被观察者的状态变化需要通知观察者 我变化了 内部也是基于发布订阅模式收集观察者状态变化后要主动通知观察者
class Subject { // 被观察者 学生constructor(name) {this.state happythis.observers []; // 存储所有的观察者}// 收集所有的观察者attach(o){ // Subject. prototype. attchthis.observers.push(o)}// 更新被观察者 状态的方法setState(newState) {this.state newState; // 更新状态// this 指被观察者 学生this.observers.forEach(o o.update(this)) // 通知观察者 更新它们的状态}
}class Observer{ // 观察者 父母和老师constructor(name) {this.name name}update(student) {console.log(当前 this.name 被通知了, 当前学生的状态是 student.state)}
}let student new Subject(学生); let parent new Observer(父母);
let teacher new Observer(老师); // 被观察者存储观察者的前提需要先接纳观察者
student. attach(parent);
student. attach(teacher);
student. setState(被欺负了);交换a,b的值不能用临时变量
巧妙的利用两个数的和、差
a a b
b a - b
a a - b
手写类型判断函数
function getType(value) {// 判断数据是 null 的情况if (value null) {return value ;}// 判断数据是引用类型的情况if (typeof value object) {let valueClass Object.prototype.toString.call(value),type valueClass.split( )[1].split();type.pop();return type.join().toLowerCase();} else {// 判断数据是基本数据类型的情况和函数的情况return typeof value;}
}
实现数组去重
给定某无序数组要求去除数组中的重复数字并且返回新的无重复数组。
ES6方法使用数据结构集合
const array [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
ES5方法使用map存储不重复的数字
const array [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];uniqueArray(array); // [1, 2, 3, 5, 9, 8]function uniqueArray(array) {let map {};let res [];for(var i 0; i array.length; i) {if(!map.hasOwnProperty([array[i]])) {map[array[i]] 1;res.push(array[i]);}}return res;
}
实现数组的push方法
let arr [];
Array.prototype.push function() {for( let i 0 ; i arguments.length ; i){this[this.length] arguments[i] ;}return this.length;
}
实现数组的乱序输出
主要的实现思路就是
取出数组的第一个元素随机产生一个索引值将该第一个元素和这个索引对应的元素进行交换。第二次取出数据数组第二个元素随机产生一个除了索引为1的之外的索引值并将第二个元素与该索引值对应的元素进行交换按照上面的规律执行直到遍历完成
var arr [1,2,3,4,5,6,7,8,9,10];
for (var i 0; i arr.length; i) {const randomIndex Math.round(Math.random() * (arr.length - 1 - i)) i;[arr[i], arr[randomIndex]] [arr[randomIndex], arr[i]];
}
console.log(arr)
还有一方法就是倒序遍历
var arr [1,2,3,4,5,6,7,8,9,10];
let length arr.length,randomIndex,temp;while (length) {randomIndex Math.floor(Math.random() * length--);temp arr[length];arr[length] arr[randomIndex];arr[randomIndex] temp;}
console.log(arr)
参考 前端进阶面试题详细解答
手写防抖函数
函数防抖是指在事件被触发 n 秒后再执行回调如果在这 n 秒内事件又被触发则重新计时。这可以使用在一些点击请求的事件上避免因为用户的多次点击向后端发送多次请求。
// 函数防抖的实现
function debounce(fn, wait) {let timer null;return function() {let context this,args arguments;// 如果此时存在定时器的话则取消之前的定时器重新记时if (timer) {clearTimeout(timer);timer null;}// 设置定时器使事件间隔指定事件后执行timer setTimeout(() {fn.apply(context, args);}, wait);};
}
实现数组的filter方法
Array.prototype._filter function(fn) {if (typeof fn ! function) {throw Error(参数必须是一个函数);}const res [];for (let i 0, len this.length; i len; i) {fn(this[i]) res.push(this[i]);}return res;
}
实现instanceOf
// 模拟 instanceof
function instance_of(L, R) {//L 表示左表达式R 表示右表达式var O R.prototype; // 取 R 的显示原型L L.__proto__; // 取 L 的隐式原型while (true) {if (L null) return false;if (O L)// 这里重点当 O 严格等于 L 时返回 truereturn true;L L.__proto__;}
}
实现深拷贝
浅拷贝 浅拷贝指的是将一个对象的属性值复制到另一个对象如果有的属性的值为引用类型的话那么会将这个引用的地址复制给对象因此两个对象会有同一个引用类型的引用。浅拷贝可以使用 Object.assign 和展开运算符来实现。深拷贝 深拷贝相对浅拷贝而言如果遇到属性值为引用类型的时候它新建一个引用类型并将对应的值复制给它因此对象获得的一个新的引用类型而不是一个原有类型的引用。深拷贝对于一些对象可以使用 JSON 的两个函数来实现但是由于 JSON 的对象格式比 js 的对象格式更加严格所以如果属性值里边出现函数或者 Symbol 类型的值时会转换失败
1JSON.stringify()
JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一它的原理就是利用JSON.stringify 将js对象序列化JSON字符串再使用JSON.parse来反序列化(还原)js对象。这个方法可以简单粗暴的实现深拷贝但是还存在问题拷贝的对象中如果有函数undefinedsymbol当使用过JSON.stringify()进行处理之后都会消失。
let obj1 { a: 0,b: {c: 0}};
let obj2 JSON.parse(JSON.stringify(obj1));
obj1.a 1;
obj1.b.c 1;
console.log(obj1); // {a: 1, b: {c: 1}}
console.log(obj2); // {a: 0, b: {c: 0}}
2函数库lodash的_.cloneDeep方法
该函数库也有提供_.cloneDeep用来做 Deep Copy
var _ require(lodash);
var obj1 {a: 1,b: { f: { g: 1 } },c: [1, 2, 3]
};
var obj2 _.cloneDeep(obj1);
console.log(obj1.b.f obj2.b.f);// false
3手写实现深拷贝函数
// 深拷贝的实现
function deepCopy(object) {if (!object || typeof object ! object) return;let newObject Array.isArray(object) ? [] : {};for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] typeof object[key] object ? deepCopy(object[key]) : object[key];}}return newObject;
}
实现一个call
call做了什么:
将函数设为对象的属性执行删除这个函数指定this到函数并传入给定参数执行函数如果不传入参数默认指向为 window
// 模拟 call bar.mycall(null);
//实现一个call方法
Function.prototype.myCall function(context) {//此处没有考虑context非object情况context.fn this;let args [];for (let i 1, len arguments.length; i len; i) {args.push(arguments[i]);}context.fn(...args);let result context.fn(...args);delete context.fn;return result;
};
实现日期格式化函数
输入
dateFormat(new Date(2020-12-01), yyyy/MM/dd) // 2020/12/01
dateFormat(new Date(2020-04-01), yyyy/MM/dd) // 2020/04/01
dateFormat(new Date(2020-04-01), yyyy年MM月dd日) // 2020年04月01日
const dateFormat (dateInput, format){var day dateInput.getDate() var month dateInput.getMonth() 1 var year dateInput.getFullYear() format format.replace(/yyyy/, year)format format.replace(/MM/,month)format format.replace(/dd/,day)return format
}
用Promise实现图片的异步加载
let imageAsync(url){return new Promise((resolve,reject){let img new Image();img.src url;img.οnlοad(){console.log(图片请求成功此处进行通用操作);resolve(image);}img.οnerrοr(err){console.log(失败此处进行失败的通用操作);reject(err);}})}imageAsync(url).then((){console.log(加载成功);
}).catch((error){console.log(加载失败);
})
解析 URL Params 为对象
let url http://www.domain.com/?useranonymousid123id456city%E5%8C%97%E4%BA%ACenabled;
parseParam(url)
/* 结果{ user: anonymous, id: [ 123, 456 ], // 重复出现的 key 要组装成数组能被转成数字的就转成数字类型 city: 北京, // 中文需解码 enabled: true, // 未指定值得 key 约定为 true}*/
function parseParam(url) {const paramsStr /.\?(.)$/.exec(url)[1]; // 将 ? 后面的字符串取出来const paramsArr paramsStr.split(); // 将字符串以 分割后存到数组中let paramsObj {};// 将 params 存到对象中paramsArr.forEach(param {if (//.test(param)) { // 处理有 value 的参数let [key, val] param.split(); // 分割 key 和 valueval decodeURIComponent(val); // 解码val /^\d$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key则添加一个值paramsObj[key] [].concat(paramsObj[key], val);} else { // 如果对象没有这个 key创建 key 并设置值paramsObj[key] val;}} else { // 处理没有 value 的参数paramsObj[param] true;}})return paramsObj;
}
使用 setTimeout 实现 setInterval
setInterval 的作用是每隔一段指定时间执行一个函数但是这个执行不是真的到了时间立即执行它真正的作用是每隔一段时间将事件加入事件队列中去只有当当前的执行栈为空的时候才能去从事件队列中取出事件执行。所以可能会出现这样的情况就是当前执行栈执行的时间很长导致事件队列里边积累多个定时器加入的事件当执行栈结束的时候这些事件会依次执行因此就不能到间隔一段时间执行的效果。
针对 setInterval 的这个缺点我们可以使用 setTimeout 递归调用来模拟 setInterval这样我们就确保了只有一个事件结束了我们才会触发下一个定时器事件这样解决了 setInterval 的问题。
实现思路是使用递归函数不断地去执行 setTimeout 从而达到 setInterval 的效果
function mySetInterval(fn, timeout) {// 控制器控制定时器是否继续执行var timer {flag: true};// 设置递归函数模拟定时器执行。function interval() {if (timer.flag) {fn();setTimeout(interval, timeout);}}// 启动定时器setTimeout(interval, timeout);// 返回控制器return timer;
}
手写节流函数
函数节流是指规定一个单位时间在这个单位时间内只能有一次触发事件的回调函数执行如果在同一个单位时间内某事件被触发多次只有一次能生效。节流可以使用在 scroll 函数的事件监听上通过事件节流来降低事件调用的频率。
// 函数节流的实现;
function throttle(fn, delay) {let curTime Date.now();return function() {let context this,args arguments,nowTime Date.now();// 如果两次时间间隔超过了指定时间则执行函数。if (nowTime - curTime delay) {curTime Date.now();return fn.apply(context, args);}};
}
使用ES5和ES6求函数参数的和
ES5
function sum() {let sum 0Array.prototype.forEach.call(arguments, function(item) {sum item * 1})return sum
}
ES6
function sum(...nums) {let sum 0nums.forEach(function(item) {sum item * 1})return sum
}
手写 Object.create
思路将传入的对象作为原型
function create(obj) {function F() {}F.prototype objreturn new F()
}
Promise.race
Promise.race function(promiseArr) {return new Promise((resolve, reject) {promiseArr.forEach(p {// 如果不是Promise实例需要转化为Promise实例Promise.resolve(p).then(val resolve(val),err reject(err),)})})
}
解析 URL Params 为对象
let url http://www.domain.com/?useranonymousid123id456city%E5%8C%97%E4%BA%ACenabled;
parseParam(url)
/* 结果
{ user: anonymous,id: [ 123, 456 ], // 重复出现的 key 要组装成数组能被转成数字的就转成数字类型city: 北京, // 中文需解码enabled: true, // 未指定值得 key 约定为 true
}
*/
function parseParam(url) {const paramsStr /.\?(.)$/.exec(url)[1]; // 将 ? 后面的字符串取出来const paramsArr paramsStr.split(); // 将字符串以 分割后存到数组中let paramsObj {};// 将 params 存到对象中paramsArr.forEach(param {if (//.test(param)) { // 处理有 value 的参数let [key, val] param.split(); // 分割 key 和 valueval decodeURIComponent(val); // 解码val /^\d$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key则添加一个值paramsObj[key] [].concat(paramsObj[key], val);} else { // 如果对象没有这个 key创建 key 并设置值paramsObj[key] val;}} else { // 处理没有 value 的参数paramsObj[param] true;}})return paramsObj;
}