当前位置:  开发笔记 > 编程语言 > 正文

可以使用哪些技术在JavaScript中定义类,以及它们的权衡取舍?

如何解决《可以使用哪些技术在JavaScript中定义类,以及它们的权衡取舍?》经验,为你挑选了12个好方法。

我更喜欢在大型项目中使用OOP,就像我现在正在研究的那样.我需要在JavaScript中创建几个类,但是,如果我没有弄错的话,至少有几种方法可以做到这一点.什么是语法,为什么会以这种方式完成?

我想避免使用第三方库 - 至少在开始时.
寻找其他答案,我找到了文章面向对象的JavaScript编程,第一部分:继承 - Doc JavaScript,讨论了JavaScript中的面向对象编程.是否有更好的继承方式?



1> Triptych..:

这是在不使用任何外部库的情况下执行此操作的方法:

// 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基于继承的方案.

此外,还有许多流行的JavaScript库,它们在JavaScript中具有类似于类的功能.你至少要检查一下Prototype和jQuery.

决定哪一个是"最好的"是在Stack Overflow上开始圣战的好方法.如果你正在着手一个更大的JavaScript项目,那么绝对值得学习一个受欢迎的图书馆并按照自己的方式去做.我是一个原型人,但Stack Overflow似乎倾向于jQuery.

至于只有"一种方法来做它",没有任何外部库的依赖,我写的方式就是它.


