本文深入解析 JavaScript 中 prototype 的核心机制,区分类方法、对象方法与原型方法。通过实验阐明 prototype 实现的是对象克隆而非传统继承,并演示如何利用 call 方法在实例间调用原型方法,帮助开发者掌握原型链的工作原理。
JavaScript prototype 机制解析
JavaScript 中的 prototype(原型)是许多开发者感到困惑的核心概念之一。
为了清晰阐述这一概念,本文将基于以下几个关键知识点展开讨论。
1. 原型设计模式概述 在 .NET 等语言中,可以通过 clone() 方法实现原型模式。
原型模式的核心思想是:若已存在一个类 A,现在需要创建一个以 A 为原型的类 B 并进行扩展,我们将 A 称为 B 的原型。
2. JavaScript 方法的分类: a. 类方法(静态方法)
b. 对象方法(实例方法)
c. 原型方法
具体示例如下:
function People(name){
//对象属性
this.name=name;
//对象方法
this.Introduce=function(){
alert("My name is "+this.name);
}
}
//类静态方法
People.Run=function(){
alert("I can run");
}
//原型方法
People.prototype.IntroduceChinese=function(){
alert("我的名字是"+this.name);
}
//测试
var p1=new People("Windking");
p1.Introduce();//调用对象方法
People.Run();//调用静态方法
p1.IntroduceChinese(); //调用原型方法
3. 使用 obj1.func.call(obj) 方法 该语法的作用是将 obj 视为 obj1 的上下文来调用 func 方法。原本调用的是 obj1 的 func,但通过传入 obj 改变了上下文对象,从而使得 obj 能够调用 obj1 定义的方法。
接下来,我们将逐一解决上述问题。
prototype 的具体含义是什么?
JavaScript 中的每个对象都拥有 prototype 属性。该属性的作用是返回对象类型原型的引用。
例如:A.prototype = new B();
理解 prototype 时,不应将其与传统的“继承”混淆。A 的 prototype 是 B 的一个实例,这可以理解为 A 将 B 中的方法和属性克隆了一遍,从而可以使用 B 的方法和属性。这里强调的是“克隆”而非“继承”。值得注意的是,可能出现 A 的 prototype 是 B 的实例,同时 B 的 prototype 也是 A 的实例的情况。
原型克隆与同名方法覆盖实验
首先看一个实验示例:
function baseClass(){
this.showMsg = function(){
alert("baseClass::showMsg");
}
}
function extendClass(){
}
extendClass.prototype = new baseClass();
var instance = new extendClass();
instance.showMsg(); // 显示baseClass:showMsg
我们首先定义了 baseClass 类,接着定义 extendClass。我们的策略是以 baseClass 的一个实例为原型,克隆出 extendClass,使其同时包含 showMsg 这个对象方法。
语句 extendClass.prototype = new baseClass() 可以解读为:extendClass 是以 baseClass 的一个实例为原型克隆创建的。
那么,如果 extendClass 中本身包含一个与 baseClass 同名的方法,会发生什么?
以下是扩展实验 2:
function baseClass(){
this.showMsg = function(){
alert("baseClass::showMsg");
}
}
function extendClass(){
this.showMsg =function (){
alert("extendClass::showMsg");
}
}
extendClass.prototype = new baseClass();
var instance = new extendClass();
instance.showMsg();//显示extendClass::showMsg
实验证明:函数运行时,会优先在本体(实例自身)中查找函数;如果找到则运行;如果找不到,则去 prototype 中查找。或者可以理解为:prototype 不会覆盖或克隆同名函数。
利用 call 调用原型方法
由此引出一个新问题:
如果我想使用 extendClass 的一个实例 instance 调用 baseClass 的对象方法 showMsg,该怎么做?
答案是可以使用 call 方法:
extendClass.prototype = new baseClass();
var instance = new extendClass();
var baseinstance = new baseClass();
baseinstance.showMsg.call(instance);//显示baseClass::showMsg
语句 baseinstance.showMsg.call(instance) 可以解读为:“将 instance 当作 baseinstance 来调用,执行它的对象方法 showMsg”。
这里可能有人疑问,为什么不用 baseClass.showMsg.call(instance)?
这涉及对象方法与类方法的区别。我们想要调用的是 baseClass 的对象方法,而非静态方法。
最后,如果下面的代码能够理解清晰,那么本文所阐述的内容就已经被掌握了:
function baseClass(){
this.showMsg = function(){
alert("baseClass::showMsg");
}
this.baseShowMsg = function() {
alert("baseClass::baseShowMsg");
}
}
baseClass.showMsg = function(){
alert("baseClass::showMsg static");
}
function extendClass(){
this.showMsg =function (){
alert("extendClass::showMsg");
}
}
extendClass.showMsg = function(){
alert("extendClass::showMsg static")
}
extendClass.prototype = new baseClass();
var instance = new extendClass();
instance.showMsg(); //显示extendClass::showMsg
instance.baseShowMsg(); //显示baseClass::baseShowMsg
instance.showMsg(); //显示extendClass::showMsg
baseClass.showMsg.call(instance);//显示baseClass::showMsg static
var baseinstance = new baseClass();
baseinstance.showMsg.call(instance);//显示baseClass::showMsg
以上就是关于 JavaScript 中 prototype 的深入学习,后续还将更新相关文章,希望大家继续关注。