可以使用哪些技术在 JavaScript 中定义类,它们的权衡是什么?

IT技术 javascript oop class
2021-01-29 03:02:22

我更喜欢在大型项目中使用 OOP,比如我现在正在处理的项目。我需要在 JavaScript 中创建几个类,但是,如果我没有记错的话,至少有几种方法可以做到这一点。语法是什么,为什么要这样做?

我想避免使用第三方库——至少一开始是这样。
在寻找其他答案时,我找到了文章Object-Oriented Programming with JavaScript, Part I: Inheritance - Doc JavaScript,该文章讨论了JavaScript中的面向对象编程。有没有更好的继承方式?

6个回答

这是不使用任何外部库的方法:

// Define a class like this
function Person(name, gender){

   // Add object properties like this
   this.name = name;
   this.gender = gender;
}

// Add methods like this.  All Person objects will be able to invoke this
Person.prototype.speak = function(){
    alert("Howdy, my name is" + this.name);
};

// Instantiate new objects with 'new'
var person = new Person("Bob", "M");

// Invoke methods like this
person.speak(); // alerts "Howdy, my name is Bob"

现在真正的答案比这复杂得多。例如,JavaScript 中没有类这样的东西。JavaScript 使用prototype基于 -based 的继承方案。

此外,还有许多流行的 JavaScript 库,它们都有自己的风格来逼近 JavaScript 中的类功能。您至少需要查看PrototypejQuery

决定其中哪一个是“最好的”是在 Stack Overflow 上开始一场圣战的好方法。如果您正在着手一个更大的 JavaScript 重项目,那么学习一个流行的库并按照他们的方式去做绝对值得。我是一个 Prototype 人,但 Stack Overflow 似乎倾向于 jQuery。

至于只有“一种方法”,对外部库没有任何依赖,我写的方式几乎就是这样。

