【问题标题】:How does this JavaScript function?这个 JavaScript 是如何工作的?
【发布时间】:2012-08-07 09:29:56
【问题描述】:

我之前在搜索如何使用 JavaScript 淡化元素时遇到了这个函数(对象)。我开始想知道它是如何工作的?

var fadeEffect=function(){
return{
    init:function(id, flag, target){
        this.elem = document.getElementById(id);
        clearInterval(this.elem.si);
        this.target = target ? target : flag ? 100 : 0;
        this.flag = flag || -1;
        this.alpha = this.elem.style.opacity ? parseFloat(this.elem.style.opacity) * 100 : 0;
        this.si = setInterval(function(){fadeEffect.tween()}, 20);
    },
    tween:function(){
        if(this.alpha == this.target){
            clearInterval(this.elem.si);
        }else{
            var value = Math.round(this.alpha + ((this.target - this.alpha) * .05)) + (1 * this.flag);
            this.elem.style.opacity = value / 100;
            this.elem.style.filter = 'alpha(opacity=' + value + ')';
            this.alpha = value
        }
    }
}
}();

我知道这是自调用的,并且只返回一个具有两种方法的对象。我主要关心的是为什么它使用 this 关键字?我假设“this”关键字是对象名称“fadeEffect”的占位符。如果“this”用于创建多个对象,我会理解……但为什么在这里使用它?

另一个困扰我的是这个三元运算符...

   this.target = target ? target : flag ? 100 : 0;

这到底是怎么回事?这就像两个三元运算符合二为一,我从未想过是可能的?

