JavaScript Prototype深入解析与实战应用
本文深入解析JavaScript中Prototype属性的定义与用法。通过剖析Array.prototype.slice.call(arguments, 1)这一经典代码,阐明内置函数与原型对象的关系。同时详细讲解如何利用Prototype为对象添加公用方法、属性及实现原型继承,帮助开发者掌握JS面向对象编程的核心机制。
JavaScript Prototype深入解析与实战应用
本文通过具体实例深入解析JavaScript中Prototype的应用,旨在帮助读者更好地理解并掌握这一核心概念。
Prototype的基本定义与函数关系
回顾前文关于JS Prototype的概念,我们需要明确其基本定义。
Prototype是函数的一个内置属性,指向该函数的原型对象。关键在于,只有函数才能引用或拥有这个属性,这是必须牢记的基础规则。
Array.prototype.slice.call解析
然而,在实际开发中,你可能会看到类似如下形式的代码引用:
function func(){
var args = Array.prototype.slice.call(arguments, 1);
return args;
}
看到上述代码,你是否对“Prototype仅属于函数”这一结论产生了怀疑?
让我们先解析上述代码中函数的具体含义。
call是函数的一个方法,同样只有函数才能调用它。其核心作用是调用并执行引用它的函数,同时可以指定执行时的上下文对象。
以代码`Array.prototype.slice.call(arguments, 1)`为例,其中包含多个关键信息,我们将逐一拆解分析。
`slice(start[, end])`是JavaScript的原生数组方法,用于截取数组中从start索引开始到end索引结束(不含end)的元素。例如:
var arr1 = [2,3,'l',4,5,'j','i'];
alert(arr1.slice(1));//结束下标省略表示从开始下标截取到末尾
//这里alert的是3,'l',4,5,'j','i',你自己可以试一下
`arguments`是JavaScript函数对象的一个属性,用于获取传递给函数的实际参数,返回一个类似数组的对象。例如:
function args(){
return this.arguments;//这里的this可以省略,你自己可以试一下
//我加上是为了说明,调用arguments的只能是对象
}
alert(JSON.stringify(args(1,3,5,6,8)));
//返回的是{"0":"1","1":"3","2":"5","3":"6","4":"8"}
关于arguments对象的详细机制此处不再展开,如需深入了解可自行查阅相关资料。
而`Array`是JavaScript中用于创建数组的关键字或构造函数。
这里为何使用`Array.prototype`来调用slice方法,而不是直接`Array.slice`?原因在于,关键字Array不能直接通过`Array.xx`的方式调用数组实例方法。至于为何如此设计,我们可以通过后续实验来明确。
alert(Array.slice());
//Uncaught TypeError: Array.slice is not a function
直接执行上述代码会报错,这证实了Array关键字确实无法直接调用数组方法。
alert(JSON.stringify(Array.prototype));
alert(JSON.stringify(Array.prototype.slice()));
上述代码返回空数组`[]`,这说明两点:首先,Array关键字确实可以访问prototype属性;其次,JavaScript允许通过`Array.prototype`来调用数组方法。
这就引出了一个问题:前文提到Prototype是函数的属性,只能被函数调用,为何这里Array关键字(构造函数)也能调用?这是否推翻了之前的定义?实际上,前面的陈述是正确的,而Array调用prototype也符合逻辑,我们需要进一步分析其中的关联。
换个角度思考,大家是否记得JavaScript生成数组的多种方式?这里不赘述所有方式。
但你是否见过如下生成数组的代码?
var arr = new Array();
这种生成数组的方式是否让你联想到JavaScript中通过构造函数生成对象的方式?
function func(){
}
var obj = new func();
对比上述数组生成方式与构造函数生成对象的方式,你会发现它们非常相似。在JavaScript中,function和Array都可以通过new关键字生成对象。这暗示了什么?观察`Array()`和`func()`,它们末尾都有圆括号,这表明`Array()`实际上是一个用于构造数组的内置函数。否则,无法解释为何可以使用`new Array()`来生成数组。
回到最初的问题,既然`Array()`是JavaScript的内置函数,而函数拥有prototype属性,那么`Array.prototype`调用自然不违反“Prototype是函数属性”的原则。因此,Array关键字调用prototype完全符合逻辑,结论依然成立。
既然可以通过类似方式生成数组,那么是否也可以`new Object()`生成对象,或`new String()`生成字符串?如果这些构造方式可行,那么下面的代码执行也是合理的,对吧?
alert(JSON.stringify(String.prototype));
alert(JSON.stringify(Object.prototype));
基于上述解释,你应该能理解这段代码为何可以执行,以及其背后的原理。建议读者自行尝试验证,此处不再赘述。
至此,我们回过头来解释文章开头提到的func函数的具体作用。
func函数的主要作用是从第二个实参开始,获取并返回func函数的其余实参。
具体分析如下:
function func(){
var args = Array.prototype.slice.call(arguments, 1);
return args;
}
alert(func(0,1,2,3,4));//给func函数传实参
`Array.prototype`本身是一个空数组对象。
`Array.prototype.slice()`表示调用这个空数组的slice方法。
`Array.prototype.slice.call()`表示调用call方法,该方法将slice函数作为上下文执行。
那么,call方法是如何调用slice方法的呢?
具体过程是:`arguments`获取func函数的实参列表,生成一个对象传递给call函数;call函数将该对象作为上下文传递给Array.prototype(即空数组),并将第二个参数1传递给slice函数;随后,call执行slice(1),使slice从索引1开始获取数组元素。由于上下文是arguments对象,因此获取的元素即为arguments中的元素。综上,该func函数的作用是获取从索引1开始的所有实参。读者可自行执行上述代码进行验证。
Prototype的三大应用场景
接下来介绍Prototype在实际开发中的应用。
应用场景一:
向原型对象添加方法,使所有实例对象拥有公用的方法。
示例:为数组原型对象添加一个打乱数组顺序的方法:
//给数组原型增加一个打乱数组的函数
Array.prototype.shuffle=function(){
var value = this.valueOf(),len = this.length,temp,key;
while(len--){
//随机生成数组的下标
key = Math.floor(Math.random()*len);
//temp为中间交换变量
temp = value[key];
value[key] = value[len];
value[len] = temp;
}
return value;
}
var arr1 = [0,1,2,3,4,5,6,7,8,9];
var arr2 = ['a','b','c','d','e','f'];
alert(JSON.stringify(arr1.shuffle()));
alert(JSON.stringify(arr2.shuffle()));
你可以尝试创建另一个数组arr3,验证其是否也能调用shuffle函数。由于该方法添加在Array的原型对象上,因此脚本内所有数组实例均拥有shuffle方法。
应用场景二:
向原型对象添加属性,即为所有实例对象提供公用的属性。
示例:
function fun(){
}
fun.prototype.name = '小东';
fun.prototype.arr = [1,2,3,4];//这里的属性可以是数组,也可以是对象
var ob1 = new fun();
var ob2 = new fun();
alert(JSON.stringify(ob1.name));
alert(JSON.stringify(ob2.arr));
应用场景三:
实现原型继承机制。
function P1(){
}
function P2(){
}
//原型对象增加属性和方法
P2.prototype.name = 'P2"s name';
P2.prototype.get=function(value){
return value;
}
//实例化P2构造函数的一个对象
var obp2 = new P2();//这个对象应该包含所有原型对象的属性和方法
//给P1的原型对象赋值一个对象,相当于P1继承了obp2的所有属性和方法
P1.prototype = obp2;//这个式子,简单来讲就类似于a = b, b赋值给a这个总该明白吧?
//调用P1从obp2继承过来的get函数
alert(P1.prototype.get('out"s name'));
//展示P1从obp2继承过来的name属性
alert(P1.prototype.name);
//用构造函数P1实例化一个obp1对象
var obp1 = new P1();
//P1的原型对象prototype既然已经继承了obp2的所有属性和函数,那么依据P1所实例化出来的对象也都有obp2的属性和函数了
alert(obp1.get('obp1"s name'));
关于Prototype的讲解至此结束。如果文中存在任何错误或疏漏,欢迎读者指正,以便及时修正。
常见内置对象原型对象说明
特别提示:
- `Array.prototype`是一个数组对象。
- `String.prototype`是一个字符串对象。
- `Object.prototype`是一个对象。
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》。
希望本文所述内容对大家学习JavaScript程序设计有所帮助。
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
为什么JS中typeof []返回object:数组类型检测误区解析
2026-11-04 19:50:47
