可重用的 javascript 对象、原型和范围

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16055046/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 03:05:39  来源:igfitidea点击:

reusable javascript objects, prototypes and scope

javascriptscopeprototype

提问by Max

MyGlobalObject;

function TheFunctionICanUseRightAwaySingleForAllInstansesAndWithoutInstanse() {
    function() {
        alert('NO CONSTRUCTOR WAS CALLED');
    }
};

The Long-named function must be callable from MyGlobalObject, which in turn must be available as a global (to window) variable in all times after script was loaded. It should support extensibility in accordance with latest standards.

长命名函数必须可从 调用MyGlobalObject,而在window脚本加载后,它必须始终作为全局 (to ) 变量可用。它应该支持符合最新标准的可扩展性。

I'm at architectural dilemma of how to built JS base for an application (almost 100% JS).

我在如何为应用程序构建 JS 基础(几乎 100% JS)的架构上陷入困境。

We need an object i.e. window.MyObject(like a module, like jQuery) so

我们需要一个对象,即window.MyObject(像一个模块,像 jQuery)所以

It can be created with

它可以用

VAR1

VAR1

 var MyGlobalObjConstructor = function(){
     this.GlobalFunctionInObject = function(){
        alert('called with MyGlobalObj.GlobalFunctionInObject()');
        }        
};
window.MyGlobalObj = new MyGlobalObjConstructor();    

Is MyGlobalObjextensible? Can I create child objects, which will inherit current state of MyGlobalObj(extended functions/properties MyGlobalObj.NewFunce.g.)? What is the main difference between using prototype (VAR3)?

MyGlobalObj可扩展的?我可以创建子对象,这些子对象将继承MyGlobalObjMyGlobalObj.NewFunc例如扩展功能/属性)的当前状态吗?使用原型(VAR3)的主要区别是什么?

By GlobaldFunctionI mean single instance for all initialized/instantiated (possibly instantializable) instances..

通过GlobaldFunction我的意思是所有的初始化/实例化(可能instantializable)情况下,单一实例..

Or with

或与

VAR2

VAR2

var MyGlobalObj = {
    GlobalFunctionInObject: function...
    GlobalFunctionInObject2: function...
};
MyGlobalObj.GlobalFunctionInObject();
// here I lose all hierarchy elements, no prototype, 
// can I use GlobalFunctionInObject2 in GlobalFunctionInObject?

Or with

或与

VAR3

VAR3

var MyGlobalConstuctor = function(){} // already 'well-formed' object
MyGlobalConstuctor.prototype.GlobalFunctionInObject = function...
};
var MyGlobalObj = new MyGlobalConstuctor();

// so I'm sceptical to NEW, because I have ALREADY wrote my functions 
// which I expect to be in memory, single instance of each of them, 
// so creating MyObject2,3,4 with NEW MyGC() makes no sense to me.
// DO I REALLY HAVE TO USE "MyGlobalConstuctor.prototype." FOR EACH FUNCTION?!!!!

What's the difference defining MyGlobalObjas a function and as an object (result of func or VAR2)?

定义MyGlobalObj为函数和对象(func 或 VAR2 的结果)有什么区别?

OR VAR4?

还是VAR4?

I see in Chrome Debugger both prototype and __proto__special fields. I've read that that's OK, but why are they not saved in a single prototype?

我在 Chrome Debugger 中看到原型和__proto__特殊字段。我已经读到那没问题,但为什么它们不保存在单个原型中?

So, what is the correct/optimal way to implement window.MyObject, so one could MyObject.MyFunction();What are the differences (pro/contra) of variants 1 2 and 3?

那么,什么是正确/最佳的实现方式window.MyObject,那么MyObject.MyFunction();变体 1 2 和 3有什么区别(赞成/反对)?

回答by A. Matías Quezada

Variation 1 - Mixin

变体 1 - Mixin

function SomeType() {
    var priv = "I'm private";
    this.publ = "I'm public";
    this.action = function() {
        return priv + this.publ;
    };
}

var obj = new SomeType();

