在这种情况下如何避免声明全局变量?

时间:2019-01-12 16:21:48

标签: javascript jquery ajax

如果我有一个带有ajax调用的函数,该函数从数据库中获取了一些值,然后需要将该值发送回php脚本,依此类推,我该如何声明此变量?

function call(id){
  $.ajax({
    . . .
    data: {id: id},
    success: function(res){
       window.id = res.id
     }
  });
}


//first call the function with a value
 call(10);

//then call it with the returned value every time the btn gets clicked

$('#btn').click(function(){
 call(window.id);
})

根据我的理解,使用像这样的全局变量是一种不好的做法,因为全局范围已经很拥挤,脚本查找变量需要更长的时间。我可以通过让我将其发送回而不是全局的方式来定义此值吗?

3 个答案:

答案 0 :(得分:4)

避免全局变量的最简单方法是首先不在全局范围内工作。这样做的方法是创建一个IIFE(立即调用函数表达式),它是一个在全局范围内的匿名函数,但是由于它是匿名的,因此它没有机会与具有相同功能的另一个函数发生冲突。名称。它会立即被调用,以便其中的代码可以执行(类似于init这种操作)。然后,在该函数内,您可以执行通常的操作。然后在此处创建IIFE范围(而非全局范围)变量。

// IIFE wrapper
(function(){

  let mainID = null; // IIFE scoped variable

  function call(id){
    $.ajax({
      . . .
      data: {id: id},
      success: function(res){
         mainID = res.id; // Set the IIFE scoped variable to the AJAX result
       }
    });
  }

  // Set up the event
  $('#btn').click(function(){
    all(res.id);  // <-- And, you can use it anywhere in the IIFE
  });

  call(10);        // first call the function with a value

})();  // <-- The extra set of parenthesis is what invokes the expression 

另一方面,即使在适当的位置,您的代码也无法正常工作,因为您要在AJAX调用有机会完成之前设置按钮的click事件处理程序。仅仅因为该代码位于异步代码之后,并不意味着它将在异步代码之后执行。异步操作的全部要点是我们不知道它何时会完成,并且由于JavaScript在单线程环境中运行,而异步操作正在进行中,因此当前功能会运行到完成。仅在当前线程空闲之后,才会调用异步操作(成功回调)的结果。如果事件处理程序依赖于AJAX调用的结果,则需要在“成功”回调中设置事件处理程序,因此无论如何对于全局毫无意义。您仍然应该将所有代码都放在IIFE中,因为这是最佳实践。

这是一个示例,显示了另一种类型的异步操作(计时器),该操作仍然像您的AJAX调用那样操作,从而显示了问题:

// Global variable
var result = null;

// Start an async operation that will execute the callback
// after three seconds:
setTimeout(function(){
  result = "complete"
  console.log("Async operation " + result);
}, 3000);

// This function will run before the async operation 
// completes, so the use of the variable that is set
// by the async operation will fail
console.log("Local operation " + result);

因此,实际上,您甚至根本不需要全局变量,只需将事件绑定代码移到成功回调中即可。从用户界面的角度来看,您可能希望按钮从禁用开始,然后在成功回调中将其启用,以便在设置click处理程序之前无法单击它。

// IIFE wrapper
(function(){

  function call(id){
    $.ajax({
      . . .
      data: {id: id},
      success: function(res){
        // Set up the event now that the AJAX call is complete
        $('#btn').click(function(){
          call(res.id);
        });
       }
    });
  }

  call(10);        // first call the function with a value

})();  // <-- The extra set of parenthesis is what invokes the expression

这是整个情况的可行示例:

(function(){
  // IIFE scoped variables
  let btn = document.querySelector("button");
  let random = null;

  function async(){
    // Async operation complete! 
    
    // Generate a random number that simulates the
    // DB response that would now be available
    random = Math.floor(Math.random() * 11);  
    
    // Enable the button:   
    btn.removeAttribute("disabled");
    
    // Set up the button's click event handler
    btn.onclick = function(){
      console.log("Button clicked. ID = " + random);
      async(); 
    };
    
  }
  
  // Start an async operation that will execute the callback
  // after three seconds:
  setTimeout(async, 3000);
})();
<button type="button" disabled>Click Me</button>

答案 1 :(得分:0)

由于id在语义上与#btn相关联,因此您可以将返回的id值存储为#btn属性:

function call(id){
  $.ajax({
    . . .
    data: {id: id},
    success: function(res){
       $('#btn').prop('response_id', res.id);
     }
  });
}

//first call the function with a value
 call(10);

//then call it with the returned value every time the btn gets clicked

$('#btn').click(function(){
 call(this.prop('response_id'));
})

答案 2 :(得分:-3)

只需将window.id替换为call.id(即,将其存储为函数的属性):

function call(id){
  $.ajax({
    . . .
    data: {id: id},
    success: function(res){
       call.id = res.id
     }
  });
}


//first call the function with a value
call(10);

//then call it with the returned value every time the btn gets clicke
$('#btn').click(function(){
  call(call.id);
})