本文探讨JavaScript中为类添加原型方法的两种常见方式:直接赋值函数对象与使用函数声明语法。通过分析脚本解析器的初始化优先级,指出函数声明具有最高优先级,而赋值语句仅在运行时执行。这导致在类定义顺序上,使用函数声明定义原型方法可能因解析顺序问题引发错误,而直接赋值则依赖运行时上下文,两者在行为机制上存在本质区别。
原型方法定义的两种常见方式
为JavaScript类添加原型(prototype)方法通常十分简便。在实际开发中,主要有两种常见的实现方式,但这两种方式在具体的使用场景和底层机制上是否存在差异呢?
JScript Class:
function JSClass()
{
}
Extends prototype method:
JSClass.prototype.MethodA = function()
{
};
Or
function = JSClass.prototype.MethodA()
{
};
为了更清晰地讨论这两种原型定义方式,我们可以将其简化为两个独立的函数进行对比,如下所示:
Foo1();
function Foo1()
{
alert('This is Foo1.');
}
和 Foo2();
var Foo2 = function()
{
alert('This is Foo2.');
}
函数声明与赋值表达式的执行优先级差异
执行第一个调用显然不会报错,但执行第二个调用时会出现问题,系统会提示:Microsoft JScript runtime error: Object expected。这表明函数定义(Foo1)在脚本解析阶段拥有最高的初始化优先级,这是合乎逻辑的。如果不优先处理函数定义,那么函数内部的调用将无法被正确处理。例如,若先定义fn1()再定义fn2(),却在fn1中调用fn2,解析过程将会失败。为什么Foo2无法被初始化?因为Foo2的定义并非函数声明,而是一条标准的赋值语句。它之所以能像标准函数一样被调用(Foo2()),完全是因为变量Foo2指向了一个函数对象的实例。
原型方法在类定义中的实际表现
再看原型方法导入的两种不同方式,原理便一目了然。由于执行优先级的不同,它们在实际使用中也表现出差异。请看以下示例:
<script language="javascript">
function NormalClass()
{
this.m_Property1 = 'P1 in Normal Class.';
this.m_Property2 = 'P2 in Normal Class.';
this.toString = function()
{
return '[class NormalClass]';
}
return new InnerClass();
function InnerClass()
{
this.m_Property1 = 'P1 in Inner Class.';
this.m_Property2 = 'P2 in Inner Class.';
this.toString = function()
{
return '[class InnerClass]';
}
}
InnerClass.prototype.Method1 = function()
{
alert(this.m_Property1);
};
function InnerClass.prototype.Method2()
{
alert(this.m_Property2);
};
}
</script>
执行:
var nc = new NormalClass();
nc.Method1();
nc.Method2();
最终效果如何?原因是什么?