浅谈用原生 js 实现数组的 slice 方法

slice 方法有这几种情况:不传参、传一个参数、传两个参数。并且传参支持负数,具体每个情况的效果在这里就不说了。

核心方法:通过 for 循环遍历 调用此方法的数组 ,把要取出的内容放入新数组,然后将新数组返回。

一切的条件处理判断,都为了 for 循环能够正确的执行

这是好久以前写的了,虽然就8行代码,但是三元运算符套用的太多了,还没加括号,现在回过头来看看真是满眼的星星。

Array.prototype._slice = function _slice() {    var n1 = Number(arguments[0]), n2 = arguments[1], n3 = [];    n2 = n2 === undefined ? this.length : Number(n2) ? Number(n2) : 0;    n2 = n2 < 0 ? Math.abs(n2) > this.length && n2 > -1 ? 0 : this.length + Math.ceil(n2) : n2 > this.length ? this.length : Math.floor(n2);    n1 = n1 ? n1 < 0 ? Math.abs(n1) > this.length || n1 > -1 ? 0 : this.length + Math.ceil(n1) : n1 > this.length ? this.length : Math.floor(n1) : 0;    for (var i = n1; i < n2; i++) {        n3[n3.length] = this[i];    }    return n3;};

所以上面的代码看看就行了,我们先来分析一下 for 循环,因为一切条件判断都是围绕这个循环来处理的,这样也便于理解接下来所解析的各种条件判断是为了什么。

i = n1 定义起始位置(包含 n1 的位置那项)

i < n2 定义结束位置(不包含 n2 的位置那项)

这个循环设置了从哪里开始取到哪里结束,所以我们只需要处理好 n1 和 n2 的值,就能满足所有的传参情况

  for 循环分析完毕,其实也没啥好分析,现在来一行一行的拆分分析代码。

因为个人表达能力有限,有些话越是想表达清楚就越是表达不清楚,觉得下方框内的文字太啰嗦的话,可以不看或者选择性忽略,或者只看标记红色的部分。

第一行:

  var n1 = Number(arguments[0]), n2 = arguments[1], n3 = [];

    用两个变量 n1 和 n2 接收 arguments 中的两个参数,用来具体判断传参情况。

    n3 的用途暂时先不说。

因为 arguments 是类数组也是个对象,所以如果当 arguments[0] 或者 arguments[1] 不存在,也不会报错而是会默认为 undefined

第二行:

  n2 = n2 === undefined ? this.length : Number(n2) ? Number(n2) : 0;

  如果觉得上面的三元运算符难以理解,可以看下面用 if 写的

    if (n2 === undefined) {
        n2 = this.length;
    } else if (Number(n2)) {
        n2 = Number(n2);
    } else {
        n2 = 0;
    }

第二行代码_IF

  通过 n2 来判断第二个形参是否有值。

  • 如果是 undefined 则表示没传值。

这时候,已经可以确定外部运行的执行代码是 slice() 或者 slice(X)

到这里,如果你想的:"判断第一个形参是否有值,如果有值就返回数组中的相应项,没值就返回所有项",不是不正确,是和本文的代码思路有些不同,我们不管第二个形参是否有传值进来、传进来的值是否是有效值,我们都要给第二个形参确定一个有效值。

这是为了把所有可能存在的分支整合成一个分支,到最后统一的一次性处理。也就是:不管传参可能会是什么样的,我们在代码尾部的for循环内只处理一次,最后我们只 return 一次就够了

  在没传值的情况下,就表示用户需要的内容为:从数组中指定的位置开始,截取到末尾

  所以我们就给这个形参赋值:this.length 。

this 代表调用 _slice 方法的数组,那 this.length 自然就是这个数组的长度了

在第二个参数为空或者无效的情况下,代表从 n1 的位置开始,取出来数组后面的所有内容。

    var ary = [1,2,3,4,5,6]
    ary.slice(1)//这里就表示从数组的第一个位置开始,取出剩下的所有项
    //结果也就是[2,3,4,5,6]

这里在 for 循环中,n1 就等于 1 , n2 就等于 5

因为现在还没有处理 n1 的值,所以 n2 是多少我们并不知道。所以先赋值 this.length ,这个值比实际所需值只多不少,所以不担心取不够的情况

给多了当然也不行,所以需要在后面会再处理一下这个情况。

  • 如果不是 undefined 则表示有值传进来,用 Number() 方法强制转换一下,保证有效性。

如果传进来的是一个数字,就将其赋给 n2

如果传进来的参数不是一个标准的数字就会被 Number() 处理为数字或 NaN,如果为 NaN 的话,为 n2 赋值为 0,表示一项也不取出(根据内置的 slice 结果处理的)

