JavaScript的隐藏功能?
我们认为每个程序员都应该知道JavaScript的哪些"隐藏功能"?
在看到以下问题的答案的出色质量之后,我认为是时候向它询问JavaScript了。
- HTML的隐藏功能
- CSS的隐藏功能
- PHP的隐藏功能
- ASP.NET的隐藏功能
- C#的隐藏功能
- Java的隐藏功能
- Python的隐藏功能
尽管JavaScript可以说是目前最重要的客户端语言(问问Google),但令人惊讶的是,大多数Web开发人员几乎没有意识到它的强大功能。
解决方案
回答
函数是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
函数式编程技术可用于编写精美的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(); }
回答
私人方法
对象可以具有私有方法。
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());
回答
与`。
它很少使用,坦率地说,很少有用...但是,在有限的情况下,它确实有其用途。
例如:对象文字对于在新对象上快速设置属性非常方便。但是,如果我们需要更改现有对象的一半属性,该怎么办?
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
语句可能是一个好主意。
另请参阅:带声明的JavaScript是否有合法用途?
回答
我们可以使用[]
而非.
访问对象属性。
这使我们可以查找与变量匹配的属性。
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漏洞。
回答
通过prototype属性获取" JavaScript中的扩展方法"。
Array.prototype.contains = function(value) { for (var i = 0; i < this.length; i++) { if (this[i] == value) return true; } return false; }
这将向所有Array
对象添加contains
方法。我们可以使用以下语法调用此方法
var stringArray = ["foo", "bar", "foobar"]; stringArray.contains("foobar");
回答
为变量分配默认值
我们可以在赋值表达式中使用逻辑或者运算符||
提供默认值:
var a = b || c;
仅当b值为假时(如果为null,false,undefined,0,空字符串或者NaN),a变量将获得c的值,否则,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。
回答
令人惊讶的是,有很多人没有意识到它也是面向对象的。
回答
JavaScript没有块作用域(但是它有闭包,所以我们甚至称它为?)。
var x = 1; { var x = 2; } alert(x); // outputs 2
回答
JavaScript中的闭包如何(类似于Cv2.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);
回答
我们还可以扩展(继承)类并使用所引用的原型链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
这个问题的两个答案都是从Ray Djajadinata的一篇很棒的MSDN文章中修改的代码。
回答
函数是对象,因此可以具有属性。
fn = function(x) { // ... } fn.foo = 1; fn.next = function(y) { // }
回答
我可以引用道格拉斯·克罗克福德(Douglas Crockford)的绝大部分书
JavaScript:好的部分。
但是我只为我们准备一个,请始终使用===
和!==
而不是==
和!=
alert('' == '0'); //false alert(0 == ''); // true alert(0 =='0'); // true
" =="不是可传递的。如果我们使用===
,它将为false
所有这些陈述均符合预期。
回答
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
它还在对象内部具有静态变量:
function Obj(){ this.Static = arguments.callee; } a = new Obj(); a.Static.name = "a"; b = new Obj(); alert(b.Static.name); //Alerts b
回答
undefined
是未定义的。因此,我们可以执行以下操作:
if (obj.field === undefined) /* ... */
回答
正如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具有最高的堆栈上下文
<select>或者<iframe>元素由于其
"窗口式"性质;因此,如果页面包含选择元素,
覆盖它们的唯一方法是创建一个iframe,
将其放置在页面"顶部"。所以整个剧本是
相当复杂,有点慢(它使用过滤器:
设置覆盖iframe的不透明度的表达式)。这
" shim"脚本只有一个" .show()"方法,该方法创建了
匀场仅一次,并将其缓存在静态变量中:)
回答
JavaScript与Date()一起工作的方式让我兴奋!
function isLeapYear(year) { return (new Date(year, 1, 29, 0, 0).getMonth() != 2); }
这确实是"隐藏功能"。
编辑:删除了"?"条件的正确性在注释中建议。
是:... new Date(year,1,29,0,0).getMonth()!= 2吗?真假 ...
请查看评论以获取详细信息。
回答
所有函数实际上都是内置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()将函数定义作为文字字符串返回。
回答
我们无需为函数定义任何参数。我们可以只使用函数的"参数"类数组对象。
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
回答
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;"
回答
我们可以使用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
回答
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();
回答
也许对某些人有些明显...
安装Firebug并使用console.log(" hello")。与使用随机alert();相比,这要好得多,我记得几年前就做了。
回答
我的最爱之一是构造函数类型检查:
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 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;
好开心 :)
回答
这是超级隐藏的,仅偶尔有用;-)
我们可以使用原型链创建一个对象,该对象可以委派给另一个对象,而无需更改原始对象。
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上的"简单"值。如果修改数组或者其他对象,则原型不再"保护"原始对象。当我们在类定义/原型中有{}或者[]时要当心。
回答
JavaScript中的时间戳:
// Usual Way var d = new Date(); timestamp = d.getTime(); // Shorter Way timestamp = (new Date()).getTime(); // Shortest Way timestamp = +new Date();
回答
Function.toString()(隐式):
function x() { alert("Hello World"); } eval ("x = " + (x + "").replace( 'Hello World', 'STACK OVERFLOW BWAHAHA"); x("')); x();
回答
具有公共接口的私有变量
它使用了带有自调用函数定义的巧妙技巧。
返回对象内部的所有内容都可以在公共接口中使用,而其他所有内容都是私有的。
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());
回答
方法(或者函数)可以在其设计使用的类型之外的对象上调用。在自定义对象上调用本机(快速)方法非常好。
var listNodes = document.getElementsByTagName('a'); listNodes.sort(function(a, b){ ... });
此代码崩溃是因为listNodes不是数组。
Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);
这段代码之所以有效,是因为listNodes定义了足以由sort()使用的类似数组的属性(length,[]运算符)。
回答
数字也是对象。因此,我们可以做一些很酷的事情,例如:
// 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
回答
如果用逗号分隔语句,则括号之间几乎可以做任何事情:
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
回答
真实和虚假价值观的概念。你不需要做类似的事情
if(someVar ===未定义|| someVar ===空)...
只需做:
if(!someVar)。
每个值都有一个对应的布尔表示形式。
回答
我们可以在任何对象上执行对象的方法,无论该对象是否具有该方法。当然,它可能并不总是有效(如果该方法假定该对象具有它不具有的功能),但是它可能非常有用。例如:
function(){ arguments.push('foo') // This errors, arguments is not a proper array and has no push method Array.prototype.push.apply(arguments, ['foo']) // Works! }
回答
如果我们希望基于类的OO看起来有点像CLOS,那么Joose是一个不错的对象系统。
// 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();
回答
所有"隐藏"功能都位于Mozilla Wiki上:http://developer.mozilla.org/en/JavaScript。
这里有JavaScript 1.5的核心参考,JavaScript 1.6的新功能,JavaScript 1.7的新功能,以及JavaScript 1.8的新功能。浏览所有这些示例,以获取实际可行且没有错误的示例。
回答
访问:
- http://images.google.com/images?q=disco
将此JavaScript代码粘贴到Web浏览器的地址栏中:
- http://amix.dk/upload/awt/spin.txt
- http://amix.dk/upload/awt/disco.txt
欣赏JavaScript迪斯科表演:-p
回答
有人会说这是一个品味问题,但是:
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";
也可以很好地与递归一起使用:)
回答
从我的头顶上...
功能
arguments.callee指向承载"参数"变量的函数,因此可用于递归匿名函数:
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"
回答
我们可以动态地重新定义运行时环境的大部分内容,例如修改" Array"构造函数或者定义" undefined"。并非我们应该这样做,但是它可以是一项强大的功能。
一种不太危险的形式是在现有对象上添加辅助方法。例如,我们可以使IE6"本地"支持数组上的indexOf。
回答
以下是一些有趣的事情:
- 将" NaN"与任何内容(甚至是" NaN")进行比较总是错误的,其中包括" =="," <"和">"。
- NaN不代表数字,但是如果我们要求输入类型,它实际上会返回一个数字。
- Array.sort可以接受比较器功能,并由类似quicksort的驱动程序调用(取决于实现)。
- 正则表达式"常量"可以保持状态,就像它们匹配的最后一件事一样。
- 某些版本的JavaScript允许我们访问正则表达式上的$ 0,$ 1,$ 2成员。
- 'null'不同于其他任何东西。它既不是对象,布尔值,数字,字符串,也不是"未定义"。有点像"未定义的""未定义"。 (注意:
typeof null ==" object"
) - 在最外面的上下文中," this"会产生其他无法命名的[Global]对象。
- 使用
var
声明变量,而不是仅仅依赖于变量的自动声明,这为运行时提供了优化对该变量的访问的真正机会 - with结构会破坏这种优化
- 变量名称可以包含Unicode字符。
- JavaScript正则表达式实际上不是正则表达式。它们基于Perl的正则表达式,并且可以使用需要很长很长时间才能评估的先行构造表达式。
- 块可以被标记并用作
break
的目标。循环可以被标记并用作continue
的目标。 - 数组不稀疏。设置原本为空的数组的第1000个元素应使用
undefined
填充。 (取决于实施情况) if(new Boolean(false)){...}
将执行{...}
块- Javascript的正则表达式引擎是特定于实现的:例如可以编写"非便携式"正则表达式。
[为了回应良好的评论而进行了一些更新;请参阅评论]
回答
在Crockford的" Javascript:好的部分"中也提到了这一点:
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.
回答
此线程有几个答案,说明如何
通过其原型扩展Array对象。这是坏
IDEA,因为它会破坏for(i in a)
语句。
因此,如果我们不巧用for(i in a)
也可以吗?
我们代码中的任何地方?好吧,只有我们自己的代码是
仅运行的代码,不太可能
在浏览器中。恐怕如果人们开始扩展
他们这样的数组对象,堆栈溢出将开始
堆满一堆神秘的JavaScript错误。
在这里查看有用的详细信息。
回答
这里有一些捷径:
var a = []; // equivalent to new Array() var o = {}; // equivalent to new Object()
回答
我不得不说自我执行功能。
(function() { alert("hi there");})();
由于Javascript没有块作用域,因此,如果要定义局部变量,可以使用自执行函数:
(function() { var myvar = 2; alert(myvar); })();
在这里," myvar"是不会干扰或者污染全局范围的,并在函数终止时消失。
回答
==运算符具有一个非常特殊的属性,它会造成这种令人不安的相等性(是的,我知道在其他动态语言(如Perl)中,这种行为是可以预期的,但JavaScript通常在比较中并不聪明):
>>> 1 == true true >>> 0 == false true >>> 2 == true false
回答
在将console.log()用于Firebug时,在Internet Explorer中进行测试时,可以避免令人讨厌的错误:
function log(message) { (console || { log: function(s) { alert(s); }).log(message); }
回答
让
与var缺少块作用域相反的是在JavaScript 1.7中引入的" let"。
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.
回答
生成器和迭代器(仅在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"); }
The function containing the yield keyword is a generator. When you call it, its formal parameters are bound to actual arguments, but its body isn't actually evaluated. Instead, a generator-iterator is returned. Each call to the generator-iterator's next() method performs another pass through the iterative algorithm. Each step's value is the value specified by the yield keyword. Think of yield as the generator-iterator version of return, indicating the boundary between each iteration of the algorithm. Each time you call next(), the generator code resumes from the statement following the yield. In normal usage, iterator objects are "invisible"; you won't need to operate on them explicitly, but will instead use JavaScript's for...in and for each...in statements to loop naturally over the keys and/or values of objects.
var objectWithIterator = getObjectSomehow(); for (var i in objectWithIterator) { document.write(objectWithIterator[i] + "<br>\n"); }
回答
要从对象中正确删除属性,应删除该属性,而不仅仅是将其设置为undefined:
var obj = { prop1: 42, prop2: 43 }; obj.prop2 = undefined; for (var key in obj) { ...
属性prop2仍将是迭代的一部分。如果我们想完全摆脱prop2,则应该执行以下操作:
delete obj.prop2;
当我们遍历属性时,prop2属性将不再显示。
回答
如果我们要搜索给定主题的不错的JavaScript参考,请在查询中包含" mdc"关键字,第一个结果将来自Mozilla开发人员中心。我没有随身携带任何离线参考书或者书籍。我总是使用" mdc"关键字技巧来直接获得所需的内容。例如:
Google:JavaScript数组排序MDC
(在大多数情况下,我们可以省略" javascript")
更新:Mozilla开发人员中心已重命名为Mozilla开发人员网络。 " mdc"关键字技巧仍然有效,但是很快我们可能不得不开始使用" mdn"了。
回答
如果我们盲目地使用eval()JSON字符串进行反序列化,则可能会遇到问题:
- 这是不安全的。该字符串可能包含恶意函数调用!
- 如果不将JSON字符串括在括号中,则属性名称可能会误认为是标签,从而导致意外行为或者语法错误:
eval("{ \"foo\": 42 }"); // syntax error: invalid label eval("({ \"foo\": 42 })"); // OK
回答
function l(f,n){n&&l(f,n-1,f(n));} l( function( loop ){ alert( loop ); }, 5 );
警报5、4、3、2、1
回答
要将浮点数转换为整数,可以使用以下一种神秘的技巧(请不要这样做):
3.14 >> 0
(通过2.9999999999999999 >> .5?)3.14 | 0
(通过JavaScript将浮点数转换为整数的最佳方法是什么?)3.14&-1
3.14 ^ 0
- ~~ 3.14
基本上,对浮点数执行任何不会更改最终值的二进制运算(即身份函数),最终会将浮点数转换为整数。
回答
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() }
回答
函数语句和函数表达式的处理方式不同。
function blarg(a) {return a;} // statement bleep = function(b) {return b;} //expression
在运行代码之前,将对所有function语句进行解析,第一个语句中将提供JavaScript文件底部的功能。另一方面,它将无法利用某些动态上下文,例如,围绕" with"语句,在解析函数时尚未执行" with"语句。
函数表达式在遇到它们的位置处内联执行。在此之前它们不可用,但是它们可以利用动态上下文。
回答
原型继承(由Douglas Crockford推广)彻底改变了我们对Java语言中的事物负载的思考方式。
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'
回答
" window.name"的值在页面更改期间仍然存在,如果在同一域中,则可以由父窗口读取(如果在iframe中,请使用" document.getElementById("框架的ID")。contentWindow.name") ),并且仅受可用内存限制。
回答
遍历对象的属性时,请确保使用hasOwnProperty方法:
for (p in anObject) { if (anObject.hasOwnProperty(p)) { //Do stuff with p here } }
这样做是为了使我们仅访问anObject的直接属性,而不使用原型链中的属性。
回答
当我们要从数组中删除元素时,可以使用delete运算符,如下所示:
var numbers = [1,2,3,4,5]; delete numbers[3]; //numbers is now [1,2,3,undefined,5]
如我们所见,该元素已被删除,但是由于该元素被未定义的值替换,因此在数组中留下了一个孔。
因此,要变通解决此问题,而不是使用删除,而是使用拼接数组方法...如下:
var numbers = [1,2,3,4,5]; numbers.splice(3,1); //numbers is now [1,2,3,5]
splice的第一个参数是数组[index]中的序数,第二个参数是要删除的元素数。
回答
微软送给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) } }
回答
如果"循环"条件对我们来说无关紧要,则大循环在" while"条件下和向后循环都更快。在我大约50%的代码中,通常不是。
IE。
var i, len = 100000; for (var i = 0; i < len; i++) { // do stuff }
慢于:
i = len; while (i--) { // do stuff }
回答
还有一种几乎未知的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
有关此的更多信息。
回答
我们通常可以使用对象而不是开关。
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);
回答
我们可能会根据类型捕获异常。从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规范的一部分,因此,除了特定的浏览器外,不能依赖它。
回答
我们可以将具有整数属性和length属性的" any *"对象转换为适当的数组,从而为它提供所有数组方法,例如推,弹出,拼接,映射,过滤,缩小等
Array.prototype.slice.call({"0":"foo", "1":"bar", 2:"baz", "length":3 })
//返回[" foo"," bar"," baz"]
这适用于其他框架中的jQuery对象,html集合和Array对象(作为解决整个数组类型问题的一种可能的解决方案)。我说,如果它具有length属性,则可以将其转换为数组,没关系。除了arguments对象之外,还有许多具有length属性的非数组对象。
回答
语法糖:内联for闭包
var i; for (i = 0; i < 10; i++) (function () { // do something with i }());
打破了Douglas Crockford的几乎所有代码约定,但我认为它看起来很不错,但更是如此:)
选择:
var i; for (i = 0; i < 10; i++) (function (j) { // do something with j }(i));
回答
这似乎仅在Firefox(SpiderMonkey)上有效。在函数内部:
arguments [-2]
给出参数的数量(与arguments.length
相同)arguments [-3]
给出了被调用的函数(与arguments.callee
相同)
回答
存在检查。所以我经常看到这样的东西
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"); }
那是我的两分钱。还有其他金块,但仅此而已。
回答
我们不必使用eval()
来组装全局变量名。
也就是说,如果我们有多个名为" spec_grapes,spec_apples"的全局变量(无论出于何种原因),则不必使用" eval(" spec_" + var)`"来访问它们。
所有全局变量都是window []
的成员,因此我们可以执行window [" spec_" + var]
。
回答
我们可以使用" 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
并且由于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);
不是真正的随机分布,请参见注释。
回答
创建新的"对象"时,括号是可选的。
function Animal () { } var animal = new Animal(); var animal = new Animal;
一样。
回答
我们可以使用左侧的[]来分配局部变量。如果我们想从一个函数中返回多个值而不创建不必要的数组,则派上用场。
function fn(){ var cat = "meow"; var dog = "woof"; return [cat,dog]; }; var [cat,dog] = fn(); // Handy! alert(cat); alert(dog);
它是核心JS的一部分,但是直到今年我才意识到。
回答
JavaScript技巧或者jslibs项目。
回答
也许是鲜为人知的之一:
arguments.callee.caller + Function#toString()
function called(){ alert("Go called by:\n"+arguments.callee.caller.toString()); } function iDoTheCall(){ called(); } iDoTheCall();
打印出" iDoTheCall"的源代码-
已过时,但有时在我们唯一选择警报时会很有用...。
回答
我们可以将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>
回答
如果我们尝试沙盒化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')")();
好玩的东西。
回答
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();
要连接大字符串,请使用数组(速度更快):
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"
快得多。
回答
合并运算符非常酷,可以编写一些简洁明了的代码,尤其是将它们链接在一起时: b || c ||陷阱是,由于它通过评估布尔值而不是null来工作,因此,如果评估为false的值是有效的,则它们常常会被忽略。不用担心,在这些情况下,只需恢复为高级三元运算符即可。
我经常看到放弃并使用全局变量而不是静态变量的代码,因此,这就是方法(在我想可以称为通用单例工厂的示例中):
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]); }
在这里阅读更多内容。
最后," with"几乎总是一个坏主意。
回答
我们可以使用闭包,使除公共成员外,具有私有(在"类"定义之外无法访问)静态和非静态成员的"类"。
请注意,以下代码中有两种类型的公共成员。可以访问私有实例成员的特定于实例的(在构造函数中定义),以及仅可以访问私有静态成员的共享成员(在" 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);
回答
与数组或者null一起使用的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; }
回答
使用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();
我们甚至可以将调用参数数组指定为第二个参数
看起来像这样:https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply
回答
我的第一个提交不是隐藏的功能,而是属性重定义功能的很少使用的应用程序。因为我们可以重新定义对象的方法,所以可以缓存方法调用的结果,这在计算成本很高并且我们希望进行惰性求值时很有用。这给出了最简单的记忆形式。
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的转换,通常无法正常工作。修复它留给读者练习。
我的第二个建议是吸气剂和吸气剂。我很惊讶他们还没有被提及。由于正式标准与实际标准(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(); } }
回答
模块模式
<script type="text/javascript"> (function() { function init() { // ... } window.onload = init; })(); </script>
没有var
语句或者在函数外部声明的变量和函数将在全局范围内定义。如果已经存在同名的变量/函数,它将被静默覆盖,这将导致非常难以发现错误。一种常见的解决方案是将整个代码主体包装到一个匿名函数中,然后立即执行它。这样,所有变量/函数都在匿名函数的作用域中定义,并且不会泄漏到全局作用域中。
要在全局范围内显式定义变量/函数,必须在它们前面加上" window"前缀:
window.GLOBAL_VAR = 12; window.global_function = function() {};
回答
命名空间
在较大的JavaScript应用程序或者框架中,在命名空间中组织代码可能很有用。 JavaScript没有内置模块或者名称空间概念,但是使用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() {};
回答
这是思考" this"的简单方法。函数内部的" this"将引用该函数的未来对象实例,通常是用new运算符创建的。很明显,内部函数的" this"将永远不会引用外部函数的实例。
以上应避免麻烦。但是使用" 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"都是指对象。
我们需要明确指出,即使使用运算符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.
为了使这一点更加清楚,我们可以创建一个Insect实例,而无需使用new运算符。
范例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中,"构造函数"是可枚举的属性。当使用运算符new时,"构造函数"将成为属性,但不能枚举。如果for-in操作" for(对象中的变量名)"返回该属性的名称,则该属性是可枚举的。
回答
知道一个函数需要多少个参数
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
回答
好吧,这不是很多功能,但是非常有用:
显示可选和格式化的警报:
alert(prompt('',something.innerHTML ));
回答
这些并非总是一个好主意,但是我们可以使用简洁的表达式来转换大多数内容。这里的重点是,并不是JavaScript中的每个值都是一个对象,因此这些表达式将在对非对象(例如null和undefined)的成员访问失败的地方成功。特别要注意,typeof null ==" object",但不能为null.toString()或者(" name"为null)。
将任何内容转换为数字:
+anything Number(anything)
将任何内容转换为无符号的四字节整数:
anything >>> 0
将任何内容转换为字符串:
'' + anything String(anything)
将任何内容转换为布尔值:
!!anything Boolean(anything)
同样,对于字符串,数字和布尔,使用不带"新"的类型名称的行为也有所不同,返回原始数字,字符串或者布尔值,但是对于"新",这些将返回"装箱"对象类型,这几乎是无用的。
回答
我最喜欢的技巧是使用" 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
回答
嗯,虽然对我来说很有趣,但我没有读完整的主题,但让我捐一点钱:
// forget the debug alerts var alertToFirebugConsole = function() { if ( window.console && window.console.log ) { window.alert = console.log; } }
回答
函数可以有方法。
我使用这种模式的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(); }
回答
JavaScript被认为非常擅长公开其所有对象,因此无论其window对象本身如何。
因此,如果我想使用JQuery / YUI div弹出窗口覆盖浏览器警报,该弹出窗口也接受字符串作为参数,则可以使用以下代码段轻松完成。
function divPopup(str) { //code to show the divPopup } window.alert = divPopup;
进行此更改后,对alert()的所有调用将显示基于新div的良好弹出窗口,而不是特定于浏览器的警报。
回答
封闭之禅
其他人提到了关闭。但是令人惊讶的是,有多少人了解闭包,使用闭包编写代码,但仍然对闭包的真正含义有错误的认识。有些人将一流的功能与闭包混为一谈。还有一些人将其视为一种静态变量。
对我来说,闭包是一种"私有"全局变量。也就是说,一种变量,某些函数将其视为全局变量,而其他函数则看不到。现在,我知道对底层机制的描述正在快速而松散地进行,但这就是它的感觉和行为方式。为了显示:
// 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(); 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
*注意:我在这里不知道正确的用语。
回答
我知道我参加聚会很晚,但是我简直不敢相信除了"将任何内容转换为数字"之外,还没有提到" +"运算符的作用。也许这就是隐藏功能的程度了吗?
// 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;
回答
JavaScript的多功能性-覆盖默认功能
这是用jQuery UI的Dialog小部件覆盖window.alert
函数的代码。我这样做是作为jQuery插件。我们可以在我的博客上阅读有关内容; altAlert,一个用于个性化警报消息的jQuery插件。
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); }; };
回答
简单的自包含函数返回值缓存:
function isRunningLocally(){ var runningLocally = ....; // Might be an expensive check, check whatever needs to be checked. return (isRunningLocally = function(){ return runningLocally; })(); },
昂贵的部分仅在第一次调用时执行,此后所有函数都将返回此值。当然,这仅对总是返回相同内容的函数有用。
回答
在函数中,我们可以返回函数本身:
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框架似乎已经实现了此功能。例如,请参见本主题。
回答
关闭时间:
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;"),而是" a"的出现在closureGet和closureSet中,仍然引用相同的变量。即使在ClosureSet为a赋予了新值之后!
回答
这是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 [1]是选择器名称(例如foo)
- args [2]是用于包装参数的引号字符(例如::```:foo(" bar,baz")
),如果没有引号,则为空字符串(
:foo(bar,baz )`)或者undefined(如果没有参数) - args [3]是参数,包括任何引号(例如," bar,baz"),如果没有参数则为undefined
- 堆栈是节点集(一个数组,其中包含所有在该点匹配的节点)
如果选择器匹配,该函数应返回" true",否则返回" false"。
例如,以下代码将使我们能够基于全文正则表达式搜索来选择节点:
$.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()的回调版本可以达到类似的效果,但是自定义选择器更加灵活,通常更具可读性。
回答
在编写回调时,我们将有很多代码,如下所示:
callback: function(){ stuff(arg1,arg2); }
我们可以使用下面的功能,使其更整洁。
callback: _(stuff, arg1, arg2)
它使用javascript的Function对象的一个鲜为人知的功能,适用。
它还显示可以用作函数名的另一个字符:_。
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); } }