【问题讨论】:

    标签: javascript ternary-operator


    【解决方案1】:

    关于你的第二个问题。这可能会更清楚:

    this.target = (target ? target : (flag ? 100 : 0));
    

    是的,一个嵌套的三元运算符!用文字写出来:

    this.target = (target 是真值吗?然后使用 target。如果不是,则使用最后一部分的结果 -> (标志是真值吗?使用 100。否则,使用 0))。

    【讨论】:

      【解决方案2】:

      把它想象成一个命名空间。 this 关键字引用自调用函数返回的对象字面量。这意味着this.target 可以在全局命名空间(或定义fadeEffect 的任何范围)中作为对象属性访问:fadeEffect.target,但它不会干扰外部范围中可能存在的其他变量。

      这两个方法设置返回对象的新属性,仅此而已。就我个人而言,我认为这是一个糟糕的代码......在这个例子中,闭包会是更好的选择:

      var fadeEffect=function(){
          var elem,target,flag,alpha,si;//make private
          return{
              init:function(id, flag, target){
                  elem = document.getElementById(id);
                  clearInterval(elem.si);
                  target = target ? target : flag ? 100 : 0;
                  flag = flag || -1;
                  alpha = elem.style.opacity ? parseFloat(elem.style.opacity) * 100 :0;
                  si = setInterval(function(){fadeEffect.tween()}, 20);
              },
              tween:function(){
                  if(alpha == target){
                      clearInterval(si);//this.elem.si doesn't add up, init defines it as this.si
                  }else{
                      var value = Math.round(alpha + ((target - alpha) * .05))+ (1 * flag);
                      elem.style.opacity = value / 100;
                      elem.style.filter = 'alpha(opacity=' + value + ')';
                      alpha = value
                  }
              }
          }
      }();
      

      这做同样的事情,但其他代码不能干扰目标的值,或弄乱间隔等......你说得对,this 在这种情况下不需要关键字,但我认为写这篇文章的人要么不熟悉 JS 闭包,要么至少对它们的工作方式不安全。此代码有效地模拟了单例模式,或者至少将对象字面量视为类的实例。我的猜测是,作者熟悉经典 OOP,但不熟悉原型继承。不管怎样,上面的代码更安全,更安全更好恕我直言


      关于嵌套三元的问题,我使用 JSLint 检查了下面的代码,它提出了一个更短但更清晰的替代方案:使用默认运算符,然后是一个三元:

       //JSLint recommends this
       target = argTarget || argFlag ? 100 : 0;
       //over nested ternary
       target = argTarget ? argTarget : argFlag ? 100 : 0;
      

      无论如何,这里是相同的代码,只是没有使用危险的this 构造,而是使用闭包,顺便说一句,JavaScript 的强大功能之一,值得仔细研究一下你可以用它们做什么!

      var fadeEffect=(function()
      {
          var elem,target,flag,alpha,si;//make private
          //define private 'methods': functions will be available, but only to return object
          //tween shouldn't be callable, it's a callback for the interval, which is set in init
          function tween()
          {
              if(alpha === target)
              {
                 clearInterval(si);//this.elem.si doesn't add up, init defines it as this.si
              }
              else
              {
                  alpha = Math.round(alpha + ((target - alpha) * 0.05))+ (1 * flag);
                  //don't know why 1*flag is needed here, suggest:
                  //alpha = Math.round(alpha + ((target - alpha) * 0.05)) + (+flag); +flag coerces to numeric
                  elem.style.opacity = alpha / 100;
                  elem.style.filter = 'alpha(opacity=' + alpha + ')';
              }
          }
          return{
              init:function(id, argFlag, argTarget)//arguments !== closure scope
              {
                  if (si !== undefined && si !== null)
                  {
                      clearInterval(si);
                  }
                  elem = document.getElementById(id);
                  //JSLint recommends this:
                  target = argTarget || argFlag ? 100 : 0;
                  //over nested ternary
                  target = argTarget ? argTarget : argFlag ? 100 : 0;
                  flag = argFlag || -1;
                  alpha = elem.style.opacity ? parseFloat(elem.style.opacity) * 100 :0;
                  si = setInterval(tween, 20);//just a reference to the tween function will do
              }
          };
      })();
      fadeEffect.init('someId',1,50);//will set things in motion
      fadeEffect.tween();//undefined
      console.log(fadeEffect.target);
      fadeEffect.target = document.getElementById('someOtherId');//no problem, but won't change the value of var target
      

      这样,tween 方法只能通过间隔调用,并且对象及其方法/函数在其上发挥作用的元素永远不会被外部操作覆盖 ,它们是对象固有的。这使得构造更安全,更重要的是,您只能真正搞砸 1 个方法:覆盖 .init 方法,对象将变得无用,但无害。将其与您的代码进行比较,您可能会弄乱这两种方法,但让区间保持不变......这是个坏消息:区间最终会寻找一个很可能已被删除的回调函数,从而导致您的代码惨遭失败:

      //asume your code using this.tween();
      fadeEffect.init('id',1,123);
      delete fadeEffect.tween;
      //inside fadeEffect:
      setInterval(function(){fadeEffect.tween()}, 20);
      //should be written as:
      setInterval(fadeEffect.tween,20);
        // === setInterval(undefined,20); === :-(
      

      【讨论】:

      • 是的,我就是这么想的。在这种情况下这不是必需的,但我在上面读到它用于在此命名空间中动态创建变量(属性)。我认为 this.elem.si 是一个错误,因为它也不适合我。它被定义为this.si。与编写此代码的人所使用的相比,我更喜欢您的代码。我接触过闭包和命名空间。我还不能舒服地谈论这些话题。
      • 你说得对,说this是没有必要的。您发布的代码创建了一个对象文字,但编写为构造函数。这只是令人困惑和不好的做法。如果您知道需要哪些变量,请使用闭包。你的对象现在的工作方式,你可能会不小心更改target 属性,导致间隔突然在不同的元素上淡出,从而最终得到 2 个半淡出的元素。使用闭包可以保护您免受此影响。我将把这段代码进一步开发成一个可用、安全的版本(这个还有一些问题需要解决)
      • @ted - :-) 当然,这取决于 OP 来决定。现在看它,我必须认为它相当长(而且笨重)。我不太擅长解释这些东西,所以我往往过于冗长。然而,我确实觉得,并且不想吹嘘,我的答案是这里唯一一个解决了 OP 问题的两个要素的答案。
      • 恕我直言,看到这样的答案显示了应该如何编写 javascript 代码真是太酷了;我教了一些对自己有用的东西;谢谢(:
      • No @ted 这个答案不太好,无法接受。我喜欢这个答案。简洁明了。他甚至在 cmets 中帮助我。我本想接受这个答案,但忘记了。非常感谢您对 Elias 的支持! :)
      【解决方案3】:

      对this.target = target ? target : flag ? 100 : 0;的再解释:

      if(target){
        this.target = target;
      }
      else{  
        if(flag){
          this.target = 100;
        } else {
          this.target = 0;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-02
        • 1970-01-01
        • 2013-09-20
        • 1970-01-01
        • 1970-01-01
        • 2014-04-30
        • 1970-01-01
        • 2019-06-15
        相关资源
        最近更新 更多