在官方的解释中,如[mdn]

The slice() method returns a shallow copy of a portion of an array into a new array object.

简单的说就是根据参数,返回数组的一部分的copy。所以了解其内部实现才能确定它是如何工作的。所以查看V8源码中的Array.js     可以看到如下的代码:

一、方法  ArraySlice,源码地址,直接添加到Array.prototype上的“入口”,内部经过参数、类型等等的判断处理,分支为SmartSlice和SimpleSlice处理。

function ArraySlice(start, end) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
var len = TO_UINT32(this.length);
var start_i = TO_INTEGER(start);
var end_i = len; if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);//如果没传入end,end=length,即slice第二个参数可选。 if (start_i < 0) {
start_i += len;//参数1的A分支 处理负值,+= length。如:为-1,则start从倒数第一个开始,负值绝对值小于length
if (start_i < 0) start_i = 0;//参数1的A.a分支 若仍未负值,则等于0。 即处理负值绝对值大于length [1,2,3].slice(-4)。
} else {
if (start_i > len) start_i = len;//参数1的B分支 参数大于length,则等于length,处理 [1,2,3].slice(5),返回[]
} if (end_i < 0) {
end_i += len;//参数2的A分支 处理负值,+= length。如:为-1,则start从倒数第一个开始,负值绝对值小于length
if (end_i < 0) end_i = 0;//参数2的A.a分支 若仍未负值,则等于0。 即处理负值绝对值大于length [1,2,3].slice(1,-4)。
} else {
if (end_i > len) end_i = len;//参数2的B分支 参数大于length,则等于length,处理 [1,2,3].slice(1,5) == [1,2,3].slice(1) ==
}
//最终返回结果的值。可以看到这里会返回一个新的真正的数组(ps:slice的好基友splice是修改原数组的。)
var result = [];
// 处理分支1 如果经历了上面代码的层层检查设置,结束值小于开始值,那么直接返回空数组,处理 [1,2,3].slice(2,1)
if (end_i < start_i) return result;
// 处理分支2 如果是数组 && !%IsObserved(this) && 结束大于1000 && %EstimateNumberOfElements(this) < 结束值 ,那么使用方法SmartSlice来处理
if (IS_ARRAY(this) &&
!%IsObserved(this) &&
(end_i > 1000) &&
(%EstimateNumberOfElements(this) < end_i)) {
SmartSlice(this, start_i, end_i - start_i, len, result);
} else {
// 处理分支2 调用SimpleSlice 处理。
SimpleSlice(this, start_i, end_i - start_i, len, result);
}
//设置length,似乎多余?还是v8中的数组[] 需指定length。 此处待探寻。。。
result.length = end_i - start_i; return result;
}
/*
* ......
*/
// Set up non-enumerable functions of the Array.prototype object and
// set their names.
// Manipulate the length of some of the functions to meet
// expectations set by ECMA-262 or Mozilla.
InstallFunctions($Array.prototype, DONT_ENUM, $Array(
//......
"slice", getFunction("slice", ArraySlice, 2)
//......
));

二、  SmartSlice,源码地址,字面意思是智能的slice。SimpleSlice,源码地址,简单的slice,不管他们的判断逻辑,可以看到,所有的slice处理,都是for循环,操作新建的result空数组的。也就是说,正因为返回值是新建的真实的数组,所有Array.prototype.slice.call(ArrayLike) 才会将类数组转化为真实的数组。

 // This function implements the optimized splice implementation that can use
