我首先要说的是,我理解JavaScript是一种无类语言。我的背景是Java,C ++和Objective-C,它们都是支持Classes的经典OOP语言。
我正在扩展到Web开发,并且一直在尝试使用JavaScript并学习它的模式。现在我正在使用在JavaScript中模拟类的构造函数模式。
所以这是我的“练习”课程:
function Car( model, year, miles ) {
this.model = model;
this.year = year;
this.miles = miles;
var privateVarTest = 10;
this.getPrivateVarTest = function() {
return privateVarTest;
}
this.setPrivateVarTest = function( value ) {
privateVarTest = value;
}
}
Car.prototype.toString = function() {
return this.model + " is a " + this.year + " model and has " +
this.miles + " miles.";
}
var myCar = new Car( "Ford Focus", "2006", "65,000" );
document.getElementById('notepad').innerHTML += '</br> Testing </br>';
document.getElementById('notepad').innerHTML += myCar.toString() + '</br>';
document.getElementById('notepad').innerHTML += myCar.model + '</br>';
document.getElementById('notepad').innerHTML += myCar.getPrivateVarTest() + '</br>';
myCar.setPrivateVarTest( 20 );
document.getElementById('notepad').innerHTML += myCar.getPrivateVarTest() + '</br>';
现在我喜欢使用prototype
定义函数的方式,因为它没有为每个创建的Car
对象实例化一个新版本的函数。但是,在经典的OOP语言中,我们使用变量private
和创建public
函数/方法来根据需要设置和获取这些变量。
JavaScript是Classless,没有private
或public
关键字用于此用途,所以我想我会尝试一种“伪造”private
变量的方法,那就是当发现使用var
而不是this
必要时,它会在constructor
之外无法访问,但我能够定义允许我使用的getter和setter。
现在最后我的问题,抱歉长时间的结束。对于经验丰富的JavaScript程序员的最佳实践,您是否会使所有变量private
遵循其他OOP语言的标准,并设置getter和setter(不能为每个对象强制创建原型),或者将它们作为尽管this
关键字基本上可以让您获得并设置所需的所有内容,并且只使用private
对该类所需的一些内部数据进行硬编码,这样做会尽可能多吗?
感谢您花时间阅读本文并提供讨论,我真的只是想了解经验丰富的Web开发人员所使用的最佳实践标准。
答案 0 :(得分:21)
一般OOP
我在营地里,无论你用什么语言编写代码,吸气者和制定者在很大程度上都是毫无意义和愚蠢的。
在大多数情况下,暴露的属性应该是罕见的,因为对象的任何属性通常应该在对象的域内,因此只有对象实际上应该更改自己的内部作为其他操作的副作用,而不是因为其他一些对象直接告诉它要改变一些东西。有一些例外情况我确定(总是存在),但我不记得上一次我需要制作一个。
此外,当公开属性时,使用方法公开的唯一原因是由于语言限制(Java)或者因为某些更改或通知必须在更改时发生那个财产。只是简单介绍Java-bean风格的方法,除了实际改变或返回属性之外什么都不做,这对保留封装没有任何帮助。如果可以的话,你也可以将财产公之于众。
但是,想要从各地获取/设置所有内容的真正问题在于,您基本上只是编写了链式过程代码并称之为OOP。你仍然有很长的一系列事物,只能用一个接一个的事情来推理。使用OOP,我们的想法是避免长缠绕的意大利面链,这样您就可以从更大的结构角度更多地查看您的体系结构,这些结构在关键点拥有特定的域。如果没有这个,你可能通过至少在命名空间下对你的功能进行分类来减少意大利面,这样你就可以更容易地知道在哪里寻找东西,但是你并没有真正利用OOP的关键胜利可以提供您的架构。
私有或JS本地构造函数/工厂关闭变量的真正价值在于信号意图。如果它暴露出来,外在的东西真的应该改变它。如果不是,那么您已经明确表示var只是对象的业务。
JS OOP
我的建议是忘记JS中的类模拟。这完全没必要。一旦您了解原型,原型就会变得优雅和简单。将构造函数的原型属性视为一种备份对象。如果在一个它没有的实例上调用方法,则下一步是检查实例的构造函数的原型对象属性。如果该对象没有它,那么它的构造函数的原型对象会被检查,依此类推,直到你最终到达核心Object构造函数的原型。
由于该查找过程,您可以动态地将新方法添加到构造函数中并使所有实例&#34;继承&#34;它们已经建成之后,但它并不是真正的继承,而是一个后备过程。
JS中的继承很简单。但这并不意味着你应该做大量的事情。级联继承的长链被认为是任何语言中的反模式有充分理由,并且由于回调过程的工作方式,如果你通过类似的18级攻击对象,它也可以真正地杀死性能。 JS中每个小东西的原型。我想说复合对象更适合继承,当继承似乎是一个更明智的选择时,每当你想通过链中超过2-3个原型链接继承时,请检查自己。
哦,有一个JS需要注意构造函数中的本地实例变量作为私有属性:这实际上只是JS在函数作用域上下文中的闭包规则。在原型中或在构造函数外部声明的方法无法访问这些内部变量。使用new关键字调用的构造函数会更改&#39; this&#39;的规则。访问,他们留下一个实例,但以其他方式执行JS函数。
JS OOP中其他疯狂但又疯狂的值得理解的是apply,call和now绑定方法。我倾向于将这些视为工厂中你想要的东西,但它们非常强大。
一旦你掌握了JS OOP,从功能角度开始理解JS,你会发现它有一个非常强大的1-2拳击组合。我们可以使用JS中的最少代码轻松完成任何事情。设计权衡是性能(现代JIT编译器处理得非常好)并且它为您提供了充足的绳索。我更喜欢绳子。自我私刑并不好玩,但这是学习/开发更好的直觉过程的一部分,结果发生得更快,并且从长远来看会导致更多可维护的代码。虽然Java基本上强制执行OOP,但是由于对开发人员做愚蠢的事情而言过于保护主义,导致社区广泛采用与OOP的全部要点完全相反的做法。
简短版本:
更新
es6 class关键字几乎没有改变JS中的OOP。这是100%的语法糖。 IMO,使用&#34; class&#34;并没有给新手任何好处,但JS中的所有三种对象构造函数/创建和对象实例化都有优点/缺点,并且它们都值得了解/理解。这三种方法是构造函数,Object.create,现在是class关键字。
答案 1 :(得分:1)
我们需要意识到我们倾向于希望我们学习的每种新语言都与我们学到的最后一种语言完全相同。或者第一个。等等。道格拉斯·克罗克福德有一个great (albeit a bit dated) google talk,他沉思,#34; Javascript是我所知道的唯一一种人们认为在使用它之前不需要学习的语言&#34;。那个演讲将回答你从未认识过的很多问题,包括你在这里问过的问题。
编写setter和getter并没有错。在工作中很少伤害到保持自己的理智。你将碰巧有一个C调的口音&#39;在讲JS时,至少你会清楚地意识到任何读你代码的人。
我的理智保存提示,用于管理&#39;这个&#39;跨越范围,始终记住,您可以保存当前的&#39;这个&#39;在进入新的背景之前:
var self = this;
我避免使用原型,除非在特殊情况下通过在声明范围内包含我的对象方法。
function MyClass(_arg){
var self = this;
this.arg = _arg;
this.returnArg = function(){
return self.arg;
}
}
var foo = new MyClass("bar");
foo.returnArg(); //"bar"
答案 2 :(得分:0)
我知道这是一篇旧帖子,但无论如何我会发布此回复,以防有人在搜索JS中的私人与公共和getter / setter时发现它。
是的,您可以轻松创建公共属性和私有属性,并使用getter和setter。我鼓励它帮助安全地模块化您的代码。这是一个有效的基本模式。 (继承是另一个故事,我建议完全避免使用经典继承,而是使用JS中非常容易的mixins。)
function MyClass(opts)
{
// enforce instantiation
if (this.constructor != MyClass) return new MyClass(opts);
// everything here is private, even if you use 'this'
let words = 'hello world';
let length = 100;
function talk() {
console.log(words);
}
// this is still private (but there's no need to do this)
this.secret = function() {
console.log("can't get me!");
}
// 'self' can be used to provide access to the desired 'this' in the
// return object (again, there's no need to do this)
let self = this;
// expose your public API
return {
get length() {
return length;
},
set length(len) {
length = sanity_check(len);
},
talk
}
}
这个类的新对象将如下所示→{length:[Getter / Setter],talk:[Function:talk]}
这种事情在JavaScript中一直是可能的。理解闭包在语言中的工作方式非常重要。
可悲的是,不是鼓励人们掌握关闭,而是现在已经添加了像Class,.constructor这样的东西,扩展到语言,然后遇到私有与公共,多重继承等问题。非常傻。它没有必要,只是使语言膨胀/复杂化。答案 3 :(得分:0)
如果是OOP,我必须说实际上javascript提供了一定程度的oop。
我的意思是OOP设计的4个主要概念可以在javascript中实现,尽管它不是很强大并且在Java或C ++中定义得很好。让我们检查这些概念,我将尝试为每个概念提供一个示例。
1-抽象:正如我之前所说,我们可以理解为什么OOP在Java中没有很好地定义,在Java中我们使用Classes,Variables,interfaced实现抽象概念......但是在javascript中,与其他OOP语言(如Java)相比,抽象是相当隐式定义的。
2-封装:我想一个例子就足够了
function Student (stdName, stdEmail, stdAvg) {
this.name = theName;
this.email = theEmail;
this.avg = stdAvg;
}
在这里你也看到我们定义了一个&#34;类&#34;就像使用函数的概念一样,如果得到类型学生,你会看到它是一个函数。
3,4 - 继承和多态: JavaScript实现继承和多态的方式与Java或C ++不同,因为它的原型(说实话我不知道任何其他方式)方法。
const Gun = function(soundEffect){
this.soundEffect = soundEffect;
};
Gun.prototype.fire = function(){
console.log(this.soundEffect);
};
const DesertEagle = function(color,clipSize){
this.color = color;
this.clipSize = clipSize;
};
DesertEagle.prototype = new Gun("pew pew peeeew");
const myWeapon = new DesertEagle("black",15);
myWeapon.fire();
现在为了覆盖变量和函数的公共/私有访问,我们必须使用某种技术来实现这样的概念。检查以下代码:
const Student = function(name, stdNumber, avg){
this.name = name;
this.stdNumber = stdNumber;
this.avg = avg;
var that = this; //NOTE : we need to store a reference to "this" in order for further calls to private members
this.publicAccess = { // a set of functions and variables that we want as public
describe: function () {
console.log(that.name + " : " + that.stdNumber);
},
avg: this.avg,
};
return this.publicAccess; // return set of public access members
};
const newStd = new Student("john", "123", "3.4");
newStd.describe();
// output: john : 123
console.log(newStd.avg)
// output: 3.4
在ES6中定义一个类很容易,但它只是语法糖,它仍然是它的核心。
我希望它会有所帮助。 我还建议您阅读本文(Javascript design patterns),它将提供有关avascript功能和设计模式的一些有用信息。
请接受我对我英语不好的道歉。