如何在JavaScript中“正确”创建自定义对象?

How to “properly” create a custom object in JavaScript?

我想知道创建一个具有属性和方法的javascript对象的最佳方法是什么。

我看到过一些例子,人们使用var self = this,然后在所有函数中使用self.,以确保范围始终正确。

然后,我看到了使用.prototype添加属性的示例,而其他的则是内联的。

有人能给我一个有一些属性和方法的javascript对象的适当例子吗?


在JavaScript中有两种实现类和实例的模型:原型方法和关闭方法。两者都有优点和缺点,并且有许多扩展的变体。许多程序员和库有不同的方法和类处理实用程序函数,可以覆盖语言中一些更糟糕的部分。好的。

其结果是,在混合公司中,您将看到元类的混乱,所有的行为都略有不同。更糟糕的是,大多数的javascript教程材料都很糟糕,并且提供了某种中间折衷来覆盖所有的基础,这让您非常困惑。(可能作者也很困惑。Javascript的对象模型与大多数编程语言非常不同,而且在许多地方设计得很糟糕。)好的。

让我们从原型的方式开始。这是您能得到的最本机的javascript:开销代码最少,instanceof将用于此类对象的实例。好的。

1
2
3
4
function Shape(x, y) {
    this.x= x;
    this.y= y;
}

我们可以通过将方法写入该构造函数函数的prototype查找中,向new Shape创建的实例添加方法:好的。

1
2
3
Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

现在要对它进行子类化,尽可能多地调用javascript所做的子类化。我们完全取代了神奇的prototype属性:好的。

1
2
3
4
5
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();

在向其中添加方法之前:好的。

1
2
3
Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

这个示例可以工作,您将在许多教程中看到类似的代码。但是,老兄,那个new Shape()是丑陋的:我们正在实例化基类,即使不需要创建实际的形状。在这种简单的情况下,它正好可以工作,因为javascript非常草率:它允许传入零参数,在这种情况下,xy成为undefined,并分配给原型的this.xthis.y。如果构造器函数做了更复杂的事情,它将平躺在脸上。好的。

所以我们需要做的是找到一种方法来创建一个原型对象,它包含我们在类级别上想要的方法和其他成员,而不调用基类的构造函数。要做到这一点,我们必须开始编写助手代码。这是我所知道的最简单的方法:好的。

1
2
3
4
5
function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

这会将原型中的基类成员转移到一个不执行任何操作的新构造函数函数,然后使用该构造函数。现在我们可以简单地写:好的。

1
2
3
4
5
function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

而不是new Shape()的错误。我们现在有了一组可接受的基本体来构建类。好的。

在这个模型下,我们可以考虑一些改进和扩展。例如,这里有一个语法糖版本:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);

任何一个版本都有一个缺点,即构造函数函数不能继承,因为它在许多语言中都是如此。因此,即使您的子类没有向构造过程中添加任何内容,它也必须记住使用基需要的任何参数调用基构造函数。这可以使用apply稍微自动化,但您仍然需要写出:好的。

1
2
3
4
function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

所以一个常见的扩展是将初始化内容分解成它自己的函数,而不是构造函数本身。然后,此函数可以从基继承,这很好:好的。

1
2
3
4
5
6
7
8
9
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!

现在我们已经为每个类提供了相同的构造函数函数样板。也许我们可以把它移到它自己的助手函数中,这样我们就不必不断地输入它,例如,不必输入Function.prototype.subclass,而是将它转过来,让基类的函数吐出子类:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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;
};

…这看起来有点像其他语言,尽管语法有点笨拙。如果你愿意的话,你可以在一些额外的功能上撒点水。也许您希望makeSubclass接受并记住一个类名,并使用它提供一个默认的toString。也许您想让构造函数在不使用new运算符的情况下意外调用时进行检测(否则通常会导致非常烦人的调试):好的。

1
2
3
4
5
Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without"new"');
        ...

也许你想把所有的新成员都传进来,让makeSubclass把他们添加到原型中,这样你就不用写太多Class.prototype...。很多类系统都这样做,例如:好的。

1
2
3
4
5
6
7
Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

在一个对象系统中,您可能认为有许多潜在的特性是可取的,没有人真正同意一个特定的公式。好的。

那么,封闭的方式。这样就避免了javascript基于原型继承的问题,完全不使用继承。而是:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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();

