你认为每个程序员都应该知道JavaScript的哪些“隐藏特性”?
在看到以下问题的优质答案后,我认为是时候向JavaScript请求它了。
HTML的隐藏特性 CSS的隐藏特性 PHP的隐藏特性 ASP的隐藏特性。网 c#的隐藏特性 Java的隐藏特性 Python的隐藏特性
尽管JavaScript可以说是目前最重要的客户端语言(问问谷歌就知道了),但令人惊讶的是,大多数web开发人员很少意识到它的强大。
你认为每个程序员都应该知道JavaScript的哪些“隐藏特性”?
在看到以下问题的优质答案后,我认为是时候向JavaScript请求它了。
HTML的隐藏特性 CSS的隐藏特性 PHP的隐藏特性 ASP的隐藏特性。网 c#的隐藏特性 Java的隐藏特性 Python的隐藏特性
尽管JavaScript可以说是目前最重要的客户端语言(问问谷歌就知道了),但令人惊讶的是,大多数web开发人员很少意识到它的强大。
当前回答
let.
与var缺乏块作用域对应的是let,在JavaScript 1.7中引入。
The let statement provides a way to associate values with variables within the scope of a block, without affecting the values of like-named variables outside the block. The let expression lets you establish variables scoped only to a single expression. The let definition defines variables whose scope is constrained to the block in which they're defined. This syntax is very much like the syntax used for var. You can also use let to establish variables that exist only within the context of a for loop.
function varTest() {
var x = 31;
if (true) {
var x = 71; // same variable!
alert(x); // 71
}
alert(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // different variable
alert(x); // 71
}
alert(x); // 31
}
截至2008年,FireFox 2.0+和Safari 3.x支持JavaScript 1.7。
其他回答
这里有一些捷径:
var a = []; // equivalent to new Array()
var o = {}; // equivalent to new Object()
你可以使用for in迭代数组
Mark Cidade指出了for in循环的用处:
// creating an object (the short way, to use it like a hashmap)
var diner = {
"fruit":"apple"
"veggetable"="bean"
}
// looping over its properties
for (meal_name in diner ) {
document.write(meal_name+"<br \n>");
}
结果:
fruit
veggetable
但还有更多。因为你可以使用关联数组这样的对象,你可以处理键和值, 就像foreach循环一样:
// looping over its properties and values
for (meal_name in diner ) {
document.write(meal_name+" : "+diner[meal_name]+"<br \n>");
}
结果:
fruit : apple
veggetable : bean
因为数组也是对象,你可以用同样的方法迭代其他数组:
var my_array = ['a', 'b', 'c'];
for (index in my_array ) {
document.write(index+" : "+my_array[index]+"<br \n>");
}
结果:
0 : a
1 : b
3 : c
可以很容易地从数组中删除一个已知元素
var arr = ['a', 'b', 'c', 'd'];
var pos = arr.indexOf('c');
pos > -1 && arr.splice( pos, 1 );
你可以很容易地打乱一个数组
arr.sort(function() Math.random() - 0.5);-不是真正的随机分布,见评论。
我提交的第一个特性与其说是一个隐藏特性,不如说是一个很少使用的属性重定义特性应用程序。因为可以重新定义对象的方法,所以可以缓存方法调用的结果,这在计算开销很大且希望延迟求值的情况下非常有用。这是记忆的最简单形式。
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(); }
}
这是jQuery的一个隐藏特性,而不是Javascript,但因为永远不会有“jQuery的隐藏特性”的问题……
你可以在jQuery中定义自己的:something选择器:
$.extend($.expr[':'], {
foo: function(node, index, args, stack) {
// decide if selectors matches node, return true or false
}
});
对于使用:foo的选择,例如$('div.block:foo("bar,baz") span'),函数foo将被用于匹配选择器中已经处理的部分的所有节点。论证的意义:
node holds the current node index is the index of the node in the node set args is an array that is useful if the selector has an argument or multiple names: args[0] is the whole selector text (e.g. :foo("bar, baz")) args[1] is the selector name (e.g. foo) args[2] is the quote character used to wrap the argument (e.g. " for :foo("bar, baz")) or an empty string if there is no quoting (:foo(bar, baz)) or undefined if there is no argument args[3] is the argument, including any quotes, (e.g. "bar, baz") or undefined if there are no arguments stack is the node set (an array holding all nodes which are matched at that point)
如果选择器匹配,函数将返回true,否则返回false。
例如,下面的代码将支持基于全文regexp搜索选择节点:
$.extend($.expr[':'], {
matches: function(node, index, args, stack) {
if (!args.re) { // args is a good place for caching
var re = args[3];
if (args[2]) { // get rid of quotes
re = re.slice(1,-1);
}
var separator = re[0];
var pos = re.lastIndexOf(separator);
var modifiers = re.substr(pos+1);
var code = re.substr(1, pos-1);
args.re = new RegExp(code, modifiers);
}
return $(node).text().match(args.re);
}
});
// find the answers on this page which contain /**/-style comments
$('.answer .post-text code:matches(!/\\*[\\s\\S]*\\*/!)');
使用.filter()的回调版本也可以达到类似的效果,但自定义选择器要灵活得多,通常可读性也更好。
这似乎只适用于Firefox (SpiderMonkey)。函数内部:
Arguments[-2]给出了参数的个数(与Arguments .length相同) Arguments[-3]给出了被调用的函数(与Arguments .callee相同)