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

JavaScript

Tony斯丁

2020-03-13

我不知道最好的方法是创建具有属性和方法的JavaScript对象。

我看过一些示例,该示例中的人员使用var self = this然后self.在所有功能中使用以确保范围始终正确。

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

有人可以给我一个带有某些属性和方法的JavaScript对象的正确示例吗?

第1373篇《如何在JavaScript中“正确”创建自定义对象?》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

7个回答
西门村村古一 2020.03.13

创建一个对象

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

var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

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

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

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

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。

但是,仍然可以通过使用原型来更高效地完成此操作。

每当函数的不同实例共享相同的方法或属性时,就可以将它们移至该对象的原型。这样,函数的每个实例都可以访问该方法或属性,但是不必为每个实例都复制它。

In our case, it makes sense to move the method f to the prototype :

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));

Inheritance

A simple but effective way to do inheritance in JavaScript, is to use the following two-liner :

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

That is similar to doing this :

B.prototype = new A();

The main difference between both is that the constructor of A is not run when using Object.create, which is more intuitive and more similar to class based inheritance.

You can always choose to optionally run the constructor of A when creating a new instance of B by adding adding it to the constructor of B :

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

If you want to pass all arguments of B to A, you can also use Function.prototype.apply() :

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

If you want to mixin another object into the constructor chain of B, you can combine Object.create with Object.assign :

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

Demo

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());


Note

Object.create可以在包括IE9 +在内的所有现代浏览器中安全使用。Object.assign不适用于任何版本的IE或某些移动浏览器。建议使用polyfill Object.create和/或Object.assign如果要使用它们并支持未实现它们的浏览器。

你可以找到一个填充工具对Object.create 这里 和一个Object.assign 在这里

路易理查德 2020.03.13

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

这是关闭的示例:

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

前一段时间,我碰到了Mozilla关于Closure的文章。这就是我的看法:“闭包可让您将某些数据(环境)与对该数据进行操作的函数相关联。这与面向对象编程(其中对象允许我们将某些数据(对象的属性)相关联”具有明显的相似之处)使用一种或多种方法 ”。这是我第一次阅读闭包与经典OOP之间的并行性,而没有引用原型。

怎么样?

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

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方法可以通过闭包对其进行操作现在,不使用类/构造函数,而是将VAT作为参数传递给函数。因为您唯一感兴趣的是计算本身,所以返回一个新函数,即calculate方法:

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/zh-CN/docs/Web/JavaScript/Guide/Closures

路易伽罗 2020.03.13

除了2009年接受的答案。如果您可以针对现代浏览器,则可以使用Object.defineProperty

Object.defineProperty()方法直接在对象上定义新属性,或修改对象上的现有属性,然后返回对象。资料来源:Mozilla

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移动版都支持它。

Mandy小卤蛋凯 2020.03.13

当在构造函数调用过程中使用关闭“ this”的技巧时,是为了编写一个函数,该函数可以被其他不想在该对象上调用方法的对象用作回调。它与“使范围正确”无关。

这是一个普通的JavaScript对象:

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);
};

您可能会从阅读道格拉斯·克罗克福德(Douglas Crockford)关于JavaScript的内容中学很多约翰·雷西格John Resig)也很聪明。祝好运!

小小小宇宙 2020.03.13

继续bobince的答案

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

现在,您可以执行以下操作:

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}`;
    }
}

最终在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);

卡卡西理查德 2020.03.13

您也可以使用结构以这种方式进行操作:

function createCounter () {
    var count = 0;

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

然后 :

var counter1 = createCounter();
counter1.increaseBy(4);
西门达蒙 2020.03.13

我相当频繁地使用此模式-我发现它在需要时为我提供了极大的灵活性。在使用中,它非常类似于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;
}();

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

基本上,这种方法将Crockfordian方法与标准Javascript对象结合在一起,以创建功能更强大的类。

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

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

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android