现在,Shape的每个实例都有自己的toString方法副本(以及我们添加的任何其他方法或其他类成员)。好的。

每一个拥有自己的类成员副本的实例的坏处在于它的效率较低。如果您处理大量的子类实例,原型继承可能会更好地为您服务。同样,调用基类的方法也有点烦人,如您所见:在子类构造函数重写它之前,我们必须记住该方法是什么,否则它会丢失。好的。

[另外,因为这里没有继承,所以instanceof操作符将不起作用;如果需要,您必须提供自己的类嗅探机制。虽然您可以像处理原型继承一样处理原型对象,但这有点棘手,不值得只使用instanceof工作。]好的。

每个拥有自己方法的实例的好处是,该方法随后可能绑定到拥有它的特定实例。这是很有用的,因为javascript在方法调用中以奇怪的方式绑定this,结果是,如果从方法的所有者分离方法:好的。

1
2
var ts= mycircle.toString;
alert(ts());

那么方法内部的this就不会像预期的那样是循环实例(它实际上是全局window对象,造成了广泛的调试麻烦)。实际上,这种情况通常发生在采用方法并将其分配给setTimeoutonclickEventListener时。好的。

使用原型方法,您必须为每个这样的任务包括一个闭包:好的。

1
2
3
setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

或者,在将来(或者现在,如果您对function.prototype进行了黑客攻击),您也可以使用function.bind()来完成它:好的。

1
setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

如果您的实例是以闭包的方式完成的,那么绑定是通过对实例变量(通常称为thatself的闭包免费完成的,尽管我个人建议不要使用后者,因为self在javascript中已有另一个不同的含义。不过,在上面的代码片段中,您没有免费获得参数1, 1,因此如果需要,您仍然需要另一个闭包或bind()。好的。

闭包方法也有很多变体。您可能更喜欢完全省略this,创建一个新的that并返回它,而不是使用new操作符:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

哪条路"合适"?两者都有。哪一个是"最好的"?这取决于你的情况。当我做大量的面向对象的工作时,我倾向于原型化以实现真正的JavaScript继承,并为简单的一次性页面效果提供闭包。好的。

但是对于大多数程序员来说,这两种方式都是非常违反直觉的。两者都有许多潜在的混乱变化。如果您使用其他人的代码/库,那么您将同时遇到这两种方案(以及许多中间方案和通常被破坏的方案)。没有一个公认的答案。欢迎来到Javascript对象的精彩世界。好的。

[这是为什么javascript不是我最喜欢的编程语言的第94部分。]好的。好啊。


我经常使用这个模式——我发现它在我需要的时候给了我很大的灵活性。在使用中,它与Java风格的类非常相似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}();

这将使用创建时调用的匿名函数,返回新的构造函数函数。因为匿名函数只被调用一次,所以可以在其中创建私有静态变量(它们位于闭包内部,对类的其他成员可见)。构造器函数基本上是一个标准的javascript对象—您在它内部定义私有属性,公共属性附加到this变量。

基本上,这种方法将Crockfordian方法与标准的JavaScript对象结合起来,创建了一个更强大的类。

您可以像使用任何其他javascript对象一样使用它:

1
2
3
Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method


道格拉斯·克罗克福德在好的部分广泛讨论了这个话题。他建议避免新操作员创建新对象。相反,他建议创建定制的构造函数。例如:

1
2
3
4
5
6
7
8
9
10
11
12
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'});

在JavaScript中,函数是一个对象,可以用来与新的操作符一起构造对象。按照惯例,用作构造函数的函数以大写字母开头。你经常看到这样的事情:

1
2
3
4
5
6
7
function Person() {
   this.name ="John";
   return this;
}

var person = new Person();
alert("name:" + person.name);**

如果在实例化新对象时忘记使用new运算符,则得到的是一个普通的函数调用,它绑定到全局对象而不是新对象。


继续回答Bobince的问题

在ES6中,您现在可以实际创建一个class

现在你可以做:

1
2
3
4
5
6
7
8
9
10
class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Shape at ${this.x}, ${this.y}`;
    }
}

因此,延伸到一个圆(如另一个答案中所示),您可以执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
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}`;
    }
}

最终在ES6中变得更干净,也更容易阅读。

下面是一个很好的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);


您也可以这样做,使用结构:

1
2
3
4
5
6
7
8
9
10
11
12
function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

然后:

