繁体   English   中英

使用jQuery时扩展object.prototype的替代方法

[英]Alternative methods for extending object.prototype when using jQuery

前段时间我试图扩展Object.prototype ...后来我在控制台看到来自jQuery文件的错误后我感到很惊讶。 我试图弄清楚什么是错的,当然我发现扩展Object.prototype信息是“邪恶的”,“你不应该这样做,因为JS是动态语言而你的代码不会很快工作”和jQuery将会有的信息现在将hasOwnProperty方法添加到for in循环中。

因为我不想离开jQuery,所以放弃了扩展Object.prototype的想法。

直到现在。 我的项目越来越大,我真的很烦,因为我必须重复多次代码的某些部分。 下面是我在项目中使用的一些结构:

charts.js:

CHARTS = {
    _init: function () {
        this.monthlyChart();
        /*
         * 
         * more propertys goes here
         * 
         */
        return this;
    },
    monthlyChart: function () {
        //create my chart
        return {
            update: function () {
               // update chart
            }
        };
    }()
    /*
     * 
     * more propertys goes here
     * 
     */
}._init;

dashboard.js

NAVBAR = {
    _init: function () {
        /*
         * 
         * more propertys goes here
         * 
         */
        return this;
    },
    doSomething: function(){            
        $(document).ready(function(){
            $('.myButton').on('click', function(){
                var data = [];
                // calling property from charts.js
                CHARTS.monthlyChart.update(data);
            });
        });
    }
}._init

正如我所提到的,项目现在非常大 - 它有超过40个js文件,其中一些有几千行代码。 真的很烦人,我每次都要重复_init部分,以及我必须重复$(document).ready && $(window).load许多功能。

我试图为我的问题找到另一种解决方案。 我试图用init属性创建类(你可以在这里找到更多)但是我这个解决方案迫使我在每个文件中添加另一个“不必要的”代码片段并访问其他文件对象属性使它变得复杂(到处返回适当的对象)等等)。 正如评论中所建议的,我开始阅读JS中的getter和setter。

毕竟我创造了类似的东西:

 //Auto initialization
if (typeof $document === 'undefined') {
    var $document = $(document),
            $window = $(window),
            $body = $('body');
}

Object.defineProperty(Object.prototype, '_init', {
    get: function () {
        // if object has no property named `_init`
        if (!this.hasOwnProperty('_init')) {
            for (var key in this) {
                // checking if name of property does starts from '_' and if it is function
                if (this.hasOwnProperty(key) && key[0] === '_' && typeof this[key] === 'function') {                    
                    if (key.indexOf('_ready_') > -1) {
                        //add function to document ready if property name starts from '_ready_'
                        $document.ready(this[key].bind(this));
                    } else if (key.indexOf('_load_') > -1) {
                        //add function to window load if property name starts from '_load_'
                        $window.load(this[key].bind(this));
                    } else {
                        // else execute function now
                        this[key].bind(this)();
                    }
                }
            }
            return this;
        }
    }
});

和我的对象:

var DASHBOARD = {
    _runMe: function(){

    },
    _ready_runMeOnReady: function(){

    },
    _load_runMeOnLoad: function(){

    },
    iAmAString: ''
}._init

看来这个解决方案适用于jQuery。 但使用安全吗? 我没有看到代码可能导致的任何问题,我也没有看到它可能导致的任何进一步问题。 如果有人告诉我为什么我不应该使用这个解决方案,我会很高兴。

我也试图了解它的工作原理 从理论上讲,我通过definePropertyObject.prototype定义了属性,而没有defineProperty赋值。 不知何故,它fore in循环中不会导致jQuery中fore in任何错误,为什么? 这是否意味着属性_init在某些时候或根本没有被定义,因为我只定义了它的getter?

任何帮助将不胜感激 :)

