试图理解JavaScript继承性
我对JavaScript非常陌生,并且来自C++/Java背景,因此我更习惯使用“经典”语言 我如何在JavaScript中实现以下内容,因为它没有类 假设我有一个基类a,它定义了派生类B和C应该实现的一些函数,例如(Java ish psuedo代码): 然后我想创建一个B或C并使用它,因为我知道它将拥有a中定义的所有函数 e、 g 我甚至不知道在用JS编程时是否应该这样想?我读过一些Douglas Crockfords的网页,但我仍然感到困惑试图理解JavaScript继承性,javascript,inheritance,Javascript,Inheritance,我对JavaScript非常陌生,并且来自C++/Java背景,因此我更习惯使用“经典”语言 我如何在JavaScript中实现以下内容,因为它没有类 假设我有一个基类a,它定义了派生类B和C应该实现的一些函数,例如(Java ish psuedo代码): 然后我想创建一个B或C并使用它,因为我知道它将拥有a中定义的所有函数 e、 g 我甚至不知道在用JS编程时是否应该这样想?我读过一些Douglas Crockfords的网页,但我仍然感到困惑 function A() { }; A.pr
function A() {
};
A.prototype.doSomething = function() {
console.log("hi")
}
A.prototype.doSomethingElse = function() {}
function B() {
A.apply(this,arguments) )
};
function C() {
A.apply(this,arguments) )
};
B.prototype = Object.create(A.prototype);
C.prototype = Object.create(A.prototype);
var b = new B();
var c = new C();
b.doSomething();
c.doSomethingElse();
通过在函数的原型链上设置这些对象,可以从其他对象继承
因为您可能希望覆盖这些基本函数,所以可以这样做
B.prototype.doSomething = function() {
console.log("goodbye");
}
当您调用doSomething时,类型b的对象将说再见而不是你好,而类型c将保持不变
这是一把小提琴,你可以用它来做实验
在一些地方有很好的外部资源
- 有关JS类的详细概述
- 更全面地了解JS中的对象创建
\uuuu proto\uuu
属性。在过去,关于\uuuu proto\uuuu
有很大的争议,因为并非所有的JS引擎都实现了它,尽管现在它们实现了
在ES6规范中,\uuuuuu proto\uuuuuu
的实现仍然是可选的,但是标准将包括一个对象。setPrototypeOf()
来进行对象链接
下面是您的问题的解决方案,无需使用new()运算符。查看此问题,了解为什么不鼓励使用new()
操作符实例化对象
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
function A() {
var a = {};
a.doSomething = function() {
console.log("A.doSomething()");
}
return a;
}
function B() {
var b = {};
Object.setPrototypeOf(b, A());
b.doSomethingElse = function() {
console.log("B.doSomethingElse()");
}
return b;
}
function C() {
var c = {};
Object.setPrototypeOf(c, A());
c.doSomething = function() {
this.__proto__.doSomething();
console.log("C.doSomething()");
}
return c;
}
var a = A();
var b = B();
var c = C();
a.doSomething();
b.doSomething();
b.doSomethingElse();
c.doSomething();
输出:
A.doSomething()
A.doSomething()
B.doSomethingElse()
A.doSomething()
C.doSomething()
如何思考: 在Javascript和类似的语言中,每个对象都有一个原型,原型本身也是一个对象。如果您试图访问对象的方法或属性(因为函数在javascript中是第一类的,所以方法和属性之间的区别是误导性的),并且如果解释器在对象本身上找不到,它将查看原型。因为原型也是一个对象,所以它也有自己的原型(对象原型)。所以有一个原型链,每个继承级别一个原型,一直跟踪到基类对象,如果它仍然没有找到您试图访问的属性,解释器将抛出一个未定义的属性错误 如何实施: 继承的方法有很多种,这是我使用的一种方法,不一定是最好的,但却是最容易理解的:
//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() { };
实际上,您正在创建一个函数并将其存储在中,然后将一个方法放入原型中(记住,函数是对象,所以它们有原型)。执行此操作时:
var a= new A(arg1,arg2,arg3...);
您正在创建一个实例,“新”操作符是一种特殊类型的操作符,基本上是这样做的:
a= A.apply(a, [arg1,arg2,arg3,...]);
a.prototype= A.prototype;
下面是函数的说明。应用方法:
下面是对传递给它的“arguments”数组的解释:
当你这么说的时候:
C.prototype.doSomething= function() {
A.doSomething.apply(this, arguments);
};
请注意,在本例中,A.doSomething类似于Java中的静态函数,您不是调用A实例的方法,而是调用构造函数A上的方法(该方法实际上在一个.prototype上,但由于函数是对象,解释器将自己在prototype上查找它)
你可以这么做,因为构造器是函数,函数是对象,对象有原型,原型是对象,对象里面可以有函数。疯狂吧?但是如果你停下来想一想前面解释的原型链,那就没那么难了。只要把那句话再读几遍就行了
为什么这么胡说八道?
你现在可能有点困惑,我鼓励你在网上寻找更多的例子。但很容易说这是一个复杂的问题,虽然有点复杂(在javascript中),但它也非常强大。你可以在运行时修改对象的行为:
如果您在运行时的任何时候执行这段代码
A.prototype.doSomething= function() {
A.doSomethingElse.(apply(this, arguments));
}
实际上,您正在修改A的所有实例以及从其继承的任何其他类的行为(B和C的所有实例也被修改)。在这种情况下,您的A.doSomething现在的行为将与A.doSomethingElse完全相同。
想想没有所有疯狂代码的Java反射
事实上,您可以修改Javascript内置类(如字符串或数组)的行为:
如果在程序中的某个位置运行此代码:
String.prototype.alert= function() {
alert(this);
}
您现在可以执行以下操作:
“text.alert();
然后会出现一个弹出框,里面有“文本”。但是不要像那样修改内置类,这是一个糟糕的做法
这只是使用基于原型的面向对象的众多优势之一,还有很多其他优势
私有方法呢?
它们不存在于javascript中,但您可以通过闭包使其他函数只能看到的函数。如果需要创建私有方法,我鼓励您阅读闭包的信息。Ch
A.prototype.doSomething= function() {
A.doSomethingElse.(apply(this, arguments));
}
String.prototype.alert= function() {
alert(this);
}