1
2
var counter1 = createCounter();
counter1.increaseBy(4);


另一种方法是http://jsfiddle.net/nnuy4/(我不知道这种处理对象创建和显示功能是否遵循任何特定的模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 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"


Closure是多功能的。Bobince在创建对象时很好地总结了原型与闭合方法。但是,您可以使用函数式编程的闭包来模拟OOP的某些方面。记住函数是JavaScript中的对象;所以以不同的方式使用函数作为对象。

下面是一个闭包示例:

1
2
3
4
5
function outer(outerArg) {
    return inner(innerArg) {
        return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context
    }
}

不久前,我看到了Mozilla关于关闭的文章。这就是我所看到的:"一个闭包允许您将一些数据(环境)与操作该数据的函数相关联。这与面向对象编程有明显的相似之处,对象允许我们将一些数据(对象的属性)与一个或多个方法相关联"。这是我第一次读到闭包和经典OOP之间的并行性,没有参考原型。

怎么用?

假设您要计算某些项目的增值税。增值税在申请期间很可能保持稳定。在OOP(伪代码)中执行此操作的一种方法:

1
2
3
4
5
6
7
8
9
public class Calculator {
    public property VAT { get; private set; }
    public Calculator(int vat) {
        this.VAT = vat;
    }
    public int Calculate(int price) {
        return price * this.VAT;
    }
}

基本上,您将一个增值税值传递给构造函数,并且您的计算方法可以通过闭包对其进行操作。现在,不要使用类/构造函数,而是将VAT作为参数传递到函数中。因为您唯一感兴趣的是计算本身,所以返回一个新函数,即calculate方法:

1
2
3
4
5
6
7
8
function calculator(vat) {
    return function(item) {
        return item * vat;
    }
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110

在您的项目中,确定最适合计算增值税的顶级值。作为经验法则,无论何时传递相同的参数,都有一种方法可以使用闭包来改进它。无需创建传统对象。

https://developer.mozilla.org/en-us/docs/web/javascript/guide/closures


当一个人在构造函数调用期间使用关闭"this"的技巧时,其目的是编写一个函数,该函数可被不想在对象上调用方法的其他对象用作回调。这与"使范围正确"无关。

下面是一个普通的javascript对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
function MyThing(aParam) {
    var myPrivateVariable ="squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to" + myPrivateVariable +"!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};

你可能会从阅读道格拉斯·克罗克福德关于JavaScript的文章中得到很多收获。约翰·雷西格也很聪明。祝你好运!


创建对象

在javascript中创建对象的最简单方法是使用以下语法:

1
2
3
4
5
6
7
8
9
10
var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

console.log(test);
console.log(test.f(3));

这对于以结构化方式存储数据非常有用。

但是,对于更复杂的用例,最好创建函数实例:

1
2
3
4
5
6
7
8
9
10
11
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));

这允许您创建多个对象,这些对象共享相同的"蓝图",类似于如何在Java中使用类。

然而,使用原型仍然可以更有效地实现这一点。

只要函数的不同实例共享相同的方法或属性,就可以将它们移动到该对象的原型中。这样,函数的每个实例都可以访问该方法或属性,但不需要为每个实例复制该方法或属性。

在我们的例子中,将方法f移动到原型是有意义的:

1
2
3
4
5
6
7
8
9
10
11
12
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));

遗传

在javascript中进行继承的一个简单但有效的方法是使用以下两个行程序:

1
2
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

类似于这样做:

1
B.prototype = new A();

两者的主要区别在于,在使用Object.create时,A的构造函数不运行,更直观,更类似于基于类的继承。

创建B的新实例时,可以选择运行A的构造函数,方法是将其添加到B的构造函数中:

1
2
3
function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

如果你想把B的所有参数都传给A,你也可以使用Function.prototype.apply()

1
2
3
function B() {
    A.apply(this, arguments); // This is optional
}

如果要将另一个对象混合到B的构造函数链中,可以将Object.createObject.assign组合:

1
2
B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street ="Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year ="2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());

注释

Object.create可以安全地用于各种现代浏览器,包括ie9+。Object.assign不适用于任何版本的IE或某些移动浏览器。如果您想使用它们并支持不实现它们的浏览器,建议使用polyfill Object.create和/或Object.assign

您可以在这里找到一个用于Object.create的polyfill还有一个是给Object.assign的。


