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

时间:2013-04-17 08:21:21

标签: javascript scope prototype

MyGlobalObject;

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

长命名函数必须可以从MyGlobalObject调用,而后者必须在加载脚本后始终作为全局(到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)之间的主要区别是什么?

By 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的结果)有什么区别?

OR VAR4?

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

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

2 个答案:

答案 0 :(得分:30)

变化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 ++等人很容易理解。
  • 每个实例可以拥有私有变量,因为每个创建的对象都有一个函数closure
  • 它允许多重继承,也称为Twitter-mixins或functional mixins
  • obj instanceof SomeType将返回true

缺点

  • 随着您创建的对象越来越多,它会消耗更多内存,因为每个对象都会创建一个新的闭包,并再次创建它的每个方法。
  • 私有属性为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,没有其他方法可以知道child是否具有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运算符意味着创建对象并使用此函数的原型({{1} })和Object.create属性作为初始化程序。

赞成

  • 高性能
  • 原型链将允许您知道某个对象是否继承了某种类型

缺点

  • 两步继承

继承

constructor

你可能认为它看起来像一套超级变种2 ......而且你是对的。它类似于变体2,但具有初始化函数(构造函数);

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(); child instanceof SubType将同时返回child instanceof SomeType

好奇心:幕后true运算符

instanceof

变体4 - 覆盖function isInstanceOf(obj, Type) { return Type.prototype.isPrototypeOf(obj); }

当你在幕后做__proto__

Object.create(obj)

function fakeCreate(obj) { var child = {}; child.__proto__ = obj; return child; } var child = fakeCreate(obj); 属性直接修改对象的隐藏__proto__属性。由于这会破坏JavaScript行为,因此它不是标准的。标准方式是首选([Prototype])。

赞成

  • 快速且高效的

缺点

  • 非标
  • 危险;你不能有一个hashmap,因为Object.create键可以改变对象的原型

继承

__proto__

评论问题

1。 var1:SomeType.call(this)会发生什么?是打电话'特殊功能?

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

在函数上使用.call()时,可以在函数内部传递一个额外的参数 context var child = { __proto__: obj }; obj.isPrototypeOf(child); // true 的值,例如:

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添加方法。

2。 var3:使用"真正定义属性"你的意思是我在功能中使用它们吗?这是一个惯例吗?因为获取this.newProperty而不与其他成员函数在同一级别定义不是问题。

我的意思是你的对象所拥有的任何属性都不是函数必须在构造函数上定义,而不是在原型上定义,否则你将面临一个更令人困惑的JS问题。你可以see it here,但它不在这个问题的焦点之内。

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

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

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的实例比原型ObjectObject.prototypeObject.prototype.constructor。令人困惑,对吧? :P

因此,如果您覆盖原型并且不重置"构造函数"如果您尝试使用"构造函数"访问某些静态方法的属性,你可能会发疯。

答案 1 :(得分: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');