With this method you are creating a new object every time you call new SomeType(), creating all its methods and adding all this method to the new object. Every time you create an object.

使用此方法,您每次调用 时都会创建一个新对象new SomeType(),创建其所有方法并将所有此方法添加到新对象中。每次创建对象时。

Pros

优点

  • It looks like classical inheritance so it's easy to understand to Java-C#-C++-etc people.
  • It can have private variables per instance since you have one function closureper each object you create
  • It allows multiple inheritance, also known as Twitter-mixins or functional mixins
  • obj instanceof SomeTypewill return true
  • 它看起来像经典继承,所以 Java-C#-C++ 等人很容易理解。
  • 每个实例可以有私有变量,因为每个创建的对象都有一个函数闭包
  • 它允许多重继承,也称为 Twitter 混合或功能混合
  • obj instanceof SomeType会返回真

Cons

缺点

  • It consumes more memory as more objects you create because with each object you are creating a new closure and creating each of it's methods again.
  • Private properties are private, not protected, subtypes can't access them
  • No easy way to know if a object has some Type as superclass.
  • 随着您创建的对象越多,它消耗的内存就越多,因为对于每个对象,您都在创建一个新的闭包并再次创建它的每个方法。
  • 私有属性是private,不是protected,子类型不能访问它们
  • 没有简单的方法可以知道对象是否具有某种类型作为超类。

Inheritance

遗产

function SubType() {
    SomeType.call(this);
    this.newMethod = function() {
        // can't access priv
        return this.publ;
    };
}

var child = new SubType();

child instanceof SomeTypewill return false there is no other way to know if child has SomeType methods than look if it has them one by one.

child instanceof SomeType将返回 false 没有其他方法可以知道 child 是否具有 SomeType 方法,而不是一一查看它们是否具有。

Variation 2 - Object literal with prototyping

变体 2 - 带有原型的对象字面量

var obj = {
    publ: "I'm public",
    _convention: "I'm public too, but please don't touch me!",
    someMethod: function() {
        return this.publ + this._convention;
    }
};

In this case you are creating a single object. If you are going to need only one instance of this type it can be the best solution.

在这种情况下,您正在创建单个对象。如果您只需要这种类型的一个实例,它可能是最好的解决方案。

Pros

优点

  • It's quick and easy to understand.
  • Performant
  • 它快速且易于理解。
  • 高性能

Cons

缺点

  • No privacy, every property is public.
  • 没有隐私,每个财产都是公开的。

Inheritance

遗产

You can inherit a object prototyping it.

您可以继承一个对象原型。

var child = Object.create(obj);
child.otherMethod = function() {
    return this._convention + this.publ;
};

If you are on a old browser you will need to garantee Object.createworks:

如果您使用的是旧浏览器,则需要保证Object.create工作正常:

if (!Object.create) {
    Object.create = function(obj) {
        function tmp() { }
        tmp.prototype = obj;
        return new tmp;
    };
}

To know if a object is a prototype of another you can use

要知道一个对象是否是另一个对象的原型,您可以使用

obj.isPrototypeOf(child); // true

Variation 3 - Constructor pattern

变体 3 - 构造函数模式

UPDATE: This is the pattern ES6 classes are sugar syntax of. If you use ES6 classes you are following this pattern under the hood.

更新:这是模式 ES6 类是. 如果您使用 ES6 类,则您在幕后遵循此模式。

class SomeType {
    constructor() {
        // REALLY important to declare every non-function property here
        this.publ = "I'm public";
        this._convention = "I'm public too, but please don't touch me!";
    }
    someMethod() {
        return this.publ + this._convention;
    }
}

class SubType extends SomeType {
    constructor() {
        super(/* parent constructor parameters here */);
        this.otherValue = 'Hi';
    }
    otherMethod() {
        return this._convention + this.publ + this.otherValue;
    }
}


function SomeType() {
    // REALLY important to declare every non-function property here
    this.publ = "I'm public";
    this._convention = "I'm public too, but please don't touch me!";
}

SomeType.prototype.someMethod = function() {
    return this.publ + this._convention;
};

var obj = new SomeType();

