Mic*_*tum 464 javascript
我想知道创建一个具有属性和方法的JavaScript对象的最佳方法是什么.
我已经看到了这个人在所有函数中使用var self = this然后使用的示例,self.以确保范围始终正确.
然后我看到了使用.prototype添加属性的示例,而其他人则使用内联方式.
有人可以给我一个具有一些属性和方法的JavaScript对象的正确示例吗?
bob*_*nce 880
在JavaScript中实现类和实例有两种模型:原型方法和闭包方式.两者都有优点和缺点,并且有很多扩展的变化.许多程序员和库具有不同的方法和类处理实用程序功能,可以用来描述该语言的一些较丑陋的部分.
结果是,在混合公司中,你会有一个混杂的元类,所有表现都略有不同.更糟糕的是,大多数JavaScript教程材料都很糟糕,并提供某种中间折衷以覆盖所有基础,让你非常困惑.(可能作者也很困惑.JavaScript的对象模型与大多数编程语言非常不同,并且在很多地方都是直接设计得很糟糕.)
让我们从原型方式开始吧.这是您可以获得的最多JavaScript本机:有最少的开销代码,instanceof将使用这种对象的实例.
function Shape(x, y) {
this.x= x;
this.y= y;
}
Run Code Online (Sandbox Code Playgroud)
我们可以new Shape通过将它们写入prototype此构造函数的查找来为创建的实例添加方法:
Shape.prototype.toString= function() {
return 'Shape at '+this.x+', '+this.y;
};
Run Code Online (Sandbox Code Playgroud)
现在将它子类化,尽可能多地调用JavaScript做的子类化.我们通过完全取代那种奇怪的魔法prototype属性来做到这一点:
function Circle(x, y, r) {
Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
this.r= r;
}
Circle.prototype= new Shape();
Run Code Online (Sandbox Code Playgroud)
在向其添加方法之前:
Circle.prototype.toString= function() {
return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}
Run Code Online (Sandbox Code Playgroud)
这个例子可以工作,你会在许多教程中看到类似的代码.但是男人,这new Shape()很难看:即使没有创建实际的Shape,我们也会实例化基类.它恰好在这个简单的情况下工作,因为JavaScript是如此草率:它允许传入零参数,在这种情况下x,y变成undefined并分配给原型this.x和this.y.如果构造函数正在做任何更复杂的事情,那么它的表面就会变得平坦.
所以我们需要做的是找到一种方法来创建一个原型对象,该对象包含我们在类级别所需的方法和其他成员,而无需调用基类的构造函数.为此,我们将不得不开始编写帮助程序代码.这是我所知道的最简单的方法:
function subclassOf(base) {
_subclassOf.prototype= base.prototype;
return new _subclassOf();
}
function _subclassOf() {};
Run Code Online (Sandbox Code Playgroud)
这会将其原型中的基类成员传递给新的构造函数,该函数不执行任何操作,然后使用该构造函数.现在我们可以写简单:
function Circle(x, y, r) {
Shape.call(this, x, y);
this.r= r;
}
Circle.prototype= subclassOf(Shape);
Run Code Online (Sandbox Code Playgroud)
而不是new Shape()错误.我们现在有一组可接受的原语用于构建类.
我们可以在此模型下考虑一些改进和扩展.例如,这里是一个语法糖版本:
Function.prototype.subclass= function(base) {
var c= Function.prototype.subclass.nonconstructor;
c.prototype= base.prototype;
this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};
...
function Circle(x, y, r) {
Shape.call(this, x, y);
this.r= r;
}
Circle.subclass(Shape);
Run Code Online (Sandbox Code Playgroud)
这两个版本都有缺点,即构造函数不能被继承,因为它在许多语言中都有.因此,即使您的子类没有为构造过程添加任何内容,它也必须记住使用基本所需的任何参数调用基础构造函数.这可以使用稍微自动化apply,但仍然需要写出来:
function Point() {
Shape.apply(this, arguments);
}
Point.subclass(Shape);
Run Code Online (Sandbox Code Playgroud)
因此,一个常见的扩展是将初始化内容分解为自己的函数而不是构造函数本身.这个函数可以从基数继承就好了:
function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
this.x= x;
this.y= y;
};
function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!
Run Code Online (Sandbox Code Playgroud)
现在我们为每个类提供了相同的构造函数样板.也许我们可以将它移动到它自己的辅助函数中,这样我们就不必继续输入它,例如代替它,转而Function.prototype.subclass将基类的函数吐出子类:
Function.prototype.makeSubclass= function() {
function Class() {
if ('_init' in this)
this._init.apply(this, arguments);
}
Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};
...
Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
this.x= x;
this.y= y;
};
Point= Shape.makeSubclass();
Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
Shape.prototype._init.call(this, x, y);
this.r= r;
};
Run Code Online (Sandbox Code Playgroud)
...它开始看起来更像其他语言,虽然语法略显笨拙.如果您愿意,可以添加一些额外的功能.也许您想要makeSubclass记住一个类名并toString使用它提供默认值.也许你想让构造函数检测到它在没有new操作符的情况下被意外调用(否则通常会导致非常烦人的调试):
Function.prototype.makeSubclass= function() {
function Class() {
if (!(this instanceof Class))
throw('Constructor called without "new"');
...
Run Code Online (Sandbox Code Playgroud)
也许你想要传递所有新成员并将它们makeSubclass添加到原型中,以节省你必须写得Class.prototype...非常多.很多类系统都这样做,例如:
Circle= Shape.makeSubclass({
_init: function(x, y, z) {
Shape.prototype._init.call(this, x, y);
this.r= r;
},
...
});
Run Code Online (Sandbox Code Playgroud)
在对象系统中你可能会考虑许多潜在的功能,而且没有人真正同意一个特定的公式.
在封闭的方式,然后.这可以避免JavaScript基于原型的继承问题,完全不使用继承.代替:
function Shape(x, y) {
var that= this;
this.x= x;
this.y= y;
this.toString= function() {
return 'Shape at '+that.x+', '+that.y;
};
}
function Circle(x, y, r) {
var that= this;
Shape.call(this, x, y);
this.r= r;
var _baseToString= this.toString;
this.toString= function() {
return 'Circular '+_baseToString(that)+' with radius '+that.r;
};
};
var mycircle= new Circle();
Run Code Online (Sandbox Code Playgroud)
现在,每个单独的实例Shape都有自己的toString方法副本(以及我们添加的任何其他方法或其他类成员).
每个实例都有自己的每个类成员副本的坏处是它的效率较低.如果您正在处理大量的子类实例,原型继承可能会更好地为您服务.同样调用基类的方法有点烦人,你可以看到:我们必须记住在子类构造函数覆盖它之前该方法是什么,否则它会丢失.
[也因为这里没有继承,instanceof操作员不会工作; 如果需要,你必须提供自己的类嗅探机制.虽然你可以用与原型继承类似的方式来调整原型对象,但它有点棘手,并且只是为了instanceof工作而不值得.]
每个实例都有自己的方法的好处是该方法可以绑定到拥有它的特定实例.这很有用,因为JavaScript this在方法调用中使用了奇怪的绑定方式,如果从方法调用中分离出一个方法,那么结果是:
var ts= mycircle.toString;
alert(ts());
Run Code Online (Sandbox Code Playgroud)
然后this在方法内部将不会像预期的那样是Circle实例(它实际上是全局window对象,导致广泛的调试祸害).在现实中,当一个方法被取出并分配给此通常发生setTimeout,onclick或EventListener一般.
使用原型方法,您必须为每个此类任务包含一个闭包:
setTimeout(function() {
mycircle.move(1, 1);
}, 1000);
Run Code Online (Sandbox Code Playgroud)
或者,在将来(或现在,如果您破解Function.prototype),您也可以使用function.bind():
setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);
Run Code Online (Sandbox Code Playgroud)
如果你的实例是以闭包的方式完成的,那么绑定是通过实例变量的闭包来完成的(通常称为that或者self,虽然我个人会建议反对后者,因为self在JavaScript中已经有了另一个不同的含义).你没有免费获得1, 1上述代码片段中的参数,所以你仍然需要另一个闭包或者bind()你需要这样做.
闭包方法也有很多变种.您可能更喜欢this完全省略,创建一个新的that并返回它而不是使用new运算符:
function Shape(x, y) {
var that= {};
that.x= x;
that.y= y;
that.toString= function() {
return 'Shape at '+that.x+', '+that.y;
};
return that;
}
function Circle(x, y, r) {
var that= Shape(x, y);
that.r= r;
var _baseToString= that.toString;
that.toString= function() {
return 'Circular '+_baseToString(that)+' with radius '+r;
};
return that;
};
var mycircle= Circle(); // you can include `new` if you want but it won't do anything
Run Code Online (Sandbox Code Playgroud)
哪种方式"合适"?都.哪个是"最好的"?这取决于你的情况.FWIW当我做强OO的东西时,我倾向于为真正的JavaScript继承进行原型设计,以及简单的一次性页面效果的闭包.
但对于大多数程序员来说,这两种方式都是非常直观的.两者都有许多潜在的混乱变化.如果您使用其他人的代码/库,您将同时遇到这两种(以及许多中间和一般破坏的方案).没有一个普遍接受的答案.欢迎来到JavaScript对象的精彩世界.
[这是为什么JavaScript不是我最喜欢的编程语言的第94部分.]
ShZ*_*ShZ 90
我经常使用这种模式 - 我发现它在我需要它时给了我非常大的灵活性.在使用中它与Java风格的类非常相似.
var Foo = function()
{
var privateStaticMethod = function() {};
var privateStaticVariable = "foo";
var constructor = function Foo(foo, bar)
{
var privateMethod = function() {};
this.publicMethod = function() {};
};
constructor.publicStaticMethod = function() {};
return constructor;
}();
Run Code Online (Sandbox Code Playgroud)
这使用在创建时调用的匿名函数,返回新的构造函数.因为匿名函数只被调用一次,所以可以在其中创建私有静态变量(它们位于闭包内部,对类的其他成员可见).构造函数基本上是一个标准的Javascript对象 - 您在其中定义私有属性,并将公共属性附加到this变量.
基本上,这种方法将Crockfordian方法与标准Javascript对象相结合,以创建更强大的类.
您可以像使用任何其他Javascript对象一样使用它:
Foo.publicStaticMethod(); //calling a static method
var test = new Foo(); //instantiation
test.publicMethod(); //calling a method
Run Code Online (Sandbox Code Playgroud)
Die*_*ino 25
Douglas Crockford在"好的部分"中广泛讨论了这个话题.他建议避免使用new运算符来创建新对象.相反,他建议创建自定义构造函数.例如:
var mammal = function (spec) {
var that = {};
that.get_name = function ( ) {
return spec.name;
};
that.says = function ( ) {
return spec.saying || '';
};
return that;
};
var myMammal = mammal({name: 'Herb'});
Run Code Online (Sandbox Code Playgroud)
在Javascript中,函数是一个对象,可以用于与new运算符一起构造对象.按照惯例,旨在用作构造函数的函数以大写字母开头.你经常看到这样的事情:
function Person() {
this.name = "John";
return this;
}
var person = new Person();
alert("name: " + person.name);**
Run Code Online (Sandbox Code Playgroud)
如果你在实例化一个新对象时忘记使用new运算符,你得到的是一个普通的函数调用,它被绑定到全局对象而不是新对象.
Nea*_*eal 13
继续关闭bobince的答案
在es6中,您现在可以实际创建一个 class
所以现在你可以这样做:
class Shape {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `Shape at ${this.x}, ${this.y}`;
}
}
Run Code Online (Sandbox Code Playgroud)
因此,您可以执行以下操作:(如在另一个答案中):
class Circle extends Shape {
constructor(x, y, r) {
super(x, y);
this.r = r;
}
toString() {
let shapeString = super.toString();
return `Circular ${shapeString} with radius ${this.r}`;
}
}
Run Code Online (Sandbox Code Playgroud)
在es6中结束了一点清洁,更容易阅读.
这是一个很好的例子:
class Shape {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `Shape at ${this.x}, ${this.y}`;
}
}
class Circle extends Shape {
constructor(x, y, r) {
super(x, y);
this.r = r;
}
toString() {
let shapeString = super.toString();
return `Circular ${shapeString} with radius ${this.r}`;
}
}
let c = new Circle(1, 2, 4);
console.log('' + c, c);Run Code Online (Sandbox Code Playgroud)
你也可以这样做,使用结构:
function createCounter () {
var count = 0;
return {
increaseBy: function(nb) {
count += nb;
},
reset: function {
count = 0;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后 :
var counter1 = createCounter();
counter1.increaseBy(4);
Run Code Online (Sandbox Code Playgroud)
另一种方式是http://jsfiddle.net/nnUY4/ (我不知道这种处理对象创建和显示功能是否遵循任何特定模式)
// Build-Reveal
var person={
create:function(_name){ // 'constructor'
// prevents direct instantiation
// but no inheritance
return (function() {
var name=_name||"defaultname"; // private variable
// [some private functions]
function getName(){
return name;
}
function setName(_name){
name=_name;
}
return { // revealed functions
getName:getName,
setName:setName
}
})();
}
}
// … no (instantiated) person so far …
var p=person.create(); // name will be set to 'defaultname'
p.setName("adam"); // and overwritten
var p2=person.create("eva"); // or provide 'constructor parameters'
alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"
Run Code Online (Sandbox Code Playgroud)
在 JavaScript 中创建对象的最简单方法是使用以下语法:
var test = {
a : 5,
b : 10,
f : function(c) {
return this.a + this.b + c;
}
}
console.log(test);
console.log(test.f(3));Run Code Online (Sandbox Code Playgroud)
这非常适合以结构化方式存储数据。
然而,对于更复杂的用例,通常最好创建函数实例:
function Test(a, b) {
this.a = a;
this.b = b;
this.f = function(c) {
return this.a + this.b + c;
};
}
var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));Run Code Online (Sandbox Code Playgroud)
这允许您创建共享相同“蓝图”的多个对象,类似于在例如中使用类的方式。爪哇。
然而,通过使用原型,这仍然可以更有效地完成。
只要函数的不同实例共享相同的方法或属性,您就可以将它们移动到该对象的原型。这样,函数的每个实例都可以访问该方法或属性,但不需要为每个实例重复它。
在我们的例子中,将方法移至f原型是有意义的:
function Test(a, b) {
this.a = a;
this.b = b;
}
Test.prototype.f = function(c) {
return this.a + this.b + c;
};
var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));Run Code Online (Sandbox Code Playgroud)
在 JavaScript 中进行继承的一种简单而有效的方法是使用以下两行代码:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
Run Code Online (Sandbox Code Playgroud)
这类似于这样做:
B.prototype = new A();
Run Code Online (Sandbox Code Playgroud)
两者的主要区别在于A使用时不运行的构造函数Object.create,这更直观,更类似于基于类的继承。
A在创建 的新实例时,您始终可以选择运行 的构造函数B,方法是将其添加到 的构造函数中B:
function B(arg1, arg2) {
A(arg1, arg2); // This is optional
}
Run Code Online (Sandbox Code Playgroud)
如果你想传递Bto的所有参数A,你也可以使用Function.prototype.apply():
function B() {
A.apply(this, arguments); // This is optional
}
Run Code Online (Sandbox Code Playgroud)
如果你想将另一个对象混合到 的构造函数链中B,你可以Object.create与结合使用Object.assign:
B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;
Run Code Online (Sandbox Code Playgroud)
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
Run Code Online (Sandbox Code Playgroud)
Object.create可以在所有现代浏览器中安全使用,包括 IE9+。Object.assign不适用于任何版本的 IE 或某些移动浏览器。建议使用polyfill Object.create和/或Object.assign如果您想使用它们并支持未实现它们的浏览器。
Object.create 您可以在此处找到一个 Polyfill ,在Object.assign 此处找到
一个。
| 归档时间: |
|
| 查看次数: |
132833 次 |
| 最近记录: |