1

我对 JavaScript 非常陌生,并且来自 C++/Java 背景,所以我更习惯于“经典”语言。

由于它没有类,我将如何在 JavaScript 中实现以下内容!?

假设我有一个基类 A,它定义了派生类 B 和 C 应该实现的一些函数,例如(Java ish 伪代码):

public class A {
  public void doSomething();
  public void doSomethingElse();
};

public class B extends A {
  public void doSomething() { 
    // does B sort of something 
  };

  public void doSomethingElse() { 
    // does B sort of something else };
  };
};

public class C extends A {
  public void doSomething() { 
    // does C sort of something 
  };

  public void doSomethingElse() { 
    // does C sort of something else };
  };
};

然后我想创建一个 B 或一个 C 并使用它,知道它将具有 A 中定义的所有功能。

例如

A a = new B();
b.doSomething();

我什至不知道这是否是我在 JS 编程时应该考虑的方式?我已经阅读了一些 Douglas Crockfords 的网页,但我仍然感到困惑!!!。

4

3 回答 3

2
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 将保持不变。

这是一个你可以用来试验的小提琴http://jsfiddle.net/Yp3UR/

在一些地方有很好的外部资源

在 StackOverflow 上,您可以查看

于 2013-06-11T14:56:54.330 回答
1

如何思考: 在 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 中不存在,但您可以通过闭包创建仅对另一个函数可见的函数。如果你需要创建私有方法,我鼓励你阅读闭包。

于 2013-06-11T16:58:57.670 回答
0

You need to do prototype chaining among classes. B.proto should point to A, as well as C.proto. The way to do prototype chaining is through the __proto__ attribute. In the past, there was a big controversy about __proto__ as not all JS engines implement it, although nowadays they do.

Implementation of __proto__ is still state as optional in the ES6 spec, but the standard will include a Object.setPrototypeOf() to do object chaining.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf

Here's a solution to your question, without using the new() operator. Check out this question "How to properly create a custom object in javascript" to know why the use of the new() operator to instantiate objects should be discouraged.

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();

Output:

A.doSomething()
A.doSomething()
B.doSomethingElse()
A.doSomething()
C.doSomething()
于 2013-06-11T15:16:38.783 回答