// special array operations to handle sparse arrays in a sensible fashion.
/**
* 源码:https://github.com/v8/v8/blob/master/src/array.js#L196-L221
* @param {Array} array 具体需要艹做的数组
* @param {Number} start_i 参数1,从何处开始
* @param {Number} del_count 需要取到的长度。 参数2 - 参数1,
* @param {Number} len 数组长度
* @param {Array} deleted_elements 对于slice来说,是选择的那部分数组,对于splice来说,是删除的那些数组。
* @returns {undefined} 此处直接艹做 传入的reuslt,即可反馈到ArraySlice作用域的result,与真实的浏览器环境不一样!。
*/
function SmartSlice(array, start_i, del_count, len, deleted_elements) {
// Move deleted elements to a new array (the return value from splice).
// 猜测? 获取start_i + del_count的key。[1,2,3,4].slice(1,2) 返回 [1,2,3,4][1+2]索引3 ,而当tart_i + del_count大于length时候返回整个数组,如[1,2,3,4].slice(2,3) 即[1,2,3,4][5] 返回整个数组
var indices = %GetArrayKeys(array, start_i + del_count);
if (IS_NUMBER(indices)) {
var limit = indices;
for (var i = start_i; i < limit; ++i) {
var current = array[i];
if (!IS_UNDEFINED(current) || i in array) {
deleted_elements[i - start_i] = current;
}
}
} else {
var length = indices.length;
for (var k = 0; k < length; ++k) {
var key = indices[k];
if (!IS_UNDEFINED(key)) {
if (key >= start_i) {
var current = array[key];
if (!IS_UNDEFINED(current) || key in array) {
deleted_elements[key - start_i] = current;
}
}
}
}
}
} // This is part of the old simple-minded splice. We are using it either
// because the receiver is not an array (so we have no choice) or because we
// know we are not deleting or moving a lot of elements.
/**
* 源码:https://github.com/v8/v8/blob/master/src/array.js#L271-L282
* @param {Array} array 具体需要艹做的数组
* @param {Number} start_i 参数1,从何处开始
* @param {Number} del_count 需要取到的长度。 参数2 - 参数1,
* @param {Number} len 数组长度
* @param {Array} deleted_elements 对于slice来说,是选择的那部分数组,对于splice来说,是删除的那些数组。
* @returns {undefined} 此处直接艹做 传入的reuslt,即可反馈到ArraySlice作用域的result,与真实的浏览器环境不一样!。
*/
function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
for (var i = 0; i < del_count; i++) {
var index = start_i + i;
// The spec could also be interpreted such that %HasLocalProperty
// would be the appropriate test. We follow KJS in consulting the
// prototype.
var current = array[index];
if (!IS_UNDEFINED(current) || index in array) {
deleted_elements[i] = current;
}
}
}

三、 既然了解了实现思路,我们可以写个自己的slice方法,来实现slice的功能,不难看出。“slice.call的作用原理就是,利用call,将slice的方法作用于arrayLikeslice的两个参数为空,slice内部解析使得arguments.lengt等于0的时候 相当于处理 slice(0) : 即选择整个数组,slice方法内部没有强制判断必须是Array类型,slice返回的是新建的数组(使用循环取值)”,所以这样就实现了类数组到数组的转化,call这个神奇的方法、slice的处理缺一不可,花几分钟实现模拟slice如下:

ps: ie低版本,无法处理dom集合的slice call转数组。(虽然具有数值键值、length 符合ArrayLike的定义,却报错)搜索资料得到?(此处待确认): 因为ie下的dom对象是以com对象的形式实现的,js对象与com对象不能进行转换

(function(global, undefined) {
'use strict';
function SimpleSlice(array, start_i, del_count, len) {
var deleted_elements = [];
for (var i = 0; i < del_count; i++) {
var index = start_i + i;
var current = array[index];
if (current !== void(0) || index in array) {
deleted_elements[i] = current;
}
}
return deleted_elements;
}
Array.prototype.mySlice = function(start_i, end_i) {
var len = this.length;
start_i = start_i === undefined ? 0 : start_i - 0;
end_i = end_i === undefined ? len : end_i - 0;
if (start_i < 0) {
start_i = Math.max(start_i + len, 0);
} else if (start_i > len) {
start_i = len;
} if (end_i < 0) {
end_i = Math.max(end_i + len, 0);
} else if (end_i > len) {
end_i = len;
}
if (end_i < start_i)
return [];
return SimpleSlice(this, start_i, end_i - start_i, len);
}
})(this);
var arr = [1,2,3,4,5,6,7,8,9,10];
console.log('test ',arr)
console.log(arr.slice(2),arr.mySlice(2))
console.log(arr.slice(6,7),arr.mySlice(6,7))
console.log(arr.slice(-4),arr.mySlice(-4))
console.log(arr.slice(-4,-2),arr.mySlice(-4,-2)); (function(){
console.log('slice call arguments : ',Array.prototype.slice.call(arguments));
console.log('mySlice call arguments : ',Array.prototype.mySlice.call(arguments));
})([],'String',false); console.log(Array.prototype.slice.call({0:'a',length:1}),Array.prototype.mySlice.call({0:'a',length:1}));
在控制台输出如下:
 

