你认为每个程序员都应该知道JavaScript的哪些“隐藏特性”?

在看到以下问题的优质答案后,我认为是时候向JavaScript请求它了。

HTML的隐藏特性 CSS的隐藏特性 PHP的隐藏特性 ASP的隐藏特性。网 c#的隐藏特性 Java的隐藏特性 Python的隐藏特性

尽管JavaScript可以说是目前最重要的客户端语言(问问谷歌就知道了),但令人惊讶的是,大多数web开发人员很少意识到它的强大。


当前回答

你永远不需要使用eval()来组装全局变量名。

也就是说,如果你有几个名为spec_grapes, spec_apples的全局变量(无论出于什么原因),你不需要用eval("spec_" + var)访问它们。

所有全局变量都是window[]的成员,所以你可以执行window["spec_" + var]。

其他回答

确保在遍历对象属性时使用hasOwnProperty方法:

for (p in anObject) {
    if (anObject.hasOwnProperty(p)) {
        //Do stuff with p here
    }
}

这样做是为了只访问object的直接属性,而不使用原型链下面的属性。

我提交的第一个特性与其说是一个隐藏特性,不如说是一个很少使用的属性重定义特性应用程序。因为可以重新定义对象的方法,所以可以缓存方法调用的结果,这在计算开销很大且希望延迟求值的情况下非常有用。这是记忆的最简单形式。

function Circle(r) {
    this.setR(r);
}

Circle.prototype = {
  recalcArea: function() {
        this.area=function() {
            area = this.r * this.r * Math.PI;
            this.area = function() {return area;}
            return area;
        }
    },
  setR: function (r) {
      this.r = r;
      this.invalidateR();
    },
  invalidateR: function() {
        this.recalcArea();
    }
}

重构将结果缓存到方法中的代码,你会得到:

Object.prototype.cacheResult = function(name, _get) {
  this[name] = function() {
    var result = _get.apply(this, arguments);
    this[name] = function() {
      return result;
    }
    return result;
  };
};

function Circle(r) {
    this.setR(r);
}

Circle.prototype = {
  recalcArea: function() {
        this.cacheResult('area', function() { return this.r * this.r * Math.PI; });
    },
  setR: function (r) {
      this.r = r;
      this.invalidateR();
    },
  invalidateR: function() {
        this.recalcArea();
    }
}

如果你想要一个记忆函数,你可以用那个代替。不涉及属性重定义。

Object.prototype.memoize = function(name, implementation) {
    this[name] = function() {
        var argStr = Array.toString.call(arguments);
        if (typeof(this[name].memo[argStr]) == 'undefined') {
            this[name].memo[argStr] = implementation.apply(this, arguments);
        }
        return this[name].memo[argStr];
    }
};

注意,这依赖于标准数组toString转换,通常不能正常工作。解决这个问题留给读者练习。

第二个提交的是getter和setter。我很惊讶他们还没有被提及。因为官方标准不同于事实上的标准(defineProperty vs. define[GS]字母),而且Internet Explorer几乎不支持官方标准,所以它们通常没什么用。也许这就是他们没有被提及的原因。注意,你可以很好地结合getter和结果缓存:

Object.prototype.defineCacher = function(name, _get) {
    this.__defineGetter__(name, function() {
        var result = _get.call(this);
        this.__defineGetter__(name, function() { return result; });
        return result;
    })
};

function Circle(r) {
    this.r = r;
}

Circle.prototype = {
  invalidateR: function() {
        this.recalcArea();
    },
  recalcArea: function() {
        this.defineCacher('area', function() {return this.r * this.r * Math.PI; });
    },
  get r() { return this._r; }
  set r(r) { this._r = r; this.invalidateR(); }
}

var unit = new Circle(1);
unit.area;

有效地组合getter, setter和结果缓存有点混乱,因为你必须防止在set上自动失效或不自动失效,这就是下面的例子所做的。如果改变一个属性会使其他多个属性失效,这是一个主要的问题(假设在这些示例中有一个“diameter”属性)。

Object.prototype.defineRecalcer = function(name, _get) {
  var recalcFunc;
  this[recalcFunc='recalc'+name.toCapitalized()] = function() {
    this.defineCacher(name, _get);
  };
  this[recalcFunc]();
  this.__defineSetter__(name, function(value) {
      _set.call(this, value);
      this.__defineGetter__(name, function() {return value; });
  });
};

function Circle(r) {
    this.defineRecalcer('area',
             function() {return this.r * this.r * Math.PI;},
             function(area) {this._r = Math.sqrt(area / Math.PI);},
    );
    this.r = r;
}

Circle.prototype = {
  invalidateR: function() {
        this.recalcArea();
    },
  get r() { return this._r; }
  set r(r) { this._r = r; this.invalidateR(); }
}

大型循环在while-condition和反向条件下更快——也就是说,如果循环的顺序对您无关紧要的话。在我大约50%的代码中,它通常不存在。

即。

var i, len = 100000;

for (var i = 0; i < len; i++) {
  // do stuff
}

比:

i = len;
while (i--) {
  // do stuff
}

也许其中一个不太为人所知:

arguments. called .caller + Function#toString()

function called(){
    alert("Go called by:\n"+arguments.callee.caller.toString());
}

function iDoTheCall(){
    called();
}

iDoTheCall();

打印出idocall的源代码—— 已弃用,但有时在警报是您唯一的选择时可能有用....

闭包:

function f() { 
    var a; 
    function closureGet(){ return a; }
    function closureSet(val){ a=val;}
    return [closureGet,closureSet];
}

[closureGet,closureSet]=f(); 
closureSet(5);
alert(closureGet()); // gives 5

closureSet(15);
alert(closureGet()); // gives 15

这里的闭包并不是所谓的解构赋值([c,d] =[1,3]等价于c=1;d=3;)但事实上,在closureGet和closurereset中出现的a仍然指向同一个变量。即使在closureSet已经分配了一个新值之后!