我想说的是,我们可以使用标题或字符串来声明一个对象。有不同的方法来调用每一种类型。见下文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var test = {

  useTitle :"Here we use 'a Title' to declare an Object",
  'useString':"Here we use 'a String' to declare an Object",
 
  onTitle : function() {
    return this.useTitle;
  },
 
  onString : function(type) {
    return this[type];
  }
 
}

console.log(test.onTitle());
console.log(test.onString('useString'));


很适合我的模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

首先,您可以更改向实例添加方法的首选项,而不是更改构造函数的prototype对象。我几乎总是在构造函数内部声明方法,因为我经常为了继承和修饰的目的而使用构造函数劫持。

以下是我如何决定哪些声明是书面的:

  • 从不直接在上下文对象(this上声明方法)
  • var声明优先于function声明。
  • 让原语优先于对象({}[])
  • public声明优先于private声明。
  • thusselfvmetc更喜欢Function.prototype.bind
  • 避免在另一个类中声明类,除非:
    • 很明显这两者是不可分割的
    • 内部类实现命令模式
    • 内部类实现了单例模式
    • 内部类实现状态模式
    • 内部类实现了另一个保证这一点的设计模式。
  • 总是从闭包空间的词法范围内返回this

这就是为什么这些帮助:

施工人员劫持

1
2
3
4
5
6
7
8
9
10
11
var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};

模型设计

1
2
3
4
5
6
7
8
9
10
11
12
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string ||"";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true

设计模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

如你所见,我真的不需要thus,因为我更喜欢Function.prototype.bind.call.apply,而不是thus。在我们的Singleton类中,我们甚至不把它命名为thus,因为INSTANCE传递了更多的信息。对于Model,我们返回this,这样我们就可以使用.call调用构造函数来返回我们传入的实例。冗余地,我们将它分配给变量updated,尽管它在其他场景中很有用。

除此之外,我更喜欢使用new关键字而不是括号来构造对象文本:

首选

1
2
3
4
5
6
7
var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);

不优选

1
2
3
var klass = Super.apply({
    override: x
});

如您所见,后者无法重写其超类的"override"属性。

如果我确实向类的prototype对象添加方法,我更喜欢对象文本——使用或不使用new关键字:

首选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};

不优选

1
Klass.prototype.method = function m() {...};


你也可以试试这个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    function Person(obj) {
    'use strict';
    if (typeof obj ==="undefined") {
        this.name ="Bob";
        this.age = 32;
        this.company ="Facebook";
    } else {
        this.name = obj.name;
        this.age = obj.age;
        this.company = obj.company;
    }

}

Person.prototype.print = function () {
    'use strict';
    console.log("Name:" + this.name +" Age :" + this.age +" Company :" + this.company);
};

var p1 = new Person({name:"Alex", age: 23, company:"Google"});
p1.print();

除了2009年的公认答案。如果你能瞄准现代浏览器,你就可以利用object.defineproperty。

The Object.defineProperty() method defines a new property directly on
an object, or modifies an existing property on an object, and returns
the object.
Source: Mozilla

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var Foo = (function () {
    function Foo() {
        this._bar = false;
    }
    Object.defineProperty(Foo.prototype,"bar", {
        get: function () {
            return this._bar;
        },
        set: function (theBar) {
            this._bar = theBar;
        },
        enumerable: true,
        configurable: true
    });
    Foo.prototype.toTest = function () {
        alert("my value is" + this.bar);
    };
    return Foo;
}());

// test instance
var test = new Foo();
test.bar = true;
test.toTest();

要查看桌面和移动兼容性列表,请参阅Mozilla的浏览器兼容性列表。是的,IE9+支持它以及Safari Mobile。


1
2
3
4
5
6
7
8
9
10
11
12
var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person('Adeola', 23, 'Web Developer');
myWorker.changeName('Timmy');

console.log("New Worker" + myWorker.lastname);


基本上,JS中没有类的概念,所以我们使用函数作为与现有设计模式相关的类构造函数。

1
2
3
4
5
6
7
8
9
//Constructor Pattern
function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.doSomething = function(){
    alert('I am Happy');
}
}

到目前为止,JS还不知道您想要创建一个对象,所以这里出现了新的关键字。

1
2
var person1 = new Person('Arv', 30, 'Software');
person1.name //Arv

参考:Web开发人员的专业JS-nik z