第三行:

  n2 = n2 < 0 ? Math.abs(n2) > this.length && n2 > -1 ? 0 : this.length + Math.ceil(n2) : n2 > this.length ? this.length : Math.floor(n2);

  如果觉得上面的三元运算符难以理解,可以看下面用 if 写的

    if(n2 < 0){
        if(Math.abs(n2) > this.length && n2 > -1){
            n2 = 0;
        }else {
            n2  = this.length + Math.ceil(n2);
        }
    }else if(n2 > this.length){
        n2  = this.length;
    }else{
        n2 = Math.floor(n2);
    }

第三行代码_IF

  • 如果 n2 为负数,结束位置则是从数组末尾为开始计算的,比如:
  •     var ary = [1,2,3,4,5];
        ary.slice(0,-3); //输出结果为[1,2]
        //结束位置从末尾开始算起,也就是从第0个位置开始,到倒数第3个(不包括)为止

当 n2 小于 0,并且 n2 > -1 ,也就是 n2 是零点几的负小数时,返回空数组,所以给 n2 赋值 0

当 n2 小于 0,并且 n2 的绝对值比数组长度更大时,返回空数组,所以给 n2 赋值 0

不要问为什么这么处理,这里是根据官方内置 slice 方法的结果处理的,哈哈哈~



当 n2 小于 0 ,剩下的情况下为什么是 this.length + Math.ceil(n2) ?

因为负数对应的位置转为正数就是用 数组长度+负数 来计算

比如上方的代码中,slice(0,-3),相当于 slice(0,2)

为什么还用 Math.ceil(n2)?

遇到小数向上进位

当 0 < n2 < this.length 时,自动向下取整,所以 Math.floor(n2)

当 n2 大于数组长度时,返回开始位置之后的所有项,所以给 n2 赋值 this.length

老规矩,根据内置的 slice 方法返回的结果处理的

  至此,第二个形参所有的情况已经处理完毕。


最后一行(没算上 for 循环):

  n1 = n1 ? n1 < 0 ? Math.abs(n1) > this.length || n1 > -1 ? 0 : this.length + Math.ceil(n1) : n1 > this.length ? this.length : Math.floor(n1) : 0;

  如果觉得上面的三元运算符难以理解,可以看下面用 if 写的

    if (n1) {
        if (n1 < 0) {
            if (Math.abs(n1) > this.length || n1 > -1) {
                n1 = 0;
            } else {
                n1 = this.length + Math.ceil(n1)
            }
        } else if (n1 > this.length) {
            n1 = this.length;
        } else {
            n1 = Math.floor(n1)
        }
    } else {
        n1 = 0;
    }

最后一行代码_IF

  n1 的处理就相对来说比较简单了

当 n1 为假时 ,返回整个数组,所以为 n1 赋值为 0

当 n1 为负数,并且 n1 的绝对值大于 this.length ,返回整个数组,所以为 n1 赋值为 0

当 n1 为负数,并且 n1 小于 -1 ,也就是负零点几小数 ,返回整个数组,所以为 n1 赋值为 0

剩下的 n1 为负数的区间,用 this.length + Math.ceil(n1) 来处理 n1 的值,这里的情况和上面 n2 的情况一样,就不重复了



当 n1 大于 this.length 时,从数组末尾开始取,所以为 n1 赋值 this.length

接下来,只剩 this.length >= n1 > 0 的时候了,一律向下取整(针对的小数),所以为 n1 赋值 Math.floor(n1)

为什么这么处理呢?

根据 slice 返回的结果判断的

  剩下的 for 循环在开头就已经提过了,n3 的作用也不必多说了吧,里面放的就是从执行这个方法的函数中取出来的内容。
  虽然篇幅略多,但其实思路很简单,就是处理两个形参的值,只不过文笔有限只能期望用更多的文字来表达心中的想法,暂时还无法做到一针见血的清晰表达。
  如果代码有误或者和原生 slice 有存在差别的地方,还望大家留言提醒,我会第一时间修改,提前谢谢啦!
  同时,本人也严重不建议把时间浪费在阅读这些文字描述上,大概了解思想然后通过自己一点点的尝试去总结自己的方法,比在这里生啃要强的太多了,代码不值钱,关键是思想,多动手动脑是最有用的。
  再次感叹下,理解和表达真是有区别的,想完整的把心中所想的表达出来看来还需要多写一写了,写的比较粗糙希望各位不要太介意…
时间: 2024-10-07 19:10:53

浅谈用原生 js 实现数组的 slice 方法的相关文章

