您认为每个程序员应该知道哪些JavaScript的“隐藏功能”?
在看到以下问题的答案的优秀品质后,我认为是时候向它询问JavaScript了。
即使JavaScript现在可以说是最重要的客户端语言(只是问Google),但令人惊讶的是,大多数网页开发人员都很少理解它的实力。
答案 0 :(得分:373)
您无需为函数定义任何参数。您可以使用函数的arguments
类似数组的对象。
function sum() {
var retval = 0;
for (var i = 0, len = arguments.length; i < len; ++i) {
retval += arguments[i];
}
return retval;
}
sum(1, 2, 3) // returns 6
答案 1 :(得分:204)
我可以引用道格拉斯·克罗克福德大部分优秀的书 JavaScript: The Good Parts
但我只会为您提供一个,始终使用===
和!==
代替==
和!=
alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true
==
不具有传递性。如果你使用===
,它会给出错误
所有这些陈述都符合预期。
答案 2 :(得分:189)
函数是JavaScript中的一等公民:
var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };
var sum = function(x,y,z) {
return x+y+z;
};
alert( passFunAndApply(sum,3,4,5) ); // 12
Functional programming techniques can be used to write elegant javascript
特别是,函数可以作为参数传递,例如Array.filter()接受回调:
[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]
您还可以声明仅存在于特定函数范围内的“私有”函数:
function PrintName() {
var privateFunction = function() { return "Steve"; };
return privateFunction();
}
答案 3 :(得分:162)
您可以使用 in 运算符检查对象中是否存在密钥:
var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true
如果您发现对象文字太丑陋,可以将它与无参数函数提示结合起来:
function list()
{ var x = {};
for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
return x
}
5 in list(1,2,3,4,5) //true
答案 4 :(得分:153)
为变量分配默认值
您可以在赋值表达式中使用逻辑或运算符||
来提供默认值:
var a = b || c;
a
变量仅在c
为 falsy 时才会获得b
的值(如果是null
,false
,undefined
,0
,empty string
或NaN
),否则a
将获得b
的值。
如果你想在没有提供的情况下为参数提供默认值,这在函数中通常很有用:
function example(arg1) {
arg1 || (arg1 = 'default value');
}
事件处理程序中的IE回退示例:
function onClick(e) {
e || (e = window.event);
}
以下语言功能已经使用了很长时间,所有JavaScript实现都支持它们,但在ECMAScript 5th Edition之前它们不是规范的一部分:
debugger
声明
描述于:§12.15调试器声明
此语句允许您通过以下方式在代码中断点:
// ...
debugger;
// ...
如果调试器存在或处于活动状态,则会导致它立即在该行上断开。
否则,如果调试器不存在或处于活动状态,则此语句没有可观察到的影响。
多行字符串文字
描述于:§7.8.4字符串文字
var str = "This is a \
really, really \
long line!";
你必须要小心,因为\
旁边的字符必须是行终止符,如果你在\
之后有空格,代码将看起来完全相同,但它会引发SyntaxError
。
答案 5 :(得分:145)
JavaScript does not have block scope(但它有closure所以我们甚至可以称之为?)。
var x = 1;
{
var x = 2;
}
alert(x); // outputs 2
答案 6 :(得分:144)
您可以使用[]
代替.
这允许您查找与变量匹配的属性。
obj = {a:"test"};
var propname = "a";
var b = obj[propname]; // "test"
您也可以使用它来获取/设置名称不是合法标识符的对象属性。
obj["class"] = "test"; // class is a reserved word; obj.class would be illegal.
obj["two words"] = "test2"; // using dot operator not possible with the space.
有些人不知道这一点并最终使用 eval()这样,这是非常糟糕的主意:
var propname = "a";
var a = eval("obj." + propname);
这很难阅读,更难找到错误(不能使用jslint),执行速度较慢,并且可能导致XSS攻击。
答案 7 :(得分:144)
如果您正在使用Google搜索针对特定主题的合适JavaScript引用,请在查询中包含“mdc”关键字,并且您的第一批结果将来自Mozilla开发人员中心。我没有随身携带任何离线参考书或书籍。我总是使用“mdc”关键字技巧来直接找到我正在寻找的东西。例如:
Google:javascript array sort mdc
(在大多数情况下,您可以省略“javascript”)
更新:Mozilla开发人员中心已重命名为Mozilla开发人员网络。 “mdc”关键字技巧仍然有效,但很快我们可能必须开始使用“mdn”而不是。
答案 8 :(得分:143)
对某些人来说可能有点明显......
安装Firebug并使用console.log(“hello”)。比使用随机警报()更好,这是我记得几年前做的很多。
答案 9 :(得分:120)
隐私方法
对象可以有私有方法。
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
// A private method only visible from within this constructor
function calcFullName() {
return firstName + " " + lastName;
}
// A public method available to everyone
this.sayHello = function () {
alert(calcFullName());
}
}
//Usage:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();
// This fails since the method is not visible from this scope
alert(person1.calcFullName());
答案 10 :(得分:99)
在Crockford的“Javascript:The Good Parts”中也提到过:
parseInt()
很危险。如果你传递一个字符串而没有通知它正确的基数,它可能会返回意外的数字。例如,parseInt('010')
返回8而不是10.将基数传递给parseInt使其正常工作:
parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.
答案 11 :(得分:97)
函数是对象,因此可以具有属性。
fn = function(x) { // ... } fn.foo = 1; fn.next = function(y) { // }
答案 12 :(得分:91)
我不得不说自动执行功能。
(function() { alert("hi there");})();
因为Javascript doesn't have block scope,如果要定义局部变量,可以使用自执行函数:
(function() {
var myvar = 2;
alert(myvar);
})();
此处,myvar
不会干扰或污染全局范围,并在函数终止时消失。
答案 13 :(得分:83)
了解函数预期的参数数量
function add_nums(num1, num2, num3 ){
return num1 + num2 + num3;
}
add_nums.length // 3 is the number of parameters expected.
知道函数接收了多少参数
function add_many_nums(){
return arguments.length;
}
add_many_nums(2,1,122,12,21,89); //returns 6
答案 14 :(得分:79)
以下是一些有趣的事情:
NaN
与任何内容(甚至NaN
)进行比较始终为false,其中包括==
,<
和>
。NaN
代表不是数字,但如果你要求的类型,它实际上会返回一个数字。Array.sort
可以使用比较器功能,并由类似快速排序的驱动程序调用(取决于实现)。$0
,$1
,$2
成员。null
与众不同。它既不是对象,也不是布尔值,数字,字符串,也不是undefined
。这有点像“替代”undefined
。 (注:typeof null == "object"
)this
会生成其他不可命名的[Global]对象。var
声明变量,而不仅仅依赖于变量的自动声明,使运行时真正有机会优化对该变量的访问with
构造将破坏此类优化break
的目标。可以标记循环并将其用作continue
。undefined
填充它。 (取决于实施)if (new Boolean(false)) {...}
将执行{...}
块[稍微更新以回应好评;请参阅评论]
答案 15 :(得分:77)
我知道我迟到了,但我无法相信+
运营商的用处并没有被提及,除了“将任何东西转换为数字”之外。也许这是隐藏一个功能的好坏吗?
// Quick hex to dec conversion:
+"0xFF"; // -> 255
// Get a timestamp for now, the equivalent of `new Date().getTime()`:
+new Date();
// Safer parsing than parseFloat()/parseInt()
parseInt("1,000"); // -> 1, not 1000
+"1,000"; // -> NaN, much better for testing user input
parseInt("010"); // -> 8, because of the octal literal prefix
+"010"; // -> 10, `Number()` doesn't parse octal literals
// A use case for this would be rare, but still useful in cases
// for shortening something like if (someVar === null) someVar = 0;
+null; // -> 0;
// Boolean to integer
+true; // -> 1;
+false; // -> 0;
// Other useful tidbits:
+"1e10"; // -> 10000000000
+"1e-4"; // -> 0.0001
+"-12"; // -> -12
当然,您可以使用Number()
来完成所有这些操作,但+
运算符非常漂亮!
您还可以通过覆盖原型的valueOf()
方法来定义对象的数字返回值。对该对象执行的任何数字转换都不会产生NaN
,而是valueOf()
方法的返回值:
var rnd = {
"valueOf": function () { return Math.floor(Math.random()*1000); }
};
+rnd; // -> 442;
+rnd; // -> 727;
+rnd; // -> 718;
答案 16 :(得分:75)
“Extension methods in JavaScript”通过原型属性。
Array.prototype.contains = function(value) {
for (var i = 0; i < this.length; i++) {
if (this[i] == value) return true;
}
return false;
}
这将为所有contains
个对象添加Array
方法。您可以使用此语法
var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");
答案 17 :(得分:60)
要从对象中正确删除属性,您应该删除该属性,而不是仅将其设置为 undefined :
var obj = { prop1: 42, prop2: 43 };
obj.prop2 = undefined;
for (var key in obj) {
...
属性 prop2 仍然是迭代的一部分。如果你想彻底摆脱 prop2 ,你应该做:
delete obj.prop2;
在迭代属性时, prop2 属性将不再出现。
答案 18 :(得分:57)
<强> with
即可。
它很少使用,坦率地说,很少有用......但是,在有限的情况下,它确实有其用途。
例如:对象文字对于在 new 对象上快速设置属性非常方便。但是,如果您需要更改现有对象的 half 属性,该怎么办?
var user =
{
fname: 'Rocket',
mname: 'Aloysus',
lname: 'Squirrel',
city: 'Fresno',
state: 'California'
};
// ...
with (user)
{
mname = 'J';
city = 'Frostbite Falls';
state = 'Minnesota';
}
Alan Storm指出这可能有些危险:如果用作上下文的对象没有分配给其中一个属性,它将在外部范围内解析,可能会创建或覆盖全局变量。如果您习惯于编写代码来处理具有默认值或空值的属性未定义的对象,那么这将特别危险:
var user =
{
fname: "John",
// mname definition skipped - no middle name
lname: "Doe"
};
with (user)
{
mname = "Q"; // creates / modifies global variable "mname"
}
因此,避免使用with
语句进行此类分配可能是个好主意。
答案 19 :(得分:51)
可以在对象上调用方法(或函数),这些对象不是它们设计使用的类型。这很适合在自定义对象上调用本机(快速)方法。
var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });
此代码崩溃,因为listNodes
不是Array
Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);
此代码有效,因为listNodes
定义了sort()
使用的足够类似数组的属性(length,[]运算符)。
答案 20 :(得分:43)
原型继承(由道格拉斯·克罗克福德推广)彻底改变了你在Javascript中思考大量内容的方式。
Object.beget = (function(Function){
return function(Object){
Function.prototype = Object;
return new Function;
}
})(function(){});
这是一个杀手!可惜几乎没有人使用它。
它允许您“生成”任何对象的新实例,扩展它们,同时保持(实时)原型继承链接到其他属性。例如:
var A = {
foo : 'greetings'
};
var B = Object.beget(A);
alert(B.foo); // 'greetings'
// changes and additionns to A are reflected in B
A.foo = 'hello';
alert(B.foo); // 'hello'
A.bar = 'world';
alert(B.bar); // 'world'
// ...but not the other way around
B.foo = 'wazzap';
alert(A.foo); // 'hello'
B.bar = 'universe';
alert(A.bar); // 'world'
答案 21 :(得分:42)
有些人会称之为品味,但是:
aWizz = wizz || "default";
// same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }
三元运算符可以被链接起来像Scheme(cond ......):
(cond (predicate (action ...))
(predicate2 (action2 ...))
(#t default ))
可以写成......
predicate ? action( ... ) :
predicate2 ? action2( ... ) :
default;
这非常“实用”,因为它可以在没有副作用的情况下对代码进行分支。所以而不是:
if (predicate) {
foo = "one";
} else if (predicate2) {
foo = "two";
} else {
foo = "default";
}
你可以写:
foo = predicate ? "one" :
predicate2 ? "two" :
"default";
也适用于递归:)
答案 22 :(得分:41)
数字也是对象。所以你可以做一些很酷的事情:
// convert to base 2
(5).toString(2) // returns "101"
// provide built in iteration
Number.prototype.times = function(funct){
if(typeof funct === 'function') {
for(var i = 0;i < Math.floor(this);i++) {
funct(i);
}
}
return this;
}
(5).times(function(i){
string += i+" ";
});
// string now equals "0 1 2 3 4 "
var x = 1000;
x.times(function(i){
document.body.innerHTML += '<p>paragraph #'+i+'</p>';
});
// adds 1000 parapraphs to the document
答案 23 :(得分:33)
JavaScript中的闭包怎么样(类似于C#v2.0 +中的匿名方法)。您可以创建一个创建函数或“表达式”的函数。
关闭的示例:
//Takes a function that filters numbers and calls the function on
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
var filteredNumbers = [];
for (var index = 0; index < numbers.length; index++)
{
if (filterFunction(numbers[index]) == true)
{
filteredNumbers.push(numbers[index]);
}
}
return filteredNumbers;
}
//Creates a function (closure) that will remember the value "lowerBound"
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
return function (numberToCheck) {
return (numberToCheck > lowerBound) ? true : false;
};
}
var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];
var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);
numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);
numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);
答案 24 :(得分:32)
您还可以使用原型链 spoon16 扩展(继承)类并覆盖属性/方法。
在下面的示例中,我们创建了一个Pet类并定义了一些属性。我们还覆盖从Object继承的.toString()方法。
在此之后我们创建一个Dog类,扩展Pet并重写.toString()方法再次改变它的行为(多态)。此外,我们还为子类添加了一些其他属性。
在此之后,我们检查继承链以显示Dog仍然是Dog类型,类型为Pet,类型为Object。
// Defines a Pet class constructor
function Pet(name)
{
this.getName = function() { return name; };
this.setName = function(newName) { name = newName; };
}
// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function()
{
return 'This pets name is: ' + this.getName();
};
// end of class Pet
// Define Dog class constructor (Dog : Pet)
function Dog(name, breed)
{
// think Dog : base(name)
Pet.call(this, name);
this.getBreed = function() { return breed; };
}
// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();
// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;
// Now we override Pet.prototype.toString
Dog.prototype.toString = function()
{
return 'This dogs name is: ' + this.getName() +
', and its breed is: ' + this.getBreed();
};
// end of class Dog
var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);
// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dog instance of Object? ' + (dog instanceof Object)); //true
这个问题的答案都是从great MSDN article by Ray Djajadinata.
修改的代码答案 25 :(得分:31)
您可能会根据其类型捕获异常。引自MDC:
try {
myroutine(); // may throw three exceptions
} catch (e if e instanceof TypeError) {
// statements to handle TypeError exceptions
} catch (e if e instanceof RangeError) {
// statements to handle RangeError exceptions
} catch (e if e instanceof EvalError) {
// statements to handle EvalError exceptions
} catch (e) {
// statements to handle any unspecified exceptions
logMyErrors(e); // pass exception object to error handler
}
注意:条件catch子句是Netscape(因此也就是Mozilla / Firefox)扩展,它不属于ECMAScript规范,因此除特定浏览器外不能依赖它。
答案 26 :(得分:31)
脱离我的头顶......
<强>功能强>
arguments.callee是指托管“arguments”变量的函数,因此它可用于递归匿名函数:
var recurse = function() {
if (condition) arguments.callee(); //calls recurse() again
}
如果你想做这样的事情,这很有用:
//do something to all array items within an array recursively
myArray.forEach(function(item) {
if (item instanceof Array) item.forEach(arguments.callee)
else {/*...*/}
})
<强>物件强>
关于对象成员的有趣之处:它们可以有任何字符串作为其名称:
//these are normal object members
var obj = {
a : function() {},
b : function() {}
}
//but we can do this too
var rules = {
".layout .widget" : function(element) {},
"a[href]" : function(element) {}
}
/*
this snippet searches the page for elements that
match the CSS selectors and applies the respective function to them:
*/
for (var item in rules) {
var elements = document.querySelectorAll(rules[item]);
for (var e, i = 0; e = elements[i++];) rules[item](e);
}
<强>字符串强>
String.split 可以将正则表达式作为参数:
"hello world with spaces".split(/\s+/g);
//returns an array: ["hello", "world", "with", "spaces"]
String.replace 可以将正则表达式作为搜索参数,将函数作为替换参数:
var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"
答案 27 :(得分:29)
您可以在大多数时间使用对象而不是切换。
function getInnerText(o){
return o === null? null : {
string: o,
array: o.map(getInnerText).join(""),
object:getInnerText(o["childNodes"])
}[typeis(o)];
}
更新:如果您担心事先评估的案例效率低下(为什么您在设计程序的早期担心效率?)那么您可以这样做:
function getInnerText(o){
return o === null? null : {
string: function() { return o;},
array: function() { return o.map(getInnerText).join(""); },
object: function () { return getInnerText(o["childNodes"]; ) }
}[typeis(o)]();
}
键入(或读取)比使用开关或对象更加繁琐,但它保留了使用对象而不是开关的好处,详见下面的注释部分。这种风格也可以让它更容易将它变成一个适当的“阶级”,一旦它长大了。
update2:对于ES.next的建议语法扩展,这变为
let getInnerText = o -> ({
string: o -> o,
array: o -> o.map(getInnerText).join(""),
object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);
答案 28 :(得分:25)
在迭代对象的属性时,请务必使用 hasOwnProperty 方法:
for (p in anObject) {
if (anObject.hasOwnProperty(p)) {
//Do stuff with p here
}
}
这样做是为了只访问 anObject 的直接属性,而不是使用原型链下的属性。
答案 29 :(得分:23)
具有公共接口的私有变量
它使用一个带有自调用函数定义的简洁小技巧。 返回的对象内的所有内容都可以在公共界面中使用,而其他所有内容都是私有的。
var test = function () {
//private members
var x = 1;
var y = function () {
return x * 2;
};
//public interface
return {
setx : function (newx) {
x = newx;
},
gety : function () {
return y();
}
}
}();
assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());
答案 30 :(得分:21)
JavaScript中的时间戳:
// Usual Way
var d = new Date();
timestamp = d.getTime();
// Shorter Way
timestamp = (new Date()).getTime();
// Shortest Way
timestamp = +new Date();
答案 31 :(得分:20)
您可以使用左侧的[]指定局部变量。如果你想从一个函数返回多个值而不创建一个不必要的数组,它会派上用场。
function fn(){
var cat = "meow";
var dog = "woof";
return [cat,dog];
};
var [cat,dog] = fn(); // Handy!
alert(cat);
alert(dog);
它是核心JS的一部分,但不知怎的,直到今年我才意识到。
答案 32 :(得分:19)
Javascript中的所有对象都实现为哈希表,因此可以通过索引器访问它们的属性,反之亦然。此外,您可以使用 for / in 运算符枚举所有属性:
var x = {a: 0};
x["a"]; //returns 0
x["b"] = 1;
x.b; //returns 1
for (p in x) document.write(p+";"); //writes "a;b;"
答案 33 :(得分:17)
如果要从数组中删除元素,可以使用 删除 运算符,如下所示:
var numbers = [1,2,3,4,5];
delete numbers[3];
//numbers is now [1,2,3,undefined,5]
如您所见,该元素已被删除,但由于该元素已替换为 undefined 值,因此在数组中留下了一个洞。
因此,要解决此问题,请使用 splice 数组方法,而不是使用 delete ,因此:
var numbers = [1,2,3,4,5];
numbers.splice(3,1);
//numbers is now [1,2,3,5]
splice 的第一个参数是数组[index]中的序数,第二个是要删除的元素数。
答案 34 :(得分:17)
这个帖子中有几个答案显示如何
通过其原型扩展Array对象。这是一个坏事
IDEA,因为它打破了for (i in a)
声明。
如果您没有碰巧使用for (i in a)
,那也没关系
你代码中的任何地方?好吧,只有你自己的代码是
只有您正在运行的代码,这不太可能
在浏览器中。我担心,如果人们开始扩展
像这样的Array对象,Stack Overflow将启动
充满了一堆神秘的JavaScript错误。
查看有用的详细信息here。
答案 35 :(得分:16)
在函数中,您可以返回函数本身:
function showSomething(a){
alert(a);
return arguments.callee;
}
// Alerts: 'a', 'b', 'c'
showSomething('a')('b')('c');
// Or what about this:
(function (a){
alert(a);
return arguments.callee;
})('a')('b')('c');
我不知道什么时候它可能有用,无论如何,它非常奇怪和有趣:
var count = function(counter){
alert(counter);
if(counter < 10){
return arguments.callee(counter+1);
}
return arguments.callee;
};
count(5)(9); // Will alert 5, 6, 7, 8, 9, 10 and 9, 10
实际上,Node.js的FAB framework似乎已经实现了这个功能;例如,请参阅this topic。
答案 36 :(得分:15)
JavaScript使用Date()的方式让我感到兴奋!
function isLeapYear(year) {
return (new Date(year, 1, 29, 0, 0).getMonth() != 2);
}
这实际上是“隐藏的功能”。
编辑:删除“?” pollecorrecteness评论中建议的条件。 是:...新日期(年,1月29日,0日,0日).getMonth()!= 2?真假 ... 请查看评论以获取详细信息。
答案 37 :(得分:13)
以下是一些快捷方式:
var a = []; // equivalent to new Array()
var o = {}; // equivalent to new Object()
答案 38 :(得分:13)
关闭之禅
其他人提到过闭包。但令人惊讶的是,有多少人知道闭包,使用闭包编写代码,但仍然对闭包真正有错误的看法。有些人将第一类函数与闭包混淆。还有一些人认为它是一种静态变量。
对我而言,闭包是一种'私有'全局变量。也就是说,一种变量,某些函数看起来像全局但其他函数看不到。现在,我知道这对于底层机制的描述是快速而松散的,但这就是它的感觉和行为。举例说明:
// Say you want three functions to share a single variable:
// Use a self-calling function to create scope:
(function(){
var counter = 0; // this is the variable we want to share;
// Declare global functions using function expressions:
increment = function(){
return ++counter;
}
decrement = function(){
return --counter;
}
value = function(){
return counter;
}
})()
现在,三个函数increment
,decrement
和value
共享变量counter
,而counter
不是实际的全局变量。这是封闭的真正本质:
increment();
increment();
decrement();
alert(value()); // will output 1
以上对闭包的使用并不是很有用。事实上,我会说以这种方式使用它是一种反模式。但它有助于理解闭包的本质。例如,大多数人在尝试执行以下操作时会被抓住:
for (var i=1;i<=10;i++) {
document.getElementById('span'+i).onclick = function () {
alert('this is span number '+i);
}
}
// ALL spans will generate alert: this span is span number 10
那是因为他们不了解封闭的本质。当事实上函数共享一个变量i
时,他们认为他们将i
的值传递给函数。就像我之前说过的,一种特殊的全局变量。
要解决此问题,您需要分离 *闭包:
function makeClickHandler (j) {
return function () {alert('this is span number '+j)};
}
for (var i=1;i<=10;i++) {
document.getElementById('span'+i).onclick = makeClickHandler(i);
}
// this works because i is passed by reference
// (or value in this case, since it is a number)
// instead of being captured by a closure
*注意:我不知道这里的正确术语。
答案 39 :(得分:13)
我最喜欢的技巧是使用apply
来执行对象方法的回调并维护正确的“this”变量。
function MakeCallback(obj, method) {
return function() {
method.apply(obj, arguments);
};
}
var SomeClass = function() {
this.a = 1;
};
SomeClass.prototype.addXToA = function(x) {
this.a = this.a + x;
};
var myObj = new SomeClass();
brokenCallback = myObj.addXToA;
brokenCallback(1); // Won't work, wrong "this" variable
alert(myObj.a); // 1
var myCallback = MakeCallback(myObj, myObj.addXToA);
myCallback(1); // Works as expected because of apply
alert(myObj.a); // 2
答案 40 :(得分:12)
您永远不必使用eval()
来汇编全局变量名。
也就是说,如果您有几个名为spec_grapes, spec_apples
的全局变量(无论出于何种原因),则不必使用eval("spec_" + var)
访问它们。
所有全局变量都是window[]
的成员,因此您可以window["spec_" + var]
。
答案 41 :(得分:11)
JavaScript使用简单的对象文字:
var x = { intValue: 5, strValue: "foo" };
这构造了一个完整的对象。
JavaScript使用基于原型的面向对象,并提供在运行时扩展类型的能力:
String.prototype.doubleLength = function() {
return this.length * 2;
}
alert("foo".doubleLength());
对象将对其不包含自身的属性的所有访问权委托给其“原型”,即另一个对象。这可以用来实现继承,但实际上更强大(即使更麻烦):
/* "Constructor" */
function foo() {
this.intValue = 5;
}
/* Create the prototype that includes everything
* common to all objects created be the foo function.
*/
foo.prototype = {
method: function() {
alert(this.intValue);
}
}
var f = new foo();
f.method();
答案 42 :(得分:11)
在对Firebug使用console.log()时,防止在Internet Explorer中进行测试时出现恼人的错误:
function log(message) {
(console || { log: function(s) { alert(s); }).log(message);
}
答案 43 :(得分:10)
JavaScript中最快的循环是while(i--)。在所有浏览器中。 因此,如果对循环元素的处理顺序并不重要,那么你应该使用while(i - )形式:
var names = new Array(1024), i = names.length;
while(i--)
names[i] = "John" + i;
此外,如果你必须使用for()循环继续,请记住始终缓存.length属性:
var birds = new Array(1024);
for(var i = 0, j = birds.length; i < j; i++)
birds[i].fly();
要加入大字符串,请使用Arrays(它更快):
var largeString = new Array(1024), i = largeString.length;
while(i--) {
// It's faster than for() loop with largeString.push(), obviously :)
largeString[i] = i.toString(16);
}
largeString = largeString.join("");
它比循环中的largeString += "something"
快得多。
答案 44 :(得分:10)
我最喜欢的一个是构造函数类型检查:
function getObjectType( obj ) {
return obj.constructor.name;
}
window.onload = function() {
alert( getObjectType( "Hello World!" ) );
function Cat() {
// some code here...
}
alert( getObjectType( new Cat() ) );
}
因此,您经常使用typeof关键字来获取疲惫的旧[Object对象],而实际上可以根据构造函数获取真实的对象类型。
另一个是使用变量参数作为“重载”函数的方法。您所做的只是使用表达式来检测参数的数量并返回重载的输出:
function myFunction( message, iteration ) {
if ( arguments.length == 2 ) {
for ( i = 0; i < iteration; i++ ) {
alert( message );
}
} else {
alert( message );
}
}
window.onload = function() {
myFunction( "Hello World!", 3 );
}
最后,我会说赋值运算符的简写。我从jQuery框架的源代码中学到了这一点......旧方法:
var a, b, c, d;
b = a;
c = b;
d = c;
新的(速记)方式:
var a, b, c, d;
d = c = b = a;
好玩:)
答案 45 :(得分:10)
如果用逗号分隔语句,则几乎可以在括号之间做任何事情:
var z = ( x = "can you do crazy things with parenthesis", ( y = x.split(" "), [ y[1], y[0] ].concat( y.slice(2) ) ).join(" ") )
alert(x + "\n" + y + "\n" + z)
输出:
can you do crazy things with parenthesis
can,you,do,crazy,things,with,parenthesis
you can do crazy things with parenthesis
答案 46 :(得分:9)
truthy和falsy值的概念。你不需要做像
这样的事情if(someVar === undefined || someVar === null)...
简单地说:
如果(!someVar)。
每个值都有一个相应的布尔表示。
答案 47 :(得分:9)
window.name
的值在页面更改后仍然存在,如果在同一个域中(如果在iframe中,使用document.getElementById("your frame's ID").contentWindow.name
访问它),则父窗口可以读取该值,并且仅受可用限制存储器中。
答案 48 :(得分:9)
创建新的“对象”时,括号是可选的。
function Animal () {
}
var animal = new Animal();
var animal = new Animal;
同样的事情。
答案 49 :(得分:9)
函数语句和函数表达式的处理方式不同。
function blarg(a) {return a;} // statement
bleep = function(b) {return b;} //expression
在运行代码之前解析所有函数语句 - 第一个语句中将提供JavaScript文件底部的函数。另一方面,它将无法利用某些动态上下文,例如周围的with
语句 - 解析函数时尚未执行with
。
函数表达式在遇到它们的地方执行内联。在此之前它们不可用,但它们可以利用动态环境。
答案 50 :(得分:8)
如果您正在尝试沙箱javascript代码,并禁用每种可能的方法来将字符串评估为javascript代码,请注意阻塞所有明显的eval / document.write / new Function / setTimeout / setInterval / innerHTML和其他DOM操作还不够。
给定任何对象o,o.constructor.constructor("alert('hi')")()
将显示一个警告对话框,其中包含“hi”字样。
您可以将其重写为
var Z="constructor";
Z[Z][Z]("alert('hi')")();
有趣的东西。
答案 51 :(得分:8)
如果您盲目eval()
要对其进行反序列化的JSON字符串,则可能会遇到问题:
如果未将JSON字符串括在括号中,则属性名称可能会被误认为是标签,从而导致意外行为或语法错误:
eval("{ \"foo\": 42 }"); // syntax error: invalid label
eval("({ \"foo\": 42 })"); // OK
答案 52 :(得分:8)
你可以将“任何具有整数属性的对象和长度属性转换为适当的数组,从而赋予它所有数组方法,如push,pop,splice,map,filter,reduce等。
Array.prototype.slice.call({"0":"foo", "1":"bar", 2:"baz", "length":3 })
//返回[“foo”,“bar”,“baz”]
这适用于jQuery对象,html集合和来自其他框架的Array对象(作为整个数组类型事物的一种可能解决方案)。我说,如果它有一个长度属性,你可以把它变成一个数组,这没关系。除了参数对象之外,还有许多具有length属性的非数组对象。
答案 53 :(得分:8)
Javascript在函数内部有静态变量:
function someFunction(){
var Static = arguments.callee;
Static.someStaticVariable = (Static.someStaticVariable || 0) + 1;
alert(Static.someStaticVariable);
}
someFunction() //Alerts 1
someFunction() //Alerts 2
someFunction() //Alerts 3
它还包含Objects中的静态变量:
function Obj(){
this.Static = arguments.callee;
}
a = new Obj();
a.Static.name = "a";
b = new Obj();
alert(b.Static.name); //Alerts b
答案 54 :(得分:8)
let
。
var's lack of block-scoping的对方是let
,introduced in JavaScript 1.7。
- let语句提供了一种将值与变量相关联的方法 在一个块的范围内,没有 影响同名的值 块外的变量。
- let表达式允许您创建仅限于a的变量 单一表达。
- let定义定义范围受限制的变量 到他们定义的块。 这种语法非常类似于 用于var。
的语法- 您还可以使用let来建立仅存在于其中的变量 for循环的上下文。
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。
答案 55 :(得分:8)
==运算符有一个非常特殊的属性,会产生这种令人不安的平等(是的,我知道在Perl这样的其他动态语言中会出现这种行为,但JavaScript通常不会在比较中变得聪明):
>>> 1 == true
true
>>> 0 == false
true
>>> 2 == true
false
答案 56 :(得分:8)
所有函数实际上都是内置 Function 类型的实例,它有一个构造函数,它接受包含函数定义的字符串,因此您可以在运行时实际定义函数,例如,连接字符串:
//e.g., createAddFunction("a","b") returns function(a,b) { return a+b; }
function createAddFunction(paramName1, paramName2)
{ return new Function( paramName1, paramName2
,"return "+ paramName1 +" + "+ paramName2 +";");
}
此外,对于用户定义的函数, Function.toString()将函数定义作为文字字符串返回。
答案 57 :(得分:8)
您可以对任何对象执行对象的方法,无论它是否具有该方法。当然它可能并不总是有效(如果该方法假定对象具有它没有的东西),但它可能非常有用。例如:
function(){
arguments.push('foo') // This errors, arguments is not a proper array and has no push method
Array.prototype.push.apply(arguments, ['foo']) // Works!
}
答案 58 :(得分:7)
微软gift to JavaScript:AJAX
AJAXCall('http://www.abcd.com/')
function AJAXCall(url) {
var client = new XMLHttpRequest();
client.onreadystatechange = handlerFunc;
client.open("GET", url);
client.send();
}
function handlerFunc() {
if(this.readyState == 4 && this.status == 200) {
if(this.responseXML != null)
document.write(this.responseXML)
}
}
答案 59 :(得分:7)
<script type="text/javascript">
(function() {
function init() {
// ...
}
window.onload = init;
})();
</script>
在没有var
语句或函数外部声明的变量和函数将在全局范围内定义。如果已经存在同名的变量/函数,它将被静默覆盖,这可能导致很难找到错误。一个常见的解决方案是将整个代码体包装成匿名函数并立即执行它。这样,所有变量/函数都在匿名函数的范围内定义,不会泄漏到全局范围。
要在全局范围内显式定义变量/函数,必须以window
为前缀:
window.GLOBAL_VAR = 12;
window.global_function = function() {};
答案 60 :(得分:7)
这是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
拥有当前节点index
是节点集args
是一个数组,如果选择器有一个参数或多个名称,这个数组很有用:
args[0]
是整个选择器文字(例如:foo("bar, baz")
)args[1]
是选择器名称(例如foo
)args[2]
是用于包装参数的引号字符
(例如"
表示:foo("bar, baz")
)或空字符串(如果没有引号)
(:foo(bar, baz)
)或如果没有参数则未定义args[3]
是参数,包括任何引号(例如"bar, baz"
)
如果没有参数,则为undefined stack
是节点集(包含所有在该点匹配的节点的数组)如果选择器匹配,则函数应返回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()的回调版本达到类似的效果,但自定义选择器更灵活,通常更具可读性。
答案 61 :(得分:7)
Function.toString()(隐式):
function x() {
alert("Hello World");
}
eval ("x = " + (x + "").replace(
'Hello World',
'STACK OVERFLOW BWAHAHA"); x("'));
x();
答案 62 :(得分:6)
undefined
未定义。所以你可以这样做:
if (obj.field === undefined) /* ... */
答案 63 :(得分:6)
Generators and Iterators(仅适用于Firefox 2+和Safari)。
function fib() {
var i = 0, j = 1;
while (true) {
yield i;
var t = i;
i = j;
j += t;
}
}
var g = fib();
for (var i = 0; i < 10; i++) {
document.write(g.next() + "<br>\n");
}
包含
yield
的函数 关键字是一个生成器。你打电话的时候 它,它的形式参数是必然的 实际的论点,但它的身体不是 实际评估。相反,一个 返回generator-iterator。每 调用generator-iteratornext()
方法执行另一个传递 通过迭代算法。每 step的值是指定的值yield
关键字。将yield
视为 生成器 - 迭代器版本的 返回,表示边界 每次迭代之间 算法。每次拨打next()
时, 生成器代码从中恢复yield
之后的陈述。在正常使用中,迭代器对象是 “无形”;你不需要操作 在他们明确,但将改为 使用JavaScript的
for...in
和for each...in
语句自然循环 超过键和/或值 对象。
var objectWithIterator = getObjectSomehow();
for (var i in objectWithIterator)
{
document.write(objectWithIterator[i] + "<br>\n");
}
答案 64 :(得分:6)
访问:
将此JavaScript代码粘贴到网络浏览器的地址栏中:
享受JavaScript迪斯科节目:-p
答案 65 :(得分:5)
所有“隐藏”功能都在Mozilla wiki上:http://developer.mozilla.org/en/JavaScript。
有the core JavaScript 1.5 reference,what's new in JavaScript 1.6,什么是new in JavaScript 1.7,还有new in JavaScript 1.8。查看所有这些实际工作的示例并且不错误。
答案 66 :(得分:5)
<强> Namespaces 强>
在较大的JavaScript应用程序或框架中,组织命名空间中的代码会很有用。 JavaScript没有模块或命名空间概念buildin,但很容易使用JavaScript对象进行模拟。这将创建一个名为ns
的命名空间,并将函数foo
附加到它。
if (!window.ns) {
window.ns = {};
}
window.ns.foo = function() {};
在整个项目中使用相同的全局名称空间前缀并为每个JavaScript文件使用子名称空间是很常见的。子命名空间的名称通常与文件名相匹配。
名为ns/button.js
的文件的标题可能如下所示:
if (!window.ns) {
window.ns = {};
}
if (!window.ns.button) {
window.ns.button = {};
}
// attach methods to the ns.button namespace
window.ns.button.create = function() {};
答案 67 :(得分:5)
这个是超级隐藏的,只是偶尔有用; - )
您可以使用原型链创建一个委托给另一个对象的对象,而无需更改原始对象。
var o1 = { foo: 1, bar: 'abc' };
function f() {}
f.prototype = o1;
o2 = new f();
assert( o2.foo === 1 );
assert( o2.bar === 'abc' );
o2.foo = 2;
o2.baz = true;
assert( o2.foo === 2 );
// o1 is unchanged by assignment to o2
assert( o1.foo === 1 );
assert( o2.baz );
这仅涵盖o1上的“简单”值。如果修改数组或其他对象,则原型不再“保护”原始对象。当你在类定义/原型中有{}或[]时,请注意。
答案 68 :(得分:4)
令人惊讶的是,有多少人没有意识到它也是面向对象的。
答案 69 :(得分:4)
这些总是一个好主意,但你可以用简洁的表达式转换大多数东西。这里重要的一点是,并非JavaScript中的每个值都是一个对象,因此这些表达式将成功,对非null和undefined等非对象的成员访问将失败。特别要注意typeof null ==“object”,但你不能null.toString()或(“name”为null)。
将任何内容转换为数字:
+anything
Number(anything)
将任何内容转换为无符号的四字节整数:
anything >>> 0
将任何内容转换为字符串:
'' + anything
String(anything)
将任何内容转换为布尔值:
!!anything
Boolean(anything)
此外,使用不带“new”的类型名称对String,Number和Boolean的行为有所不同,返回原始数字,字符串或布尔值,但是使用“new”这些将返回“boxed”对象类型,这是几乎没用。
答案 70 :(得分:4)
while
中的大循环更快 - 条件和向后 - 也就是说,如果循环的顺序对您无关紧要。在大约50%的代码中,它通常不会。
即。
var i, len = 100000;
for (var i = 0; i < len; i++) {
// do stuff
}
慢于:
i = len;
while (i--) {
// do stuff
}
答案 71 :(得分:4)
jQuery和JavaScript:
变量名可以包含许多奇数字符。我使用$字符来标识包含jQuery对象的变量:
var $links = $("a");
$links.hide();
jQuery的链接对象模式非常好,但应用这种模式会让人感到有些困惑。幸运的是JavaScript允许你打破行,如下所示:
$("a")
.hide()
.fadeIn()
.fadeOut()
.hide();
一般JavaScript:
我发现使用自执行函数模拟范围很有用:
function test()
{
// scope of test()
(function()
{
// scope inside the scope of test()
}());
// scope of test()
}
答案 72 :(得分:3)
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
由于Array也是对象,你可以用完全相同的方式迭代其他数组:
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);
答案 73 :(得分:3)
Joose是一个不错的对象系统,如果您希望基于类的OO感觉有点像CLOS。
// Create a class called Point
Class("Point", {
has: {
x: {
is: "rw",
init: 0
},
y: {
is: "rw",
init: 0
}
},
methods: {
clear: function () {
this.setX(0);
this.setY(0);
}
}
})
// Use the class
var point = new Point();
point.setX(10)
point.setY(20);
point.clear();
答案 74 :(得分:3)
存在检查。所以我经常看到像这样的东西
var a = [0, 1, 2];
// code that might clear the array.
if (a.length > 0) {
// do something
}
例如,只需执行此操作:
var a = [0, 1, 2];
// code that might clear the array.
if (a.length) { // if length is not equal to 0, this will be true
// do something
}
你可以做各种各样的存在检查,但这只是一个简单的例子来说明一点
以下是有关如何使用默认值的示例。
function (someArgument) {
someArgument || (someArgument = "This is the deault value");
}
那是我的两分钱。还有其他的掘金,但现在就是这样。
答案 75 :(得分:3)
句法糖:在线for-loop闭包
var i;
for (i = 0; i < 10; i++) (function ()
{
// do something with i
}());
几乎打破了道格拉斯·克罗克福德的所有代码约定,但我认为看起来非常好,从来没有那么简单:)
替代:
var i;
for (i = 0; i < 10; i++) (function (j)
{
// do something with j
}(i));
答案 76 :(得分:3)
与数组或空值一起使用的JavaScript typeof
运算符始终返回object
值,这在某些情况下可能不是程序员所期望的。
这是一个函数,它也会为这些项返回正确的值。数组识别是从Douglas Crockford的书“JavaScript:The Good Parts”中复制的。
function typeOf (value) {
var type = typeof value;
if (type === 'object') {
if (value === null) {
type = 'null';
} else if (typeof value.length === 'number' &&
typeof value.splice === 'function' &&
!value.propertyIsEnumerable('length')) {
type = 'array';
}
}
return type;
}
答案 77 :(得分:2)
要将浮点数转换为整数,您可以使用以下隐藏的黑客之一(请不要):
3.14 >> 0
(通过2.9999999999999999 >> .5?)3.14 | 0
(通过What is the best method to convert floating point to an integer in JavaScript?)3.14 & -1
3.14 ^ 0
~~3.14
基本上,在float上应用任何不会改变最终值的二进制操作(即身份函数)最终会将float转换为整数。
答案 78 :(得分:2)
JavaScript被认为非常善于公开其所有对象,所以无论它的窗口对象本身是什么。
因此,如果我想使用JQuery / YUI div弹出窗口覆盖浏览器警报,它也接受字符串作为参数,只需使用以下代码段即可完成。
function divPopup(str)
{
//code to show the divPopup
}
window.alert = divPopup;
通过此更改,对alert()的所有调用都将显示基于div的新popup而不是特定于浏览器的警报。
答案 79 :(得分:2)
正如Marius已经指出的那样,你可以在函数中使用公共静态变量。
我通常使用它们来创建只执行一次的函数,或者缓存一些复杂的计算结果。
这是我旧的“单身”方法的例子:
var singleton = function(){
if (typeof arguments.callee.__instance__ == 'undefined') {
arguments.callee.__instance__ = new function(){
//this creates a random private variable.
//this could be a complicated calculation or DOM traversing that takes long
//or anything that needs to be "cached"
var rnd = Math.random();
//just a "public" function showing the private variable value
this.smth = function(){ alert('it is an object with a rand num=' + rnd); };
};
}
return arguments.callee.__instance__;
};
var a = new singleton;
var b = new singleton;
a.smth();
b.smth();
正如您所看到的,在这两种情况下,构造函数只运行一次。
例如,我曾在2004年使用过这种方法 创建一个灰色背景的模态对话框 覆盖整个页面(类似于Lightbox)。互联网 Explorer 5.5和6具有最高的堆叠上下文 &LT;选择&GT;或&lt; iframe&gt;由于他们的元素 “窗口”的本性;所以,如果页面包含选择元素, 覆盖它们的唯一方法是创建一个iframe和 将其定位在页面的“顶部”。所以整个剧本都是 相当复杂,有点慢(它使用过滤器: 用于为覆盖iframe设置不透明度的表达式。该 “shim”脚本只有一个“.show()”方法,它创建了 垫片只有一次,并将其缓存在静态变量中:)
答案 80 :(得分:2)
// forget the debug alerts
var alertToFirebugConsole = function() {
if ( window.console && window.console.log ) {
window.alert = console.log;
}
}
答案 81 :(得分:2)
以下是使用jQuery UI Dialog widget覆盖window.alert
函数的代码。我这是一个jQuery插件。你可以在我的博客上阅读它; altAlert, a jQuery plug-in for personalized alert messages
jQuery.altAlert = function (options)
{
var defaults = {
title: "Alert",
buttons: {
"Ok": function()
{
jQuery(this).dialog("close");
}
}
};
jQuery.extend(defaults, options);
delete defaults.autoOpen;
window.alert = function ()
{
jQuery("<div />", {
html: arguments[0].replace(/\n/, "<br />")
}).dialog(defaults);
};
};
答案 82 :(得分:2)
可能是其中一个鲜为人知的人:
arguments.callee.caller + Function#toString()
function called(){
alert("Go called by:\n"+arguments.callee.caller.toString());
}
function iDoTheCall(){
called();
}
iDoTheCall();
打印出iDoTheCall
的源代码 -
已弃用,但有时候警报是您唯一的选择....
答案 83 :(得分:2)
这似乎只适用于Firefox(SpiderMonkey)。在函数内部:
arguments[-2]
给出了参数的数量(与arguments.length
相同)arguments[-3]
提供被调用的函数(与arguments.callee
相同)答案 84 :(得分:2)
还有一种几乎未知的JavaScript语法:
var a;
a=alert(5),7;
alert(a); // alerts undefined
a=7,alert(5);
alert(a); // alerts 7
a=(3,6);
alert(a); // alerts 6
有关此here的更多信息。
答案 85 :(得分:1)
简单的自包含函数返回值缓存:
function isRunningLocally(){
var runningLocally = ....; // Might be an expensive check, check whatever needs to be checked.
return (isRunningLocally = function(){
return runningLocally;
})();
},
昂贵的部分仅在第一次调用时执行,之后所有函数都返回此值。当然,这仅对将始终返回相同内容的函数有用。
答案 86 :(得分:1)
瓶盖:
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和closureSet中出现a
的事实仍然是指相同的变量。即使在closureSet为a
分配了一个新值之后!
答案 87 :(得分:1)
功能可以有方法。
我使用这种AJAX表单提交模式。
var fn = (function() {
var ready = true;
function fnX() {
ready = false;
// AJAX return function
function Success() {
ready = true;
}
Success();
return "this is a test";
}
fnX.IsReady = function() {
return ready;
}
return fnX;
})();
if (fn.IsReady()) {
fn();
}
答案 88 :(得分:1)
当您编写回调时,您有很多代码,如下所示:
callback: function(){
stuff(arg1,arg2);
}
您可以使用下面的功能,使其更清洁。
callback: _(stuff, arg1, arg2)
它使用了一个鲜为人知的javascript函数对象函数,apply。
它还显示了另一个可用作函数名的字符:_。
function _(){
var func;
var args = new Array();
for(var i = 0; i < arguments.length; i++){
if( i == 0){
func = arguments[i];
} else {
args.push(arguments[i]);
}
}
return function(){
return func.apply(func, args);
}
}
答案 89 :(得分:1)
这是一种思考'this'的简单方法。函数内的'this'将引用函数的未来对象实例,通常使用operator new创建。很明显,内部函数的“this”永远不会引用外部函数的实例。
以上应该让人摆脱困境。但是你可以用“这个”做更复杂的事情。
示例1:
function DriveIn()
{
this.car = 'Honda';
alert(this.food); //'food' is the attribute of a future object
//and DriveIn does not define it.
}
var A = {food:'chili', q:DriveIn}; //create object A whose q attribute
//is the function DriveIn;
alert(A.car); //displays 'undefined'
A.q(); //displays 'chili' but also defines this.car.
alert(A.car); //displays 'Honda'
规则:
每当一个函数被调用为一个对象的属性时,函数内部(但在任何内部函数之外)的任何“this”都会引用该对象。
我们需要明确指出,即使使用operator new,“The Rule of This”也适用。在幕后,new通过对象的构造函数属性将“this”附加到对象。
示例2:
function Insect ()
{
this.bug = "bee";
this.bugFood = function()
{
alert("nectar");
}
}
var B = new Insect();
alert(B.constructor); //displays "Insect"; By "The Rule of This" any
//ocurrence of 'this' inside Insect now refers
//to B.
为了使这一点更加清晰,我们可以在不使用operator new的情况下创建一个Insect实例。
示例3:
var C = {constructor:Insect}; //Assign the constructor attribute of C,
//the value Insect.
C.constructor(); //Call Insect through the attribute.
//C is now an Insect instance as though it
//were created with operator new. [*]
alert(C.bug); //Displays "bee."
C.bugFood(); //Displays "nectar."
[*]我能辨别的唯一实际差异是,在示例3中,'constructor'是一个可枚举的属性。当使用operator new时,'constructor'成为属性但不可枚举。如果for-in操作“for(对象中的var name)”返回属性的名称,则属性是可枚举的。
答案 90 :(得分:1)
我的第一次提交并不是一个隐藏的功能,而是一个很少使用的属性重新定义功能的应用程序。因为您可以重新定义对象的方法,所以可以缓存方法调用的结果,如果计算成本很高并且您需要延迟评估,这将非常有用。这提供了最简单的memoization形式。
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();
}
}
如果你想要一个memoized功能,你可以改为。不涉及财产重新定义。
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和setters。我很惊讶他们还没有被提及。由于官方标准与事实上的标准(defineProperty与 define [GS] etter )不同,并且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上没有自动失效,这就是下面的例子。如果更改一个属性会使多个其他属性无效(这些示例中存在“直径”属性),这主要是一个问题。
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(); }
}
答案 91 :(得分:1)
使用 Function.apply 指定该函数可以使用的对象:
假设您有班级
function myClass(){
this.fun = function(){
do something;
};
}
如果你以后再做:
var a = new myClass();
var b = new myClass();
myClass.fun.apply(b); //this will be like b.fun();
您甚至可以将一个调用参数数组指定为secondo参数
看看这个:https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply
答案 92 :(得分:1)
除了公共成员之外,您可以使用闭包创建具有私有(在“类”定义之外)不可访问的“类”静态和非静态成员。
请注意,以下代码中有两种类型的公共成员。特定于实例(在构造函数中定义),可以访问私有实例成员,以及只能访问私有 static的共享成员(在prototype
对象中定义)成员。
var MyClass = (function () {
// private static
var nextId = 1;
// constructor
var cls = function () {
// private
var id = nextId++;
var name = 'Unknown';
// public (this instance only)
this.get_id = function () { return id; };
this.get_name = function () { return name; };
this.set_name = function (value) {
if (typeof value != 'string')
throw 'Name must be a string';
if (value.length < 2 || value.length > 20)
throw 'Name must be 2-20 characters long.';
name = value;
};
};
// public static
cls.get_nextId = function () {
return nextId;
};
// public (shared across instances)
cls.prototype = {
announce: function () {
alert('Hi there! My id is ' + this.get_id() + ' and my name is "' + this.get_name() + '"!\r\n' +
'The next fellow\'s id will be ' + MyClass.get_nextId() + '!');
}
};
return cls;
})();
测试此代码:
var mc1 = new MyClass();
mc1.set_name('Bob');
var mc2 = new MyClass();
mc2.set_name('Anne');
mc1.announce();
mc2.announce();
如果你有Firebug,你会发现除了在定义它们的闭包内设置一个断点之外,没有办法访问私有成员。
在定义需要对值进行严格验证的类以及完全控制状态更改时,此模式非常有用。
要扩展此类,您可以将MyClass.call(this);
放在扩展类的构造函数的顶部。您还需要复制 MyClass.prototype
对象(不要重复使用它,因为您也会更改MyClass
的成员。
如果您要替换announce
方法,则可以通过以下方式致电MyClass.announce
:MyClass.prototype.announce.call(this);
答案 93 :(得分:1)
合并运算符非常酷,并且可以生成一些干净,简洁的代码,尤其是当您将它们链接在一起时:a || b || c || "default";
问题在于,因为它通过评估bool而不是null来工作,如果值评估为假是有效的,他们经常会被看过来。不用担心,在这些情况下,只需回到良好的三元运算符。
我经常看到已经放弃并使用全局而不是静态变量的代码,所以这里是如何(在我认为你可以称之为通用单例工厂的例子中):
var getInstance = function(objectName) {
if ( !getInstance.instances ) {
getInstance.instances = {};
}
if ( !getInstance.instances[objectName] ) {
getInstance.instances[objectName] = new window[objectName];
}
return getInstance.instances[objectName];
};
另外,请注意new window[objectName];
,这是通过名称一般实例化对象的关键。我刚刚在2个月前想出来了。
本着同样的精神,当我使用DOM时,当我第一次初始化我正在添加的任何功能时,我经常将功能参数和/或标志隐藏到DOM节点中。如果有人发出警告,我会加上一个例子。
令人惊讶的是,第一页上没有人提到hasOwnProperty
,这是一种耻辱。当使用in
进行迭代时,在迭代的容器上使用hasOwnProperty
方法是好的,防御性编程,以确保使用的成员名称是您期望的成员名称。
var x = [1,2,3];
for ( i in x ) {
if ( !x.hasOwnProperty(i) ) { continue; }
console.log(i, x[i]);
}
Read here了解更多信息。
最后,with
几乎总是一个坏主意。
答案 94 :(得分:1)
您可以将JavaScript对象绑定为HTML元素属性。
<div id="jsTest">Klick Me</div>
<script type="text/javascript">
var someVariable = 'I was klicked';
var divElement = document.getElementById('jsTest');
// binding function/object or anything as attribute
divElement.controller = function() { someVariable += '*'; alert('You can change instance data:\n' + someVariable ); };
var onclickFunct = new Function( 'this.controller();' ); // Works in Firefox and Internet Explorer.
divElement.onclick = onclickFunct;
</script>
答案 95 :(得分:1)
答案 96 :(得分:1)
您可以动态重新定义运行时环境的大部分内容,例如modifying the Array
constructor或定义undefined
。不是你应该的,但它可以是一个强大的功能。
一种稍微不那么危险的形式是向现有对象添加辅助方法。例如,您可以make IE6 "natively" support indexOf on arrays。
答案 97 :(得分:0)
函数l(f,n){n&amp; l(f,n-1,f(n));}
l(function(loop){alert(loop);}, 5);
提醒5,4,3,2,1
答案 98 :(得分:-5)
嗯,这不是一个功能,但它非常有用:
显示可选择和格式化的警报:
alert(prompt('',something.innerHTML ));