javascript - 可重用的 javascript 对象、原型(prototype)和范围

标签 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 例如)?使用原型(prototype)(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 调试器中看到原型(prototype)和 __proto__特殊领域。我已经读到那没问题,但为什么它们不保存在单个原型(prototype)中?

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

最佳答案

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

  • 缺点
  • 随着您创建的对象越多,它消耗的内存就越多,因为对于每个对象,您都在创建一个新的闭包并再次创建它的每个方法。
  • 私有(private)属性(property)是private ,不是 protected , 子类型无法访问它们
  • 没有简单的方法可以知道对象是否具有某种类型作为父类(super class)。

  • 遗产
    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 - 带有原型(prototype)的对象字面量
    var obj = {
        publ: "I'm public",
        _convention: "I'm public too, but please don't touch me!",
        someMethod: function() {
            return this.publ + this._convention;
        }
    };
    

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

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

  • 缺点
  • 没有隐私,每个属性(property)都是公开的。

  • 遗产

    您可以继承一个对象原型(prototype)。
    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;
        };
    }
    

    要知道一个对象是否是另一个对象的原型(prototype),您可以使用
    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();
    

    如果您没有继承并记住重新分配构造函数属性,则可以重新分配原型(prototype)而不是添加每个方法:
    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运算符意味着创建一个新对象并使用此函数的原型(prototype)( Object.create )和 constructor属性作为初始值设定项。

    优点
  • 高性能
  • 原型(prototype)链可以让你知道一个对象是否继承自某种类型

  • 缺点
  • 两步继承

  • 遗产
    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 的 super 集……你是对的。它就像变体 2,但有一个初始化函数(构造函数);
    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]属性(property)。因为这会破坏 JavaScript 行为,所以它不是标准的。首选标准方式( Object.create )。

    优点
  • 快速高效

  • 缺点
  • 非标
  • 危险的;你不能有哈希图,因为 __proto__键可以改变对象的原型(prototype)

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

    评论问题

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

    哦,是的,函数是对象,所以它们有方法,我会提到三个:.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 而不与其他成员函数在同一级别定义它不是问题。

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

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

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

    这不是最佳实践,因为不是每个人都知道它,但有时它会有所帮助。但是如果你重新分配原型(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新品 对象,Object 的一个实例比原型(prototype)Object.prototypeObject.prototype.constructorObject .令人困惑,对吧? :P

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

    关于javascript - 可重用的 javascript 对象、原型(prototype)和范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16055046/

    相关文章:

    javascript - select2 背后的 Javascript 功能是什么

    javascript - Highcharts 更改或添加 point.names 和 y 数组中名称的颜色

    javascript - jQuery 在 x 毫秒后隐藏通知

    javascript - Dojo访问js文件之外的js对象

    iphone - (iPhone/iOS) UISearchBar 范围按钮覆盖 UITableView 的第一行

    javascript - `Object` 是 JavaScript 中的函数吗?

    javascript - React运行错误无法解析react-dom/client

    javascript - 编写 javascript 闭包的正确方法

    javascript - div 中的文本 : splitting in middle when it word wraps

    c - #define 变量的范围是什么?