浅谈用原生 js 实现函数的 bind 方法

bind的作用是让目标函数执行时候的 this 改为指定的上下文. 一般情况下,this 取决于调用者,谁调用函数 this 就是谁(自执行函数.定时器--之类的特殊情况除外). 有那么一句话,在 JavaScript 中,万物皆对象. 1 var want = function want() { 2 console.log(this); 3 } 4 5 var good = function good() { 6 console.log('https://www.cnblogs.com/xwa

浅谈ES6原生Promise

浅谈ES6原生Promise 转载 作者:samchowgo 链接:https://segmentfault.com/a/1190000006708151 ES6标准出炉之前,一个幽灵,回调的幽灵,游荡在JavaScript世界. 正所谓: 世界本没有回调,写的人多了,也就有了})})})})}). Promise的兴起,是因为异步方法调用中,往往会出现回调函数一环扣一环的情况.这种情况导致了回调金字塔问题的出现.不仅代码写起来费劲又不美观,而且问题复杂的时候,阅读代码的人也难以理解. 举例如下

浅谈小网站SEO的几点建设方法

现在很多小企业也开始建站,那该怎样做好小企业的seo呢? 1.通过seo树立品牌 对于小企业来说,当然不能做到像大型企业一样品牌人人皆知.但是与其他同等小企业.竞争对手相比,如果能够在搜索引擎结果中表现良好,就可以树立 良好的形象.所谓"排名第一就是品牌",在搜索引擎中排名第一就能让客户感觉到企业是一个充满活力.办事认真.积极上进的组织,客户第一面就打上了良 好的印象分.排名第一带来的客户在互联网真是数不甚数.那么如何通过seo树立品牌呢?第一,要使自己的企业名.地区行业关键字排名第一

【JS】怎样用原生JS实现jQuery的ready方法

Jquery中$(document).ready()的作用类似于传统JavaScript中的window.onload方法,只是与window.onload方法还是有差别的. 总的来说,window.onload()方法是必须等到页面内包含图片的全部元素载入完成后才干运行. $(document).ready()是DOM结构绘制完成后就运行,不必等到载入完成.   详细一些.能够从下面几方面对照它们的差别: 1.运行时间 window.onload必须等到页面内包含图片的全部元素载入完成后才干运

浅谈局域网ARP攻击的危害及防范方法(图)

浅谈局域网ARP攻击的危害及防范方法(图)   作者:冰盾防火墙 网站:www.bingdun.com 日期:2015-03-03   自 去年5月份开始出现的校内局域网频繁掉线等问题,对正常的教育教学带来了极大的不便,可以说是谈“掉”色变,造成这种现象的情况有很多,但目前最常见的是 ARP攻击了.本文介绍了 ARP攻击的原理以及由此引发的网络安全问题,并且结合实际情况,提出在校园网中实施多层次的防范方法,以解决因ARP攻击而引发的网络安全问题,最后介 绍了一些实用性较强且操作简单的检测和抵御攻

【JS】如何用原生JS实现jQuery的ready方法

Jquery中$(document).ready()的作用类似于传统JavaScript中的window.onload方法,不过与window.onload方法还是有区别的. 总的来说,window.onload()方法是必须等到页面内包括图片的所有元素加载完毕后才能执行.$(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕.   具体一些,可以从以下几方面对比它们的区别: 1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行

浅谈webview与js交互

浅谈下webview与js的交互,项目中因为需要获取页面中的许多属性,所以用到了这些,让后端在方法中传了过来. 下面的demo是本地的html代码,具体可以仿写,都差不多,注重思想. 1:项目目录 2:jump的代码 <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"/> <script type="text/

原生js的数组除重复

js对数组的操作在平常的项目中也会遇到,除去一些增加,或者减少的操作外,还有一个比较重要的操作就是数组的除重,通过数组的除重,我们可以将一个数组中存在的多个重复的数组进行清理,只留下不重复的.另外下面我介绍一种原生就s的数组除重方法. Array.prototype.check= function(){ for(var i=0;i<this.length;i++){ for(var j=i+1;j<this.length;j++){ if(this[i]==this[j]){ this.spl

跨域问题相关知识详解(原生js和jquery两种方法实现jsonp跨域)

1.同源策略 同源策略(Same origin policy),它是由Netscape提出的一个著名的安全策略.同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现,现在所有支持JavaScript 的浏览器都会使用这个策略. 所谓同源,就是指两个页面具有相同的协议,主机(也常说域名),端口,三个要素缺一不可. 所谓同源策略,指的是浏览器对不同源的脚本或者文本的访问