通过不在Object.defineProperty(obj, prop, descriptor)包含描述符,JavaScript将所有布尔描述符属性默认为false。 即可writableenumerable和可configurable 因为你的_init属性是enumerable:false所以你的新属性在for in迭代器中是隐藏的enumerable:false

我不是JQuery的粉丝所以不会评论为什么关于JQuery

向JavaScript的基本类型添加属性没有绝对的规则,这取决于代码运行的环境。 添加到基本类型会将其添加到全局命名空间。 如果您的应用程序与第三方脚本共享命名空间,则可能会发生冲突,导致您的代码或第三方代码或两者都失败。

如果您是唯一的代码,则冲突不会成为问题,但添加到object.prototype将在使用object的所有代码上产生额外开销。

我强烈建议您重新审视全局_init的需求。 当你每次需要一个新物体时,你都不会使用它。 我是JavaScript数据结构的添加方法的粉丝,并试图远离正式的OOP范例

你的问题实际上包含两个问题。

它认为这个解决方案适用于jQuery。 使用安全吗? 我没有看到代码可能导致的任何问题,我也没有看到它可能导致的任何进一步问题。 如果有人告诉我为什么我不应该使用这个解决方案,我会很高兴。

首先,避免修改内置原型有三个主要原因。

For-in循环

使用for-in循环的代码太多而没有hasOwnProperty检查。 在你的情况下,jQuery代码不执行检查。

解决方案

  • 如果没有.hasOwnProperty检查,请不要使用for-in循环。
    • 在这种情况下不适用,因为它是第三方代码,您无法修改它。
  • for-in循环仅遍历可枚举键。
    • 您已使用该解决方案。 Object.defineProperty创建不可枚举的属性( ECMAScript 5.1规范
      • IE8不支持。

冲突

财产名称存在风险。 想象一下,你使用jQuery插件检查对象上是否存在._init属性 - 它可能导致微妙且难以调试的bug。 带有下划线的名称在现代JavaScript库中广泛用于指示私有属性。

封装违规(糟糕的设计)

但是你的问题更严重。 定义全局._init属性表明每个对象都具有通用初始化逻辑。 它破坏了封装,因为您的对象无法完全控制其状态。

由于这个原因,你不能依赖_init方法的存在。 你的同事无法实现自己的课程

替代设计

全局初始化程序

您可以创建全局函数initialize并包装所有需要初始化的对象。

解耦视图和逻辑

您的对象不应该在一个对象中合并逻辑和视图(它违反单一责任原则)并且您是意大利面条代码的受害者。 此外 - 对象初始化不应该将其绑定到DOM,某些控制器对象应该是逻辑和显示之间的代理。

检查流行的客户端MVC框架如何解决这个问题(Angular,Ember,Backbone)已经解决了这个问题,这是个好主意。

  1. 使用getter和setter是否安全?

    是。 但是如果你只支持IE9 +。

  2. 修改Object.prototype是否安全?

    否。创建另一个对象以继承所有应用程序对象。


为什么扩展基本JavaScript对象是 eval evil?

因为在加载脚本的网页上创建的EVERY SINGLE对象将继承该属性或方法。 如果你这样做,有很多缺点,如冲突和性能开销。

有很多方法可以让它变得更好,让我告诉你我使用的那个。

// Here we create the base object:
var someBaseObject = {};
someBaseObject.someMethod = function () {
    // some code here
}
someBaseObject.someProperty = "something";

// And inherit another object from the someBaseObject
someObject = Object.create(someBaseObject);
someObject.someAnotherMethod = function () {
    // some code here
}

这种方法允许我们单独保留Object原型,并构建一个原型链,其中someObject继承自someBaseObject,而someBaseObject继承自Object。

这篇文章我唯一想说的就是:单独留下基础对象并构建自己的对象,这样你就不会头疼。

注意:IE9 +支持Object.create 道格拉斯·克罗克福德Douglas Crockford)的 IE8和更低版本是垫片:

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM