可重用的 javascript 对象、原型和范围 [英] reusable javascript objects, prototypes and scope

查看:56
本文介绍了可重用的 javascript 对象、原型和范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

MyGlobalObject;

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

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

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.

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

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

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

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

它可以用

VAR1

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

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

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

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

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

或与

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

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

还是 VAR4?

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

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?

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

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?

推荐答案

Variation 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() 时都会创建一个新对象,创建其所有方法并将所有此方法添加到新对象中.每次创建对象时.

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.

  • 它看起来像经典继承,所以 Java-C#-C++ 等人很容易理解.
  • 每个实例可以有私有变量,因为您有一个函数 closure根据您创建的每个对象
  • 它允许多重继承,也称为 Twitter-mixins 或 功能性混合
  • obj instanceof SomeType 将返回 true
  • 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 closure per each object you create
  • It allows multiple inheritance, also known as Twitter-mixins or functional mixins
  • obj instanceof SomeType will return true
  • 创建的对象越多,它消耗的内存就越多,因为对于每个对象,您都在创建一个新的闭包并再次创建它的每个方法.
  • 私有属性是private,不是protected,子类型不能访问它们
  • 没有简单的方法可以知道对象是否具有某种类型作为超类.
  • 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.
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 方法,而不是一一查看它们是否具有.

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

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.

  • 简单易懂.
  • 性能
  • 没有隐私,每个财产都是公开的.

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

You can inherit a object prototyping it.

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

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

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

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

变体 3 - 构造器模式

更新:这是模式 ES6 类是的糖语法.如果您使用 ES6 类,那么您实际上就是在遵循此模式.

Variation 3 - Constructor pattern

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

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

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

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

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

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

The new keyword under the hood simply does this:

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

var obj = doNew(SomeType);

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

What you have is a function than has no methods; it just has a prototype property with a list of functions, the new operator means to create a new object and use this function's prototype (Object.create) and constructor property as initializer.

  • 性能
  • 原型链可以让你知道一个对象是否继承自某种类型
  • 两步继承
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();

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

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

child instanceof SubTypechild 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).

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__ 键可以更改对象的原型
  • Non-standard
  • Dangerous; you can't have a hashmap since the __proto__ key can change the object's prototype
var child = { __proto__: obj };
obj.isPrototypeOf(child); // true

评论问题

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

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

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

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

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

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

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

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.

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

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

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.prototype 是一个 new 对象,是 Object 的一个实例,而不是原型 Object.prototypeObject.prototype.constructorObject.令人困惑,对吧?:P

A.prototype is a new object, a instance of Object than prototypes Object.prototype and Object.prototype.constructor is Object. Confusing, right? :P

因此,如果您覆盖原型并且不重置构造函数"属性,它将引用 Object 而不是 A,并且如果您尝试使用构造函数"属性来访问一些你可能会抓狂的静态方法.

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

这篇关于可重用的 javascript 对象、原型和范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