You can re-assign the prototype insteadd of adding each method if you are not inheriting and remember to re-assign the constructor property:

如果您没有继承并记住重新分配构造函数属性,则可以重新分配原型而不是添加每个方法:

SomeType.prototype = {
    constructor: SomeType,
    someMethod = function() {
        return this.publ + this._convention;
    }
};

Or use _.extend or $.extend if you have underscore or jquery in your page

或者,如果页面中有下划线或 jquery,请使用 _.extend 或 $.extend

_.extend(SomeType.prototype, {
    someMethod = function() {
        return this.publ + this._convention;
    }
};

The newkeyword under the hood simply does this:

new引擎盖下的关键字简单地做到这一点:

function doNew(Constructor) {
    var instance = Object.create(Constructor.prototype);
    instance.constructor();
    return instance;
}

var obj = doNew(SomeType);

What you have is a function than has no methods; it just has a prototypeproperty with a list of functions, the newoperator means to create a newobject and use this function's prototype (Object.create) and constructorproperty as initializer.

你拥有的是一个函数而不是没有方法;它只有一个prototype带有函数列表的属性,new操作符意味着创建一个对象并使用该函数的原型 ( Object.create) 和constructor属性作为初始值设定项。

Pros

优点

  • Performant
  • Prototype chain will allow you to know if a object inherits from some type
  • 高性能
  • 原型链将让你知道一个对象是否继承自某种类型

Cons

缺点

  • Two-step inheritance
  • 两步继承

Inheritance

遗产

function SubType() {
    // Step 1, exactly as Variation 1
    // This inherits the non-function properties
    SomeType.call(this);
    this.otherValue = 'Hi';
}

// Step 2, this inherits the methods
SubType.prototype = Object.create(SomeType.prototype);
SubType.prototype.otherMethod = function() {
    return this._convention + this.publ + this.otherValue;
};

var child = new SubType();

You may think it looks like a super-set of Variation 2... and you'll be right. It's like variation 2 but with a initializer function (the constructor);

你可能认为它看起来像是变奏 2 的超级集……你是对的。它就像变体 2,但有一个初始化函数(构造函数);

child instanceof SubTypeand child instanceof SomeTypewill return both true

child instanceof SubTypechild instanceof SomeType都将返回true

Curiosity:Under the hood instanceofoperator does is

好奇心:幕后instanceof操作员所做的是

function isInstanceOf(obj, Type) {
    return Type.prototype.isPrototypeOf(obj);
}

Variation 4 - Overwrite __proto__

变化 4 - 覆盖 __proto__

When you do Object.create(obj)under the hood it does

当你Object.create(obj)在引擎盖下做的时候

function fakeCreate(obj) {
    var child = {};
    child.__proto__ = obj;
    return child;
}

var child = fakeCreate(obj);

The __proto__property modifies directly the object's hidden [Prototype]property. As this can break JavaScript behaviour, it's not standard. And the standard way is preferred (Object.create).

__proto__属性直接修改对象的隐藏[Prototype]属性。由于这可能会破坏 JavaScript 行为,因此它不是标准的。首选标准方式(Object.create)。

Pros

优点

  • Quick and performant
  • 快速高效

Cons

缺点

  • Non-standard
  • Dangerous; you can't have a hashmap since the __proto__key can change the object's prototype
  • 非标
  • 危险的; 你不能有一个哈希图,因为__proto__键可以改变对象的原型

Inheritance

遗产

var child = { __proto__: obj };
obj.isPrototypeOf(child); // true

Comment questions

评论问题

1. var1: what happens in SomeType.call(this)? Is 'call' special function?

1. var1:在 SomeType.call(this) 中发生了什么?'call' 是特殊功能吗?

Oh, yes, functions are objects so they have methods, I will mention three: .call(), .apply()and .bind()

哦,是的,函数是对象,使他们有方法,我会提到三:.CALL() ,适用().bind()

When you use .call() on a function, you can pass one extra argument, the context, the value of thisinside the function, for example:

当你在一个函数上使用 .call() 时,你可以传递一个额外的参数,contextthis函数内部的值,例如:

var obj = {
    test: function(arg1, arg2) {
        console.log(this);
        console.log(arg1);
        console.log(arg2);
    }
};

// These two ways to invoke the function are equivalent

obj.test('hi', 'lol');

// If we call fn('hi', 'lol') it will receive "window" as "this" so we have to use call.
var fn = obj.test;
fn.call(obj, 'hi', 'lol');

So when we do SomeType.call(this)we are passing the object thisto function SomeCall, as you remember this function will add methods to object this.

因此,当我们这样做时,我们SomeType.call(this)将对象传递this给函数SomeCall,正如您所记得的,该函数将向对象添加方法this

2. var3: With your "REALLY define properties" do you mean if I use them in functions? Is it a convention? Because getting this.newProperty without it being defined at the same level with other member functions is not a problem.

2. var3:你的“真正定义属性”是指我在函数中使用它们吗?是公约吗?因为获取 this.newProperty 而不与其他成员函数在同一级别定义它不是问题。

I mean any property your object will have that is not a function must be defined on the constructor, not on the prototype, otherwise you will face one of the more confusing JS problems. You can see it here, but it's outside of the focus of this question.

我的意思是你的对象将拥有的任何属性不是函数必须在构造函数上定义,而不是在原型上定义,否则你将面临一个更令人困惑的 JS 问题。你可以在这里看到它,但它超出了这个问题的重点。

3. Var3: what happens if I don't re-assign constructor?

3. Var3:如果我不重新分配构造函数会发生什么?

Actually you might not see the difference and this is what makes it a dangerous bug. Every function's prototype object has a constructorproperty so you can access the constructor from an instance.

实际上,您可能看不到差异,这就是使它成为危险错误的原因。每个函数的原型对象都有一个constructor属性,因此您可以从实例访问构造函数。

function A() { }

// When you create a function automatically, JS does this:
// A.prototype = { constructor: A };

A.prototype.someMethod = function() {
    console.log(this.constructor === A); // true
    this.constructor.staticMethod();
    return new this.constructor();  
};

A.staticMethod = function() { };

It's not a best practice because not everybody knows about it, but sometimes it helps. But if you reassign the prototype...

这不是最佳实践,因为不是每个人都知道它,但有时它会有所帮助。但是如果你重新分配原型......

A.prototype = {
    someMethod = function() {
        console.log(this.constructor === A); // false
        console.log(this.constructor === Object); // true
        this.constructor.staticMethod();
        return new this.constructor();  
    }
};

A.prototypeis a newobject, a instance of Objectthan prototypes Object.prototypeand Object.prototype.constructoris Object. Confusing, right? :P

A.prototype是一个对象,一个Object比原型的实例,Object.prototype并且Object.prototype.constructorObject。令人困惑,对吧?:P

So if you overwrite the prototype and don't reset the "constructor" property, it will refer to Objectinstead of A, and if you try to use the "constructor" property to access some static method you may get crazy.

因此,如果您覆盖原型并且不重置“构造函数”属性,它将引用Object而不是A,并且如果您尝试使用“构造函数”属性来访问某些静态方法,您可能会发疯。

回答by eosterberg

I usually settle with returning an object with functions as properties:

我通常会返回一个带有函数作为属性的对象:

var newCat = function (name) {
return {name: name, purr: function () {alert(name + ' purrs')}};
};

var myCat = newCat('Felix');
myCat.name; // 'Felix'
myCat.purr(); // alert fires

You can have inheritance by calling the newCat function and extend the object you get:

您可以通过调用 newCat 函数来继承并扩展您获得的对象:

var newLion = function (name) {
    var lion = newCat(name);
    lion.roar = function () {
        alert(name + ' roar loudly');
    }
    return lion;
}

If you want a global cats object:

如果你想要一个全局的猫对象:

var cats = (function () {

var newCat = function (name) {
    return {
        name: name,
        purr: function () {
            alert(name + ' is purring')
        }
    };
};

return {
    newCat: newCat
};
}());

Now you can call:

现在你可以调用:

var mySecondCat = cats.newCat('Alice');