JS Revearing Pattern事件未定义问题

JS Revealing Pattern event undefined issue

本文关键字:未定义 问题 事件 Pattern Revearing JS      更新时间:2023-09-26

我使用JS的模块化设计模式,在使用参数绑定函数时经常遇到问题。我有一个特定的函数,我想将它绑定到不同的事件,以避免为每个绑定的事件编写函数。函数或参数中唯一的区别是将更新的表。问题是,当我用我需要的参数构建一个函数并将这些参数传递给绑定事件时,在加载时,我会在控制台中得到一个未定义的错误。请记住,我想坚持这种设计模式,因为它提供了安全性。

这是我的JS:

var Users = (function(){
    var $addRoleForm = $('#addUserRole');
    var $rolesTableBody = $('#table-roles tbody');
    $addRoleForm.submit(ajaxUpdate(event, $rolesTableBody));
    function ajaxUpdate(event, tableName) {
        event.preventDefault();
        event.stopPropagation();
        var url = this.action;
        var data = $(this).serialize();
        var $this = $(this);
        $.ajax({
            type: 'POST',
            url: url,
            dataType: 'json',
            data: data,
            success: function(data) {
                if(data.st === 0){
                    $messageContainer.html('<p class="alert alert-danger">' + data.msg + '</p>');
                    setTimeout(function(){
                        $messageContainer.hide();
                    }, 7000);
                } else {
                    $messageContainer.html('<p class="alert alert-success">' + data.msg + '</p>');
                    tableName.fadeOut().html('').html(data.build).fadeIn();
                    $this.find('input').val('');
                    setTimeout(function(){
                        $messageContainer.hide();
                    }, 7000);
                }
            },
            error: function(xhr, status, error){
                console.log(xhr.responseText);
            }
        });
    }
})();

这是我在控制台中得到的错误,加载时:

Uncaught TypeError: Cannot read property 'preventDefault' of undefined

我尝试过像这样绑定事件:$addRoleForm.on('submit', ajaxUpdate(event, $rolesTableBody));,并收到相同的结果。

有什么办法解决这个问题吗?

您看到了这个问题,因为按照现在的编写方式,ajaxUpdate执行,返回undefined,然后将undefined传递给事件侦听器,所以基本上就是这样做的:$addRoleForm.submit(undefined)

这里有2个选择:

1) 您可以将其封装在一个匿名函数中:

$addRoleForm.submit(function(event) {
  //pass the value of "this" along using call
  ajaxUpdate.call(this, event, someValue);
});
$someOtherForm.submit(function(event) {
  //pass the value of "this" along using call
  ajaxUpdate.call(this, event, someOtherValue);
});

2) 您可以使用bind:提前设置第一个参数

$addRoleForm.submit(ajaxUpdate.bind($addRoleForm, someValue));
$someOtherForm.submit(ajaxUpdate.bind($someOtherForm, someOtherValue));

使用这种方式,您将this的值绑定为$addRoleForm,并将第一个参数设置为始终为someValue,因此它与:相同

ajaxUpdate(someValue, event) {
  //value of "this" will be $addRoleForm;
}

要传递事件和自定义参数,您应该使用一个匿名函数调用

$addRoleForm.submit(function(event) {
    ajaxUpdate(event, $rolesTableBody));
});

这是迄今为止最简单、可读性最好的方法。


你现在所做的等同于这个

var $addRoleForm    = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
var resultFromCallingFunction = ajaxUpdate(event, $rolesTableBody); // undefined
$addRoleForm.submit(resultFromCallingFunction);

在您调用ajaxUpdate函数的情况下,正如括号所做的那样,并将返回的结果传递回提交回调,在您的情况下是undefined,这是在未指定其他值时函数返回的默认值。

你可以参考这个函数,比如这个

$addRoleForm.submit(ajaxUpdate);

但是不能传递第二个参数

问题指的是Revearing Module模式。使用这种设计的好处是可读性。使用anon函数可能会起作用,但会破坏模块模式本身的整体目的。

构造模块以帮助维护作用域的一个好方法是先设置helper函数,然后在最后调用return。

事件用例示例:

var User = function() {
    // local VARS available to User
    var addRoleForm = document.querySelector('#addUserRole');
    var rolesTableBody = document.querySelector('#table-roles tbody');
    // Helper function 1
    function ajaxUpdate(tableName) {
         ...
    }
    // Helper function 2
    function someFunc() {
         ...
    }
    function bindEvents() {
        addRoleForm.addEventListener('submit', ajaxUpdate, false);
        addRoleForm.addEventListener('click', someFunc, false); 
    }
    function init() {
        bindEvents();
    }        
    return {
             runMe:init
           }
}().runMe();

有助于"模块化"您的工作流程。作为一名IIFE,你也在书写你的启示模式。这可能会在将来引起调试方面的麻烦。编辑IIFE以通过返回调用更容易维护,其他开发人员也更容易在最初使用和学习。此外,它还允许您在IFFE之外扩展到另一个模块,例如:

var Clothes = function() {
function anotherFunc() {
    ...
}
init() {
    User.runMe();
    anotherFunc();
}
return {
         addClothes: init
       }
}().addClothes();

我希望这有助于让你更好地理解如何/何时/为什么使用JS揭示模式。快速提示:您可以将模块制作成IIFE,这不是问题。您只是限制了您可以使用的范围的上下文。另一种方法是将var User和var Clothings封装到一个主模块中,然后使其成为IIFE。这有助于防止污染您的全局命名空间。

我上面写的例子:

// MAIN APPLICATION    
var GettinDressed = (function() {

    // MODULE ONE
    ///////////////////////////
    Var User = function() {
    // local VARS available to User
    var addRoleForm = document.querySelector('#addUserRole');
    var rolesTableBody = document.querySelector('#table-roles tbody');
    // Helper function 1
    function ajaxUpdate(tableName) {
         ...
    }
    // Helper function 2
    function someFunc() {
         ...
    }
    function bindEvents() {
        addRoleForm.addEventListener('submit', ajaxUpdate, false);
        addRoleForm.addEventListener('click', someFunc, false); 
    }
    function init() {
        bindEvents();
    }        
    return {
             runMe:init,
             style: someFunc
           }
    }();
    // MODULE TWO
    //////////////////////////
    var Clothes = function() {
    function anotherFunc() {
        ...
    }
    init() {
        User.style();
        anotherFunc();
    }
    return {
             dressUp: init
           }
    }();
  // Define order of instantiation
  User.runMe();
  Clothes.dressUp();
}());