根据developer.mozilla.org/en-US/docs/Web/JavaScript/...属性也应该添加到原型中(“Person.prototype.name='';”)
2021-03-18 03:02:22
但它不像 X 语言那样工作,在那里我学到了一种真正的方法,即用于制作对象实例的东西应该可以工作:(
2021-03-30 03:02:22
从 2015 年下半年开始,EcmaScript 6 新标准发布,所以我建议采用新的方式(更简洁和更轻松es6-features.org/#ClassDefinition
2021-04-01 03:02:22
@DaveD - 也许确实如此,但似乎不再如此..?
2021-04-04 03:02:22
jQuery 甚至不提供任何方法来创建类功能???(它拥有的所有类都是 CSS 类)您应该从答案的那部分中删除它。
2021-04-08 03:02:22

在 JavaScript 中定义类的最好方法是不定义类。

严重地。

面向对象有几种不同的风格,其中一些是:

  • 基于类的 OO(由 Smalltalk 首次引入)
  • 基于原型的 OO(由 Self 首次引入)
  • 基于多方法的面向对象(我认为首先由 CommonLoops 引入)
  • 基于谓词的面向对象(不知道)

可能还有其他我不知道的。

JavaScript 实现了基于原型的 OO。在基于原型的 OO 中,通过复制其他对象(而不是从类模板实例化)来创建新对象,并且方法直接存在于对象中而不是类中。继承是通过委托完成的:如果一个对象没有方法或属性,它会在它的原型(即它被克隆的对象)上查找,然后是原型的原型等等。

换句话说:没有课程。

JavaScript 实际上对该模型进行了很好的调整:构造函数。您不仅可以通过复制现有对象来创建对象,还可以“凭空”构建它们,可以这么说。如果您使用new关键字调用函数,该函数将成为构造函数,并且this关键字不会指向当前对象,而是指向新创建的“空”对象。因此,您可以按自己喜欢的任何方式配置对象。通过这种方式,JavaScript 构造函数可以在传统的基于类的 OO 中扮演类的角色之一:作为新对象的模板或蓝图。

现在,JavaScript 是一种非常强大的语言,因此如果您愿意,可以很容易地在 JavaScript 中实现基于类的 OO 系统但是,只有在您确实需要它时才应该这样做,而不仅仅是因为 Java 就是这样做的。

“如果您使用 new 关键字调用一个函数,该函数将成为一个构造函数,而 this 关键字将不会指向当前对象,而是指向一个新创建的“空”对象”。如果你调用一个没有 new 关键字的函数,这将引用调用上下文,默认情况下是全局对象(窗口)。在严格模式下, undefined 是默认值。call、apply 和 bind 将调用上下文作为第一个参数。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-03-31 03:02:22

ES2015 类

在 ES2015 规范中,您可以使用类语法,它只是原型系统的糖。

class Person {
  constructor(name) {
    this.name = name;
  }
  toString() {
    return `My name is ${ this.name }.`;
  }
}

class Employee extends Person {
  constructor(name, hours) {
    super(name);
    this.hours = hours;
  }
  toString() {
    return `${ super.toString() } I work ${ this.hours } hours.`;
  }
}

好处

主要好处是静态分析工具发现更容易针对此语法。来自基于类的语言的其他人也更容易使用该语言作为多语言。

注意事项

警惕它目前的局限性。要实现私有属性,必须求助于使用 Symbols 或 WeakMaps在未来的版本中,类很可能会被扩展以包含这些缺失的功能。

支持

目前浏览器支持不是很好(除了 IE 几乎所有人都支持),但是您现在可以通过像Babel这样的转译器来使用这些功能

资源

我更喜欢使用 Daniel X. Moore 的{SUPER: SYSTEM}. 这是一门提供真正实例变量、基于特征的继承、类层次结构和配置选项等优点的学科。下面的例子说明了真实实例变量的使用,我认为这是最大的优势。如果您不需要实例变量并且只对公共或私有变量感到满意,那么可能有更简单的系统。

function Person(I) {
  I = I || {};

  Object.reverseMerge(I, {
    name: "McLovin",
    age: 25,
    homeState: "Hawaii"
  });

  return {
    introduce: function() {
      return "Hi I'm " + I.name + " and I'm " + I.age;
    }
  };
}

var fogel = Person({
  age: "old enough"
});
fogel.introduce(); // "Hi I'm McLovin and I'm old enough"

哇,这本身并不是很有用,但是看看添加一个子类:

function Ninja(I) {
  I = I || {};

  Object.reverseMerge(I, {
    belt: "black"
  });

  // Ninja is a subclass of person
  return Object.extend(Person(I), {
    greetChallenger: function() {
      return "In all my " + I.age + " years as a ninja, I've never met a challenger as worthy as you...";
    }
  });
}

var resig = Ninja({name: "John Resig"});

resig.introduce(); // "Hi I'm John Resig and I'm 25"

另一个优点是能够拥有基于module和特征的继承。

// The Bindable module
function Bindable() {

  var eventCallbacks = {};

  return {
    bind: function(event, callback) {
      eventCallbacks[event] = eventCallbacks[event] || [];

      eventCallbacks[event].push(callback);
    },

    trigger: function(event) {
      var callbacks = eventCallbacks[event];

      if(callbacks && callbacks.length) {
        var self = this;
        callbacks.forEach(function(callback) {
          callback(self);
        });
      }
    },
  };
}

拥有 person 类的示例包括可绑定module。

function Person(I) {
  I = I || {};

  Object.reverseMerge(I, {
    name: "McLovin",
    age: 25,
    homeState: "Hawaii"
  });

  var self = {
    introduce: function() {
      return "Hi I'm " + I.name + " and I'm " + I.age;
    }
  };

  // Including the Bindable module
  Object.extend(self, Bindable());

  return self;
}

var person = Person();
person.bind("eat", function() {
  alert(person.introduce() + " and I'm eating!");
});

person.trigger("eat"); // Blasts the alert!

披露:我是 Daniel X. Moore,这是我的{SUPER: SYSTEM}. 这是在 JavaScript 中定义类的最佳方式。

使用 javascript 对象定义 Javascript 类的简单方法:wappee.com/story/i/203
2021-03-17 03:02:22
你几乎听起来像一个声称最好的 xD 的超级英雄
2021-03-20 03:02:22
(换句话说,使用术语“实例变量”的正常含义,变量是否是一个变量与变量的可访问性级别正交。)
2021-03-24 03:02:22
@DanielXMoore“实例变量在类的各个实例之间共享”那些不是实例变量,那些是静态/类变量。
2021-03-31 03:02:22
@JAB 这是不正确的,静态/类变量在类的所有实例之间共享每个实例都有自己的实例变量。
2021-04-07 03:02:22
var Animal = function(options) {
    var name = options.name;
    var animal = {};

    animal.getName = function() {
        return name;
    };

    var somePrivateMethod = function() {

    };

    return animal;
};

// usage
var cat = Animal({name: 'tiger'});
这是一种非常优雅的方式来构建一个可用的对象结构,而无需导入任何东西。我正在使用 Resig 的class系统,但我可能更喜欢这个。谢谢你。
2021-03-30 03:02:22
这种方法的问题在于,每次创建新的 Animal 实例时,它都会重新定义函数,而不是仅使用原型定义一次。
2021-04-07 03:02:22