但它不像X语言那样工作,我学会了一个真正的方式,用于使对象实例的东西应该工作:(
从2015年的一半开始,EcmaScript 6新标准发布,所以我建议采用新的方式(更清洁,更简单)http://es6-features.org/#ClassDefinition
jQuery甚至没有提供任何方法来创建类似于类的功能??? (它拥有的所有类都是CSS类)你应该从答案的那一部分中删除它.
根据https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript,属性也应该添加到原型中("Person.prototype.name ='';")

2> Jörg W Mitta..:

在JavaScript中定义类的最佳方法是不定义类.

认真.

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

基于类的OO(由Smalltalk首次引入)

基于原型的OO(由Self首次推出)

基于多方法的OO(我认为首先由CommonLoops引入)

基于谓词的OO(不知道)

还有其他我不知道的.

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

换句话说:没有课程.

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

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



3> Dale..:
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这样的转换器来使用这些功能.

资源

ECMAScript 6中的类(最终语义)

什么?等待.真?不好了!(关于ES6课程和隐私的帖子)

兼容性表 - 类

巴别塔 - 班级



4> Daniel X Moo..:

我更喜欢使用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"

另一个优点是具有模块和基于特征的继承的能力.

// 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类的示例包括可绑定模块.

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中定义类的最佳方法.


@JAB这是不正确的,静态/类变量在类的所有*实例之间共享.每个实例都有自己的实例变量.
你几乎听起来像是一个声称最好的xD的超级英雄

5> liammclennan..:
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'});


这种方法的问题在于,每次创建一个新的Animal实例时,它都将重新定义函数,而不是仅使用原型定义它们一次.

6> Amol M Kulka..:

以下是在javascript中创建对象的方法,到目前为止我已经使用过了

例1:

obj = new Object();
obj.name = 'test';
obj.sayHello = function() {
    console.log('Hello '+ this.name);
}

例2:

obj = {};
obj.name = 'test';
obj.sayHello = function() {
    console.log('Hello '+ this.name);
}
obj.sayHello();

例3:

var obj = function(nameParam) {
    this.name = nameParam;
}
obj.prototype.sayHello = function() {
    console.log('Hello '+ this.name);
}

示例4:Object.create()的实际好处.请参考[此链接]

var Obj = {
    init: function(nameParam) {
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var usrObj = Object.create(Obj);  // <== one level of inheritance

usrObj.init('Bob');
usrObj.sayHello();

例5(自定义Crockford的Object.create):

Object.build = function(o) {
   var initArgs = Array.prototype.slice.call(arguments,1)
   function F() {
      if((typeof o.init === 'function') && initArgs.length) {
         o.init.apply(this,initArgs)
      }
   }
   F.prototype = o
   return new F()
}
MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}  // For example

var userB = {
    init: function(nameParam) {
        this.id = MY_GLOBAL.nextId();
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var bob = Object.build(userB, 'Bob');  // Different from your code
bob.sayHello();


使用ES6/ES2015更新答案

类定义如下:

class Person {
    constructor(strName, numAge) {
        this.name = strName;
        this.age = numAge;
    }

    toString() {
        return '((Class::Person) named ' + this.name + ' & of age ' + this.age + ')';
    }
}

let objPerson = new Person("Bob",33);
console.log(objPerson.toString());



7> Jarek..:

我想你应该读道格拉斯Crockford的JavaScript中原型继承和在JavaScript经典传承.

他的页面中的示例:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

影响?它将允许您以更优雅的方式添加方法:

function Parenizor(value) {
    this.setValue(value);
}

Parenizor.method('setValue', function (value) {
    this.value = value;
    return this;
});

我还推荐他的视频: 高级JavaScript.

您可以在他的页面上找到更多视频:http: //javascript.crockford.com/在John Reisig的书中,您可以找到Douglas Crockfor网站上的许多示例.


只有我吗?这更优雅怎么样?我会将函数定义称为实际的''strings'`作为很多东西的名字,但优雅不是其中之一......
@JAB,但反射是例外,而不是规则.使用上面的方法,您必须*使用字符串声明所有方法*.

8> annakata..:

因为我不承认YUI/Crockford的工厂计划,因为我喜欢保持自包含和可扩展的东西,这是我的变化:

function Person(params)
{
  this.name = params.name || defaultnamevalue;
  this.role = params.role || defaultrolevalue;

  if(typeof(this.speak)=='undefined') //guarantees one time prototyping
  {
    Person.prototype.speak = function() {/* do whatever */};
  }
}

var Robert = new Person({name:'Bob'});

理想情况下,类型测试类似于第一种方法原型



9> Sam..:

如果你想要简单,你可以完全避免使用"new"关键字,只使用工厂方法.我有时更喜欢这个,因为我喜欢使用JSON来创建对象.

function getSomeObj(var1, var2){
  var obj = {
     instancevar1: var1,
     instancevar2: var2,
     someMethod: function(param)
     {  
          //stuff; 
     }
  };
  return obj;
}

var myobj = getSomeObj("var1", "var2");
myobj.someMethod("bla");

不过,我不确定大型物体的性能是什么.


使用这种方法,你失去了继承的能力,因为你没有使用obj.prototype.something,所以你每次使用object = more memory时都会定义函数.

10> Mick..:
var Student = (function () {
    function Student(firstname, lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
        this.fullname = firstname + " " + lastname;
    }

    Student.prototype.sayMyName = function () {
        return this.fullname;
    };

    return Student;
}());

var user = new Student("Jane", "User");
var user_fullname = user.sayMyName();

这就是TypeScript使用构造函数将类编译为JavaScript的方式.



11> orip..:

简单的方法是:

function Foo(a) {
  var that=this;

  function privateMethod() { .. }

  // public methods
  that.add = function(b) {
    return a + b;
  };
  that.avg = function(b) {
    return that.add(b) / 2; // calling another public method
  };
}

var x = new Foo(10);
alert(x.add(2)); // 12
alert(x.avg(20)); // 15

原因that是,this如果将方法作为事件处理程序,则可以绑定到其他内容,因此在实例化期间保存该值并稍后使用它.

编辑:这绝对不是最好的方式,只是一种简单的方式.我也在等待好的答案!



12> intrepidis..:

您可能希望使用折叠模式创建一个类型:

    // Here is the constructor section.
    var myType = function () {
        var N = {}, // Enclosed (private) members are here.
            X = this; // Exposed (public) members are here.

        (function ENCLOSED_FIELDS() {
            N.toggle = false;
            N.text = '';
        }());

        (function EXPOSED_FIELDS() {
            X.count = 0;
            X.numbers = [1, 2, 3];
        }());

        // The properties below have access to the enclosed fields.
        // Careful with functions exposed within the closure of the
        // constructor, each new instance will have it's own copy.
        (function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() {
            Object.defineProperty(X, 'toggle', {
                get: function () {
                    var before = N.toggle;
                    N.toggle = !N.toggle;
                    return before;
                }
            });

            Object.defineProperty(X, 'text', {
                get: function () {
                    return N.text;
                },
                set: function (value) {
                    N.text = value;
                }
            });
        }());
    };

    // Here is the prototype section.
    (function PROTOTYPE() {
        var P = myType.prototype;

        (function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() {
            Object.defineProperty(P, 'numberLength', {
                get: function () {
                    return this.numbers.length;
                }
            });
        }());

        (function EXPOSED_METHODS() {
            P.incrementNumbersByCount = function () {
                var i;
                for (i = 0; i < this.numbers.length; i++) {
                    this.numbers[i] += this.count;
                }
            };
            P.tweak = function () {
                if (this.toggle) {
                    this.count++;
                }
                this.text = 'tweaked';
            };
        }());
    }());

该代码将为您提供一个名为myType的类型.它将具有称为切换文本的内部私有字段.它也会有这些暴露的成员:字段数字 ; 属性toggle,textnumberLength ; 方法incrementNumbersByCounttweak.

折叠模式在这里详细说明: Javascript折叠模式

推荐阅读
爱唱歌的郭少文_
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有