JavaScript Prototype深入解析与实战应用

作者:木棉时间:2026-10-23 13:26:53浏览:258

本文深入解析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程序设计有所帮助。

作者最新文章