34. 能不能实现数组filter方法 ?
if (len === 0) { O.length = 0;
return undefined;
} len --;
let value = O[len];
delete O[len];
O.length = len;
return value;
}
- 能不能实现数组filter方法 ?
代码如下:
Array.prototype.filter = function(callbackfn, thisArg) { // 处理数组类型异常
if (this === null || this === undefined) { throw new TypeError("Cannot read property 'filter' of null or undefined");
} // 处理回调类型异常
if (Object.prototype.toString.call(callbackfn) != "[object Function]") { throw new TypeError(callbackfn + ' is not a function')
} let O = Object(this);
let len = O.length >>> 0;
let resLen = 0;
let res = [];
for(let i = 0; i < len; i++) { if (i in O) {
let element = O[i];
if (callbackfn.call(thisArg, O[i], i, O)) { res[resLen++] = element;
} }
} return res;
}
- 能不能实现数组splice方法 ?
splice 可以说是最受欢迎的数组方法之一,api 灵活,使用方便。现在来梳理一下用法:
splice(position, count) 表示从 position 索引的位置开始,删除count个元素 splice(position, 0, ele1, ele2, ...) 表示从 position 索引的元素后面插入一系列的元素
splice(postion, count, ele1, ele2, ...) 表示从 position 索引的位置开始,删除 count 个元素,然后再插入一系列的元素

初步实现
Array.prototype.splice = function(startIndex, deleteCount, ...addElements){ let argumentsLen = arguments.length;
let array = Object(this);
let len = array.length;
let deleteArr = new Array(deleteCount);
// 拷贝删除的元素 sliceDeleteElements(array, startIndex, deleteCount, deleteArr);
// 移动删除元素后面的元素 movePostElements(array, startIndex, len, deleteCount, addElements);
// 插入新元素 for (let i = 0; i < addElements.length; i++) {
array[startIndex + i] = addElements[i];
} array.length = len - deleteCount + addElements.length;
return deleteArr;
}
先拷贝删除的元素,如下所示:
const sliceDeleteElements = (array, startIndex, deleteCount, deleteArr) => { for (let i = 0; i < deleteCount; i++) {
let index = startIndex + i;
if (index in array) { let current = array[index];
deleteArr[i] = current;
} }
};
然后对删除元素后面的元素进行挪动, 挪动分为三种情况:
1)添加的元素和删除的元素个数相等
2)添加的元素个数小于删除的元素
3)添加的元素个数大于删除的元素
当添加的元素个数小于删除的元素时, 如图所示:

const movePostElements = (array, startIndex, len, deleteCount, addElements) => { //...
// 如果添加的元素和删除的元素个数不相等,则移动后面的元素 if(deleteCount > addElements.length) {
// 删除的元素比新增的元素多,那么后面的元素整体向前挪动 // 一共需要挪动 len - startIndex - deleteCount 个元素
for (let i = startIndex + deleteCount; i < len; i++) { let fromIndex = i;
// 将要挪动到的目标位置 let toIndex = i - (deleteCount - addElements.length);
if (fromIndex in array) { array[toIndex] = array[fromIndex];
} else { delete array[toIndex];
} }
// 注意注意!这里我们把后面的元素向前挪,相当于数组长度减小了,需要删除冗余元素 // 目前长度为 len + addElements - deleteCount
for (let i = len - 1; i >= len + addElements.length - deleteCount; i --) { delete array[i];
} }
};
当添加的元素个数大于删除的元素时, 如图所示:

