11
MyGlobalObject;

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

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

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

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

它可以用

VAR1

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

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

GlobaldFunction的意思是所有初始化/实例化(可能实例化)实例的单个实例..

或与

VAR2

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

或与

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?!!!!

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

还是 VAR4?

我在 Chrome 调试器中看到了原型和__proto__特殊字段。我读过没关系,但为什么它们没有保存在一个原型中?

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

4

2 回答 2

32

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

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

优点

  • 它看起来像经典继承,因此对于 Java-C#-C++ 等人来说很容易理解。
  • 每个实例可以有私有变量,因为每个创建的对象都有一个函数闭包
  • 它允许多重继承,也称为 Twitter-mixins 或函数式 mixins
  • obj instanceof SomeType将返回真

缺点

  • 随着您创建的对象越多,它会消耗更多的内存,因为对于每个对象,您都在创建一个新的闭包并再次创建它的每个方法。
  • 私有属性是private,不是protected,子类型不能访问它们
  • 没有简单的方法可以知道一个对象是否有某个类型作为超类。

遗产

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

var child = new SubType();

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

变体 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;
    }
};

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

优点

  • 它快速且易于理解。
  • 高性能

缺点

  • 没有隐私,每个财产都是公共的。

遗产

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

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

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

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

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

obj.isPrototypeOf(child); // true

变体 3 - 构造函数模式

更新:这是模式 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();

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

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

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

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

引擎盖下的new关键字只是这样做:

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

var obj = doNew(SomeType);

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

优点

  • 高性能
  • 原型链将让您知道一个对象是否从某种类型继承

缺点

  • 两步继承

遗产

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

您可能会认为它看起来像是 Variation 2 的超集……您是对的。类似于变体 2,但具有初始化函数(构造函数);

child instanceof SubType并将child instanceof SomeType返回true

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

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

变体 4 - 覆盖__proto__

当你Object.create(obj)在引擎盖下这样做时

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

var child = fakeCreate(obj);

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

优点

  • 快速高效

缺点

  • 非标
  • 危险的; 你不能有一个哈希图,因为__proto__键可以改变对象的原型

遗产

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

评论问题

1. var1: SomeType.call(this) 中发生了什么?“呼叫”有特殊功能吗?

哦,是的,函数是对象,所以它们有方法,我会提到三个: .call( ).apply().bind()

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

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

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

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

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

3. Var3:如果我不重新分配构造函数会怎样?

实际上,您可能看不到差异,这就是使其成为危险错误的原因。每个函数的原型对象都有一个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() { };

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

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

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

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

于 2013-04-17T11:14:03.987 回答
5

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

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

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

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

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

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

var cats = (function () {

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

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

现在您可以致电:

var mySecondCat = cats.newCat('Alice');
于 2013-04-17T08:38:50.373 回答