为什么闭包编译器发生了变化?#39;这个?

时间:2015-08-21 22:38:11

标签: javascript google-closure-compiler

在我的压缩代码中,在高级编译下,编译器已经更改了我的函数的调用上下文。我经过一些推理为什么以及怎么做,所以我可以弄清楚如何解决它。

背景故事

我已将代码生成到模块中,过去几天我一直在将react js material ui library转换为闭包式提供/需要语法。我无法让CommonJS很好地使用我的模块化方法而且我无法使goog.module使用我使用'plovr'的调试工具。几乎在那里,但我绊倒了这个。

我的编译代码有源图,所以我可以看到它出错的地方,对我来说似乎没有任何意义。

错误抛出此处。请注意,这是压缩代码,但您会看到它通过源映射映射到原始代码。 decomposeColor不存在,因为this等于window对象。 enter image description here

如果我在控制台中键入thisenter image description here

然后我在堆栈中向上一级并在控制台中键入this,它是我希望看到一级降低的正确对象。 enter image description here enter image description here

这里的内容相同,但实际代码看起来像是压缩的 enter image description here enter image description here

知道什么会导致编译器这样做吗?

UPDATE:

在评论中的一些指针(感谢Jan)之后我知道应该寻找什么,似乎编译器已经从我的对象方法转换了

goog.provide('mui.utils.colorManipulator');
mui.utils.colorManipulator = {
  //...
  /**
   * @this {mui.utils.colorManipulator}
   */
  fade: function fade(color, amount) {
    color = this._decomposeColor(color);
    if (color.type === 'rgb' || color.type === 'hsl') color.type += 'a';
    return this._convertColorToString(color, amount);
  }
  //...
}

进入在全局范围内声明的函数。

function kc(f, a) {
  f = this.nd(f);
  if ("rgb" === f.type || "hsl" === f.type)
    f.type += "a";
  return this.md(f, a)
}

所以'这个'上下文将有所不同,我只需要弄清楚为什么编译器会这样做。

更新

这里是colorManipulator的所有代码。它几乎是从这个this

移植而来的
goog.provide('mui.utils.colorManipulator')

mui.utils.colorManipulator = {

  /**
   * The relative brightness of any point in a colorspace, normalized to 0 for
   * darkest black and 1 for lightest white. RGB colors only. Does not take
   * into account alpha values.
   *
   * TODO:
   * - Take into account alpha values.
   * - Identify why there are minor discrepancies for some use cases
   *   (i.e. #F0F & #FFF). Note that these cases rarely occur.
   *
   * Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
   */
  _luminance(color) {
    color = this._decomposeColor(color);

    if (color.type.indexOf('rgb') > -1) {
      let rgb = color.values.map((val) => {
        val /= 255; // normalized
        return val <= 0.03928 ? val / 12.92 : Math.pow((val + 0.055) / 1.055, 2.4);
      });

      return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2];

    }
    else {
      let message = 'Calculating the relative luminance is not available for ' +
                    'HSL and HSLA.';
      console.error(message);
      return -1;
    }
  },

  /**
   * @params:
   * additionalValue = An extra value that has been calculated but not included
   *                   with the original color object, such as an alpha value.
   */
  _convertColorToString(color, additonalValue) {
    let str = color.type + '(' +
              parseInt(color.values[0]) + ',' +
              parseInt(color.values[1]) + ',' +
              parseInt(color.values[2]);

    if (additonalValue !== undefined) {
      str += ',' + additonalValue + ')';
    }
    else if (color.values.length === 4) {
      str += ',' + color.values[3] + ')';
    }
    else {
      str += ')';
    }

    return str;
  },

  // Converts a color from hex format to rgb format.
  _convertHexToRGB(color) {
    if (color.length === 4) {
      let extendedColor = '#';
      for (let i = 1; i < color.length; i++) {
        extendedColor += color.charAt(i) + color.charAt(i);
      }
      color = extendedColor;
    }

    let values = {
      r:    parseInt(color.substr(1,2), 16),
      g:    parseInt(color.substr(3,2), 16),
      b:    parseInt(color.substr(5,2), 16),
    };

    return 'rgb(' + values.r + ',' +
                    values.g + ',' +
                    values.b + ')';
  },

  // Returns the type and values of a color of any given type.
  _decomposeColor(color) {
    if (color.charAt(0) === '#') {
      return this._decomposeColor(this._convertHexToRGB(color));
    }

    let marker = color.indexOf('(');
    let type = color.substring(0, marker);
    let values = color.substring(marker + 1, color.length - 1).split(',');

    return {type: type, values: values};
  },

  // Set the absolute transparency of a color.
  // Any existing alpha values are overwritten.
  /**
   * @this {mui.utils.colorManipulator}
   */
  fade(color, amount) {
    color = this._decomposeColor(color);
    if (color.type === 'rgb' || color.type === 'hsl') color.type += 'a';
    return this._convertColorToString(color, amount);
  },

  // Desaturates rgb and sets opacity to 0.15
  lighten(color, amount) {
    color = this._decomposeColor(color);

    if (color.type.indexOf('hsl') > -1) {
      color.values[2] += amount;
      return this._decomposeColor(this._convertColorToString(color));
    }
    else if (color.type.indexOf('rgb') > -1) {
      for (let i = 0; i < 3; i++) {
        color.values[i] *= 1 + amount;
        if (color.values[i] > 255) color.values[i] = 255;
      }
    }

    if (color.type.indexOf('a') <= -1) color.type += 'a';

    return this._convertColorToString(color, '0.15');
  },

  darken(color, amount) {
    color = this._decomposeColor(color);

    if (color.type.indexOf('hsl') > -1) {
      color.values[2] += amount;
      return this._decomposeColor(this._convertColorToString(color));
    }
    else if (color.type.indexOf('rgb') > -1) {
      for (let i = 0; i < 3; i++) {
        color.values[i] *= 1 - amount;
        if (color.values[i] < 0) color.values[i] = 0;
      }
    }

    return this._convertColorToString(color);
  },


  // Calculates the contrast ratio between two colors.
  //
  // Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
  contrastRatio(background, foreground) {
    let lumA = this._luminance(background);
    let lumB = this._luminance(foreground);

    if (lumA >= lumB) {
      return ((lumA + 0.05) / (lumB + 0.05)).toFixed(2);
    }
    else {
      return ((lumB + 0.05) / (lumA + 0.05)).toFixed(2);
    }
  },

  /**
   * Determines how readable a color combination is based on its level.
   * Levels are defined from @LeaVerou:
   * https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/contrast-ratio.js
   */
  contrastRatioLevel(background, foreground) {
    let levels = {
      'fail': {
        range: [0, 3],
        color: 'hsl(0, 100%, 40%)',
      },
      'aa-large': {
        range: [3, 4.5],
        color: 'hsl(40, 100%, 45%)',
      },
      'aa': {
        range: [4.5, 7],
        color: 'hsl(80, 60%, 45%)',
      },
      'aaa': {
        range: [7, 22],
        color: 'hsl(95, 60%, 41%)',
      },
    };

    let ratio = this.contrastRatio(background, foreground);

    for (let level in levels) {
      let range = levels[level].range;
      if (ratio >= range[0] && ratio <= range[1]) return level;
    }
  },
};