const movePostElements = (array, startIndex, len, deleteCount, addElements) => { //...
if(deleteCount < addElements.length) { // 删除的元素比新增的元素少,那么后面的元素整体向后挪动
// 思考一下: 这里为什么要从后往前遍历?从前往后会产生什么问题?
for (let i = len - 1; i >= startIndex + deleteCount; i--) { let fromIndex = i;
// 将要挪动到的目标位置 let toIndex = i + (addElements.length - deleteCount);
if (fromIndex in array) { array[toIndex] = array[fromIndex];
} else { delete array[toIndex];
} }
} };
优化一: 参数的边界情况
当用户传来非法的 startIndex 和 deleteCount 或者负索引的时候,需要我们做出特殊的处理。
const computeStartIndex = (startIndex, len) => { // 处理索引负数的情况
if (startIndex < 0) { return startIndex + len > 0 ? startIndex + len: 0;
} return startIndex >= len ? len: startIndex;
}
const computeDeleteCount = (startIndex, len, deleteCount, argumentsLen) => { // 删除数目没有传,默认删除startIndex及后面所有的
if (argumentsLen === 1)
return len - startIndex;
// 删除数目过小 if (deleteCount < 0)
Array.prototype.splice = function (startIndex, deleteCount, ...addElements) { //,...
let deleteArr = new Array(deleteCount);
// 下面参数的清洗工作 startIndex = computeStartIndex(startIndex, len);
deleteCount = computeDeleteCount(startIndex, len, deleteCount, argumentsLen);
// 拷贝删除的元素 sliceDeleteElements(array, startIndex, deleteCount, deleteArr);
//...
}
优化二: 数组为密封对象或冻结对象
什么是密封对象?
密封对象是不可扩展的对象,而且已有成员的[[Configurable]]属性被设置为false,这意味着不能添加、删除方法和属性。但是属性值是可以修改的。
什么是冻结对象?
冻结对象是最严格的防篡改级别,除了包含密封对象的限制外,还不能修改属性值。
接下来,我们来把这两种情况一一排除。
// 判断 sealed 对象和 frozen 对象, 即密封对象和冻结对象 if (Object.isSealed(array) && deleteCount !== addElements.length) {
throw new TypeError('the object is a sealed object!')
} else if(Object.isFrozen(array) && (deleteCount > 0 || addElements.length > 0))
{ throw new TypeError('the object is a frozen object!')
}
好了,现在就写了一个比较完整的splice,如下:
const sliceDeleteElements = (array, startIndex, deleteCount, deleteArr) => { for (let i = 0; i < deleteCount; i++) {
let index = startIndex + i;
if (index in array) { let current = array[index];
deleteArr[i] = current;
} }
};
const movePostElements = (array, startIndex, len, deleteCount, addElements) => { // 如果添加的元素和删除的元素个数相等,相当于元素的替换,数组长度不变,被删除元素后面的元素不
需要挪动
// 将要挪动到的目标位置 let toIndex = i - (deleteCount - addElements.length);
if (fromIndex in array) { array[toIndex] = array[fromIndex];
} else { delete array[toIndex];
} }
// 注意注意!这里我们把后面的元素向前挪,相当于数组长度减小了,需要删除冗余元素 // 目前长度为 len + addElements - deleteCount
for (let i = len - 1; i >= len + addElements.length - deleteCount; i --) { delete array[i];
} } else if(deleteCount < addElements.length) {
// 删除的元素比新增的元素少,那么后面的元素整体向后挪动 // 思考一下: 这里为什么要从后往前遍历?从前往后会产生什么问题?
for (let i = len - 1; i >= startIndex + deleteCount; i--) { let fromIndex = i;
// 将要挪动到的目标位置 let toIndex = i + (addElements.length - deleteCount);
if (fromIndex in array) { array[toIndex] = array[fromIndex];
} else { delete array[toIndex];
} }
} };
const computeStartIndex = (startIndex, len) => { // 处理索引负数的情况
if (startIndex < 0) { return startIndex + len > 0 ? startIndex + len: 0;
} return startIndex >= len ? len: startIndex;
}
const computeDeleteCount = (startIndex, len, deleteCount, argumentsLen) => { // 删除数目没有传,默认删除startIndex及后面所有的
if (argumentsLen === 1)
return len - startIndex;
// 删除数目过小 if (deleteCount < 0)
return 0;
// 删除数目过大 if (deleteCount > len - startIndex)
return len - startIndex;
return deleteCount;
}
Array.prototype.splice = function(startIndex, deleteCount, ...addElements){
全站分卷索引 · 147 卷
- 1HTML
- 22. canvas 相关
- 36. 说说 title 和 alt 属性
- 42. 浮动布局的优点?有什么缺点?清除浮动有哪些方式?
- 53. 使用display:inline-block会产生什么问题?解决方法?
- 64. 布局题:div垂直居中,左右10px,高度始终为宽度一半
- 75. 盒模型
- 87. CSS如何进行圣杯布局
- 98. CSS如何实现双飞翼布局?
- 109. 什么是BFC?
- 1112. 应用场景
- 1213. 画一个对话框
- 1314. 画一个平行四边形
- 1415. 用一个div画五角星
- 15JavaScript
- 163. null是对象吗?为什么?
- 178. 如何创建并使用BigInt?
- 189. typeof 是否能正确判断类型?
- 1910. instanceof能否判断基本数据类型?
- 2012. Object.is和===的区别?
- 2115. == 和 ===有什么区别?
- 2217. 如何让if(a == 1 && a == 2)条件成立?
- 2320. 闭包有哪些表现形式?
- 2421. 如何解决下面的循环输出问题?
- 2522. 原型对象和构造函数有何关系?
- 2623. 能不能描述一下原型链?
- 2725. 函数的arguments为什么不是数组?如何转化成数组?
- 2826. forEach中return有效果吗?如何中断forEach循环?
- 2928. JS中flat---数组扁平化
- 3029. 什么是高阶函数
- 3131. 能不能实现数组map方法 ?
- 3232. 能不能实现数组reduce方法 ?
- 3333. 能不能实现数组 push、pop 方法 ?
- 3434. 能不能实现数组filter方法 ?
- 3536. 能不能实现数组sort方法?
- 3637. 能不能模拟实现一个new的效果?
- 3739. 能不能实现一个 call/apply 函数?
- 3840. 谈谈你对JS中this的理解
- 3941. JS中浅拷贝的手段有哪些?
- 4042. 能不能写一个完整的深拷贝?
- 4143. 数据是如何存储的?
- 4244. V8 引擎如何进行垃圾内存的回收?
- 4345. 描述一下 V8 执行一段JS代码的过程?
- 4446. 宏任务(MacroTask)引入
- 4547. 微任务(MicroTask)引入
- 4649. 理解EventLoop:nodejs
- 4750. nodejs 和 浏览器关于eventLoop的主要区别
- 4853. JS异步编程有哪些方案?为什么会出现这些方案?
- 4954. 能不能简单实现一下 node 中回调函数的机制?
- 5055. Promise 凭借什么消灭了回调地狱?
- 5156. 为什么Promise要引入微任务?
- 5258. 实现Promise的 resolve、reject 和 finally
- 5359. 现Promise的 all 和 race
- 5460. 谈谈你对生成器以及协程的理解
- 5561. 如何让 Generator 的异步代码按顺序执行完毕?
- 5662. 解释一下async/await的运行机制。
- 5763. forEach 中用 await 会产生什么问题?怎么解决这个问题?
- 5864. 关于JS中一些重要的api实现
- 5965. 事件流向
- 602. HTTP有哪些请求方法?
- 615. 如何理解 HTTP 状态码?
- 626. 简要概括一下 HTTP 的特点?HTTP 有哪些缺点?
- 637. 对 Accept 系列字段了解多少?
- 648. 对于定长和不定长的数据,HTTP 是怎么传输的?
- 659. HTTP 如何处理大文件的传输?
- 6610. HTTP 中如何处理表单数据的提交?
- 6711. HTTP1.1 如何解决 HTTP 的队头阻塞问题?
- 6812. 对 Cookie 了解多少?
- 6913. 如何理解 HTTP 代理?
- 7014. 如何理解 HTTP 缓存及缓存代理?
- 7117. 客户端的缓存控制
- 7219. 传统 RSA 握手
- 7320. RSA 和 ECDHE 握手过程的区别
- 7422. HTTP/2 有哪些改进?
- 7523. HTTP/2 中的二进制帧是如何设计的?
- 76TCP协议
- 771. 能不能说一说 TCP 和 UDP 的区别?
- 783. 为什么是三次而不是两次、四次?
- 794. 三次握手过程中可以携带数据么?
- 806. 说说 TCP 四次挥手的过程
- 817. 为什么是四次挥手而不是三次?
- 829. 说说半连接队列和 SYN Flood 攻击的关系
- 8310. 如何应对 SYN Flood 攻击?
- 8412. 说说 TCP 快速打开的原理(TFO)
- 8513. 能不能说说TCP报文中时间戳的作用?
- 8614. TCP 的超时重传时间是如何计算的?
- 8715. 能不能说一说 TCP 的流量控制?
- 8816. 能不能说说 TCP 的拥塞控制?
- 8917. 能不能说说 Nagle 算法和延迟确认?
- 9018. 如何理解 TCP 的 keep-alive?
- 911. 能不能说一说浏览器缓存?
- 922. 能不能说一说浏览器的本地存储?各自优劣如何?
- 933. 说一说从输入URL到页面呈现发生了什么?(网络)
- 944. 说一说从输入URL到页面呈现发生了什么?(解析算法)
- 955. 说一说从输入URL到页面呈现发生了什么?(渲染过程)
- 966. 谈谈你对重绘和回流的理解
- 977. 能不能说一说XSS攻击?
- 988. 能不能说一说CSRF攻击?
- 999. HTTPS为什么让数据传输更安全?
- 10010. 能不能实现事件的防抖和节流?
- 10111. 能不能实现图片懒加载?
- 102Vue
- 1032. mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?
- 1045. 描述下 vue 从初始化页面--修改数据--刷新页面 UI 的过程?
- 1057. 虚拟 DOM 实现原理
- 10610. Vue 组件间通信有哪些方式?
- 10714. route 和 router 的区别是什么?
- 10816. Vue 的 nextTick 的原理是什么?
- 10919. vuex
- 1101. 简单的反转链表
- 1112. 区间反转
- 1123. 两个一组翻转链表
- 1134. K个一组翻转链表
- 1145. 如何检测链表形成环?
- 1156. 如何找到环的起点
- 1167. 合并两个有序链表
- 1178. 合并 K 个有序链表
- 1189. 判断回文链表
- 119栈和队列
- 1203. 普通的层次遍历
- 1214. 二叉树的锯齿形层次遍历
- 1225. 二叉树的右视图
- 1236. 完全平方数
- 1247. 单词接龙
- 1258. 优先队列
- 1269. 关于堆的说明
- 12711. 实现优先队列
- 12812. 前 K 个高频元素
- 12913. 合并 K 个排序链表
- 13014. 什么是双端队列?
- 13116. 栈实现队列
- 13217. 队列实现栈
- 133二叉树
- 1342. 中序遍历
- 1353. 后序遍历
- 1364. 最大深度
- 1375. 最小深度
- 1386. 对称二叉树
- 1397. 二叉树的最近公共祖先
- 1408. 二叉搜索树的最近公共祖先
- 1419. 二叉树的直径
- 14210. 二叉树的所有路径
- 14311. 二叉树的最大路径和
- 14412. 验证二叉搜索树
- 14513. 将有序数组转换为二叉搜索树
- 14614. 二叉树展开为链表
- 14715. 不同的二叉搜索树II