【知识图谱】【编码题】-- 原理类
1. 深拷贝
1.1 WeakMap(ES6新增):提供了一种主动解决内存回收的方式,TODO,先不展开
属性:WeakMap.prototype.constructor
方法:set、delete、has、get
1.2 浅拷贝方法:Object.assign()、{...obj}、Array.prototype.slice()、Array.prototype.concat()
1.3 循环引用问题:TODO
1.4 递归爆栈问题:TODO
1.5 正解:(4步走,1.定义判断数组和对象,2.简单类型直接返,3.数组对象用扩展,4.利用递归搞循环(嵌套))
点击查看代码
function deepClone(obj,) {
// 1. 定义判断对象&数组的函数
function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
function isArray(obj) {
return Array.isArray(obj);
}
let result;
// 2. 拷贝简单类型,直接返回
if (!isObject(obj) && !isArray(obj)) {
return obj;
}
// 3. 对象&数组情况,(利用扩展运算符是浅拷贝,只拷贝第一层)
if (isObject(obj)) {
result = { ...obj };
}
if (isArray(obj)) {
result = [...obj];
}
// 4. 递归处理[重点][非常好,就是用for...in进行循环遍历]
for (var key in obj) {
if ( isObject(obj[key]) || isArray(obj[key]) ) {
result[key] = deepClone(obj[key]);
} else {
result[key] = obj[key];
}
}
return result;
}
参考链接:面试题之如何实现一个深拷贝 [经典!]
2. 防抖:// 防抖函数:防止多次调用,在时间间隔内只调用一次。(口诀:每次执行前都要清空定时器,return一个闭包)
// 节流函数:防止多次调用,每隔一段时间进行一次调用。(口诀:每次执行前都要检查定时器,非空则返回,return一个闭包)
点击查看代码
// 这是防抖函数
function debounce(callback,timeout,immediate){
// 1. 声明定时器ID
let id;
return function(...arguments){
// 2. 清空定时器(所以防抖要清空)
clearInterval(id);
if(immediate){
// 3. apply立即执行
callback.apply(this,arguments);
immediate = false;
}else {
// 4. 间隔执行
id = setTimeout(()=>{
callback.apply(this,arguments);
},timeout);
}
}
}
3. 节流:场景:被频繁调用的,window.onresize()、mousemove事件、上传进度等
点击查看代码
// 1.自我实现1 利用时间戳
function throttle(callback,timeout){
let lastExceTime = 0;
return function(...args){
let now = +new Date();
if(now - lastExceTime > timeout){
callback.apply(this,args);
lastExceTime = now;
}
}
}
// 2.自我实现2 检查此时定时器是否为空[推荐]
// immediate是否立即执行
function throttle(callback,timeout,immediate = true){
let throttleId;
return function(...args){
if(throttleId) return;
if(immediate){
callback.apply(this,args);
immediate = false;
return;
}
throttleId = setTimeout(()=>{
callback.apply(this,args);
throttleId = null;
},timeout);
}
}
4. 手写splice
4.1 this是原数组,通过slice进行截取
4.2 通过this[i] = newArr[i]的形式进行改写
4.3 this.length = newArr.length,完成改造
点击查看代码
Array.prototype.mysplice = function(start, nums, ...args) {
let deleteCounts = this.slice(start,start+nums);// 被删除元素
let newArr = [...this.slice(0, start), ...args, ...this.slice(start+nums)];//删除元素后原数组
for(let i = 0; i< newArr.length; i++) {
// 改变原数组的核心步骤
this[i] = newArr[i];
}
// 改变原数组的核心步骤
this.length = newArr.length;
return deleteCounts;
}
let arr = [1,2,3,4,5,6];
console.log(arr.mysplice(-2,1),arr);
5. 手写call、apply、bind函数
5.1 arguments是类数组,里面是所有的入参,[...arguments]可以转换成真正的数组;
5.2 this是待调用函数,
5.3 核心逻辑:改变this对象(context),把调用函数新增到新对象上(context),执行完再删除。
点击查看代码call
Function.prototype.myCall = function (context) {
var context = context || window;
// 1. [添函数]给 context 添加函数
context.fn = this;
// 2. [取参数]将 context 后面的参数取出来
var args = [...arguments].slice(1);
var result = context.fn(...args);
// 3. [删函数]删除函数
delete context.fn;
return result;
}
function a(){
console.log(this.name);
}
let b = {
name: 'zhangsan',
}
a.myCall(b,'1','2');
点击查看代码apply
Function.prototype.myApply = function (context) {
context = context || window;
context.fn = this;
let result
// 需要判断是否存储第二个参数
// 如果存在,就将第二个参数展开
if(arguments[1]){
result = context.fn(...arguments[1]);
}else {
result = context.fn();
}
delete context.fn
return result
}
bind是个难点,要晕了!TODO
点击查看代码bind
Function.prototype.mybind = function(ctx, ...rest) {
// 根本不需要进行判断,如果是其他类型,会因为找不到mybind函数而报错的
//if (typeof this !== 'function') {
// throw new Error('只能由 function 调用');
//}
// 步骤1:把this执行赋值新变量
const func = this;
// 步骤2:返回一个新函数,函数里面用apply
let result = function(...params) {
return func.apply(
// 问题1. 为什么要判断是不是构造函数的实例?[不然的话,new操作会改变ctx对象的属性的,其实是想改变实例的属性值]
// 如果是 new 对象出的,this 绑定的应该绑定为构造函数
this instanceof result ? this : ctx,
rest.concat(params)
);
};
// 问题2. 为什么要设置一个空函数?
[不改的话,是个对象]
// 问题3. 为什么要设置原型链接?[不然new出来的实例的构造函数就是result了,其实应该是调用函数,要回本溯源,new会返回一个新对象]
// 步骤3:巧设空函数当桥梁,返回函数的原型是空函数的原型
let fNOP = function() {};
fNOP.prototype = func.prototype;
result.prototype = new fNOP();
console.log(result,result.prototype);
return result;
};
6. Promise的实现:TODO
6.1 概念:什么是Promise,一种可以链式调用、解决之前回调地域的异步编程方式
6.2 回调地域:指一层嵌套一层,代码冗余、难以维护、可读性差的异步编程方式
6.3 Promise用法:
// then()返回的也是个Promise对象
let p1 = new Promise((resolve,reject)=>{
resolve();// 是个函数
reject();// 是个函数
});
p1.then((resolve,reject)=>{
resolve();// 是个函数
reject();// 是个函数
}
)
.then()
.then()
7. 手写instanceof:判断实例对象的原型链(__proto__)上是否有构造函数的原型prototype (口诀:要是new出来的实例对象)
点击查看代码
function myInstanceof(left,right){
left = left.__proto__;
let prototype = right.prototype;
while(true){
if(left === null){
return false;
}
if(left === prototype){
return true;
}
left = left.__proto__;
}
}

浙公网安备 33010602011771号