3 个答案:

答案 0 :(得分:3)

  

对象属性展平的含义

     

在高级模式下,编译器会折叠对象属性以进行准备   用于缩短名称。例如,编译器转换为:

     

var foo = {}; foo.bar = function(a){alert(a)};
  foo.bar(&#34;你好&#34);进入这个:

     

var foo $ bar = function(a){alert(a)}; FOO $巴(&#34;你好&#34);这个   property flattening允许后面的重命名传递重命名   有效率的。编译器可以用单个字符替换foo $ bar,   例如。

     

但财产扁平化也会使以下做法变得危险:

     

在构造函数和原型方法之外使用它:

     

属性展平可以改变a中关键字的含义   功能。例如:

     

var foo = {}; foo.bar = function(a){this.bad = a; }; //坏了   foo.bar(&#34;你好&#34);变为:

     

var foo $ bar = function(a){this.bad = a; }; FOO $巴(&#34;你好&#34);   在转换之前,foo.bar中的this指的是foo。   转型后,这指的是全球性的。在案件   像这样编译器产生这个警告:

     

&#34;警告 - 在静态方法中危险地使用它foo.bar&#34;阻止   由于破坏了对此的引用而导致的属性变平,只能使用   这在构造函数和原型方法中。这个意思是   当您使用new关键字调用构造函数时,或者是明确的   在作为原型属性的函数中。

来源:https://developers.google.com/closure/compiler/docs/limitations?hl=en#implications-of-object-property-flattening]

换句话说,由于属性展平,闭包编译器不支持在对象文字中使用 this

因此,一个简单的解决方案是引用您尝试访问其属性的对象的完整命名空间。

mui.utils.colorManipulator.name_of_function(args);

答案 1 :(得分:2)

this不应该与对象文字一起使用,因为它们可以被视为名称空间中的静态函数。 this应与使用.bind.call.apply或使用构造函数和new创建的实例调用的函数一起使用。据说它是受支持的,但是关闭不能完全理解发生了什么:How does "this" keyword work within a function?

我会更改代码以直接引用mui.utils以外的其他功能。

答案 2 :(得分:0)

代码中的

fade是对象的方法,而压缩版本则不是,因此对this的引用从方法的对象更改为全局对象。

解决这个问题的另一种方法是使用new function单例模式来实例化你的对象。

mui.utils.colorManipulator = new function() { 
    this.fade = function() { /*...*/ }
};

或者像jfriend00建议的那样,在方法中指定完整的命名空间,以便编译器不会丢失引用。