如何思考:
在 Javascript 和类似语言中,每个对象都有一个原型,它本身也是一个对象。如果您尝试访问对象的方法或属性(因为函数是 javascript 中的第一类,方法和属性之间的区别具有误导性),并且如果解释器无法在对象本身上找到它,它将查看原型。由于原型也是一个对象,它也有自己的原型(对象原型原型)。所以有这个原型链,每个继承级别都有一个原型,它一直追溯到基类 Object ,如果它仍然没有找到您试图访问的属性,解释器将抛出未定义的属性错误。
如何实现它:
有很多方法可以进行继承,这是我使用的一种,它不一定是最好的,但它是最容易理解的一种:
//A constructor
var A= function() {};
A.prototype.doSomething= function() { };
A.prototype.doSomethingElse= function() { };
//B constructor
var B= function () {
A.apply(this, arguments); //calls A constructor on this instance of B
};
B.prototype= new A(); //this makes B "extend" A. Basically it sets B prototype to be an instance of A.
B.prototype.doSomething= function() {
A.doSomething.apply(this, arguments); //calling "super"
};
B.prototype.doSomethingElse= function() {
A.doSomethingElse.apply(this, arguments); //calling "super"
};
//C constructor
var C= function () {
A.apply(this, arguments);
};
C.prototype= new A();
C.prototype.doSomething= function() {
A.doSomething.apply(this, arguments); //calling "super"
};
C.prototype.doSomethingElse= function() {
A.doSomethingElse.apply(this, arguments); //calling "super"
};
因此,如果说 C没有方法 doSomethingElse 并且您执行以下操作:
c= new C();
c.doSomethingElse();
它将在 c 实例上调用 A.doSomethingElse 方法。
关于 .apply 函数的一点解释:javascript“扩展”对象中的函数,因此它们本身就是对象。事实上,你实际上可以这样做:
var myFunc= new Function("alert('myFunc');");
myFunc();
由于函数是对象,它们也有自己的属性和方法。“申请”就是其中之一。
当你这样说时:
//A constructor
var A= function() {};
A.prototype.doSomething= function() { };
您实际上是在创建一个函数并将其存储在 A 中,然后在 A 原型中放置一个方法(请记住,函数是对象,因此它们具有原型)。当你这样做时:
var a= new A(arg1,arg2,arg3...);
您正在创建 A 的一个实例,“new”运算符是一种特殊的运算符,它基本上是这样做的:
a= A.apply(a, [arg1,arg2,arg3,...]);
a.prototype= A.prototype;
这是 Function.apply 方法的解释:
https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects %2FF功能%2申请
这是传递给它的“参数”数组的解释:
https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments
当你这样说时:
C.prototype.doSomething= function() {
A.doSomething.apply(this, arguments);
};
请注意,在这种情况下,A.doSomething类似于Java 中的静态函数,您调用的不是 A 实例的方法,而是构造函数 A 上存在的方法(该方法实际上在 A.原型,但由于函数是对象,解释器将自行在原型上查找它)。
您可以这样做,因为构造函数是函数,函数是对象,对象具有原型,原型是对象,对象内部可以具有函数。疯了吧?但是如果你停下来想想前面解释的原型链,这并不难。把那句话重读几遍。
为什么要讲这些废话?
您现在可能有点困惑,我鼓励您在网上寻找更多示例。但很容易说这是令人费解且过于复杂的,它有点(在 javascript 中)但它也非常强大。您可以在运行时修改对象的行为:
如果您在运行时的任何时候执行这一点代码
A.prototype.doSomething= function() {
A.doSomethingElse.(apply(this, arguments));
}
您实际上是在修改 A 的所有实例以及从它继承的任何其他类的行为(也修改了 B 和 C 的所有实例)。在这种情况下,您的 A.doSomething 现在的行为将与 A.doSomethingElse 完全相同。想想没有所有疯狂代码的 Java 反射。
事实上,您可以修改 Javascript 的内置类(如 String 或 Array)的行为:如果您在程序中的某处运行此代码:
String.prototype.alert= function() {
alert(this);
}
您现在可以执行以下操作: "text".alert(); 然后会出现一个弹出框,里面有“文本”。但是不要像那样修改内置类,这是一种不好的做法。
这只是使用基于原型的面向对象的众多优点之一。还有很多其他的。
私有方法呢?
它们在 javascript 中不存在,但您可以通过闭包创建仅对另一个函数可见的函数。如果你需要创建私有方法,我鼓励你阅读闭包。