观V8源码中的array.js,解析 Array.prototype.slice为什么能将类数组对象转为真正的数组?的更多相关文章

  1. angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)

    昨天晚上写完angular源码分析:angular中jqLite的实现--你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但 ...

  2. 从express源码中探析其路由机制

    引言 在web开发中,一个简化的处理流程就是:客户端发起请求,然后服务端进行处理,最后返回相关数据.不管对于哪种语言哪种框架,除去细节的处理,简化后的模型都是一样的.客户端要发起请求,首先需要一个标识 ...

  3. 挖掘隐藏在源码中的Vue技巧!

    前言 最近关于Vue的技巧文章大热,我自己也写过一篇(vue开发中的"骚操作"),但这篇文章的技巧是能在Vue的文档中找到蛛丝马迹的,而有些文章说的技巧在Vue文档中根本找不到踪迹 ...

  4. Jquery源码中的Javascript基础知识(一)

    jquery源码中涉及了大量原生js中的知识和概念,文章是我在学习两者的过程中进行的整理和总结,有不对的地方欢迎大家指正. 本文使用的jq版本为2.0.3,附上压缩和未压缩版本地址: http://a ...

  5. jquery源码中noConflict(防止$和jQuery的命名冲突)的实现原理

    jquery源码中noConflict(防止$和jQuery的命名冲突)的实现原理 最近在看jquery源码分析的视频教学,希望将视频中学到的知识用博客记录下来,更希望对有同样对jquery源码有困惑 ...

  6. Python源码中的PyCodeObject

    1.Python程序的执行过程 Python解释器(interpreter)在执行任何一个Python程序文件时,首先进行的动作都是先对文件中的Python源代码进行编译,编译的主要结果是产生的一组P ...

  7. jQuery源码中的“new jQuery.fn.init()”什么意思?

    所有文章搬运自我的个人主页:sheilasun.me 引子 最近打算试试看看jQuery的源码,刚开个头就卡住了.无论如何都理解不了jQuery源码入口部分中的 return new jQuery.f ...

  8. Netty 源码中对 Redis 协议的实现

    原文地址: haifeiWu的博客 博客地址:www.hchstudio.cn 欢迎转载,转载请注明作者及出处,谢谢! 近期一直在做网络协议相关的工作,所以博客也就与之相关的比较多,今天楼主结合 Re ...

  9. ArrayList源码中EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别

    2018年7月22日09:54:17 JDK 1.8.0_162 ArrayList源码中EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别 ...

随机推荐

  1. The Art of Multiprocessor Programming读书笔记 (更新至第3章)

    这份笔记是我2013年下半年以来读“The Art of Multiprocessor Programming”这本书的读书笔记.目前有关共享内存并发同步相关的书籍并不多,但是学术文献却不少,跨越的时 ...

  2. php在数组中判断某个值是否存在

    php在数组中查找指定值是否存在的方法有很多,记得很久以前我一直都是傻傻的用foreach循环来查找的,下面我主要分享一下用php内置的三个数组函数来查找指定值是否存在于数组中,这三个数组分别是 in ...

  3. 前端_CSS

    目录 CSS语法 CSS的四种引入方式 CSS选择器 CSS属性操作 补充 示例(一些小模板) CSS语法 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 1 2 3 4 5 6 7 ...

  4. wordpress登录密码框明文显示最后一个输入的字符

    wordpress登录密码框明文显示最后一个输入的字符 (function(a){a.fn.dPassword=function(c){var e={interval:200,duration:100 ...

  5. Notes of Daily Scrum Meeting(11.3)

    Notes of Daily Scrum Meeting(11.3) 2014年11月3日  星期一  20:00—20:30 团队成员 今日团队任务 当日工作分配额 完成情况 陈少杰 阅读理解代码中 ...

  6. Trick and Magic(OO博客第二弹)

    代码是设计,不是简单的陈述.而设计不仅要求功能的正确性,更注重设计风格和模式. 真正可以投入应用的程序设计,不是那种无脑的“黑箱”,超巨大的数组,多重循环暴力搜索,成吨全局变量……事实上,在实际应用中 ...

  7. 2018-2019-20172321 《Java软件结构与数据结构》第八周学习总结

    2018-2019-20172321 <Java软件结构与数据结构>第八周学习总结 教材学习内容总结 第12章 优先队列与堆 一.概述 堆 堆的前提就是他首先是一个完全二叉树,其次就是满足 ...

  8. Chapter 2 软件过程

    软件发展前期,人们只重视结果而忽略了过程,随着技术的成熟,软件过程的重要性被日益发觉.软件过程是软件工程人员为了获得软件产品而在软件工具的支持下实施的一系列软件工程活动. 软件过程的基本活动包括问题提 ...

  9. Leetcode题库——5.最长回文子串

    @author: ZZQ @software: PyCharm @file: longestPalindrome.py @time: 2018/9/18 20:06 要求:给定一个字符串 s,找到 s ...

  10. 解决登录不上网页得tomcat(授权manager-gui角色的操作如下)

    1:编辑/usr/local/tomcat/conf/tomcat-users.xml文件,在没有注释的内容中添加: <role rolename="manager-gui" ...