JavaScript的隐藏function?

每个程序员都应该知道什么JavaScript的“隐藏特性”?

看到以下问题的答案的优秀质量后,我认为是时候问它的JavaScript。

  • HTML的隐藏特性
  • CSS的隐藏特性
  • PHP的隐藏function
  • ASP.NET的隐藏function
  • C#的隐藏特性
  • Java的隐藏特性
  • Python的隐藏特性

尽pipeJavaScript现在可以说是最重要的客户端语言(只要问一下Google),但令人惊讶的是,大多数Web开发人员很less意识到它真的有多强大。

您不需要为函数定义任何参数。 你可以使用函数的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 

我可以引用道格拉斯·克罗克福德(Douglas Crockford)的绝佳书籍JavaScript:The Good Parts 。

但是我会为你取一个,总是用===!==代替==!=

 alert('' == '0'); //false alert(0 == ''); // true alert(0 =='0'); // true 

==不是传递的。 如果您使用=== ,则会按预期为所有这些语句赋予错误。

function是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 。

特别的,函数可以作为parameter passing,例如Array.filter()接受一个callback:

 [1, 2, -1].filter(function(element, index, array) { return element > 0 }); // -> [1,2] 

您也可以声明一个只存在于特定函数范围内的“私有”函数:

 function PrintName() { var privateFunction = function() { return "Steve"; }; return privateFunction(); } 

您可以使用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 

将默认值分配给variables

您可以使用逻辑或运算符|| 在赋值expression式中提供一个默认值:

 var a = b || c; 

如果b虚假的 (如果是nullfalseundefined0empty stringNaN ),那么avariables将得到c的值,否则a将得到b的值。

在函数中,这通常是有用的,当你不想提供一个参数时给一个默认值:

 function example(arg1) { arg1 || (arg1 = 'default value'); } 

事件处理程序中的示例IE回退:

 function onClick(e) { e || (e = window.event); } 

以下语言特性已经在我们很长一段时间了,所有JavaScript实现都支持它们,但是直到ECMAScript第5版 ,它们才成为规范的一部分:

debugger语句

描述在: §12.15debugging器语句

这个语句允许你通过编程在你的代码中放置断点

 // ... debugger; // ... 

如果一个debugging器存在或活动,它将导致它立即中断,就在那一行。

否则,如果debugging器不存在或不活跃,则该语句没有可观察的效果。

多行string文字

描述在: §7.8.4string文字

 var str = "This is a \ really, really \ long line!"; 

你必须小心,因为\旁边的字符必须是行结束符,如果你在\之后有一个空格,例如,代码看起来完全一样,但是会引发一个SyntaxError

JavaScript没有块范围 (但它有closures,所以我们甚至称它?)。

 var x = 1; { var x = 2; } alert(x); // outputs 2 

您可以用[]而不是用来访问对象属性.

这允许你查找一个匹配variables的属性。

 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漏洞。

如果您是在特定主题上search体面的JavaScript参考,请在查询中包含“mdc”关键字,您的第一个结果将来自Mozilla开发人员中心。 我不随身携带任何脱机参考资料或书籍。 我总是使用“mdc”关键字技巧来直接find我正在寻找的东西。 例如:

Google: javascript数组sortingmdc
(在大多数情况下,你可能会省略“javascript”)

更新: Mozilla开发人员中心已更名为Mozilla开发者networking 。 “mdc”关键字技巧仍然有效,但是很快我们可能不得不开始使用“mdn”

也许有些明显的一些…

安装Firebug并使用console.log(“hello”)。 比使用随机alert()更好,这在几年前我记得很多。

私有方法

一个对象可以有私有方法。

 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()); 

在Crockford的“Javascript:The Good Parts”中也提到:

parseInt()是危险的。 如果您传递一个string而不通知它的正确基地它可能会返回意外的数字。 例如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. 

函数是对象,因此可以有属性。

 fn = function(x){
    // ...
 }

 fn.foo = 1;

 fn.next = function(y){
   //
 }

我不得不说自我执行的function。

 (function() { alert("hi there");})(); 

因为Javascript 没有块范围 ,所以如果你想定义局部variables,你可以使用一个自动执行的函数:

 (function() { var myvar = 2; alert(myvar); })(); 

这里, myvar不干扰或污染全局范围,并在函数终止时消失。

知道一个函数需要多less个参数

 function add_nums(num1, num2, num3 ){ return num1 + num2 + num3; } add_nums.length // 3 is the number of parameters expected. 

知道该函数接收了多less个参数

 function add_many_nums(){ return arguments.length; } add_many_nums(2,1,122,12,21,89); //returns 6 

这里有一些有趣的事情:

  • 比较NaN和任何东西(甚至NaN )都是错误的,包括==<>
  • NaN代表不是一个数字,但如果你要求的types,它实际上返回一个数字。
  • Array.sort可以采用一个比较器函数,并由类似quicksort的驱动程序调用(取决于实现)。
  • 正则expression式“常量”可以保持状态,就像它们匹配的最后一样。
  • 某些版本的JavaScript允许您访问正则expression式中的$0$1$2成员。
  • null不同于其他任何东西。 它既不是对象,布尔型,数字,string,也不是undefined 。 这有点像一个“替代” undefined 。 (注意: typeof null == "object"
  • 在最外层的上下文中, this产生不可用的[Global]对象。
  • var声明一个variables,而不是仅仅依靠variables的自动声明,给运行时真正的机会来优化对variables的访问
  • with构造会破坏这样的优化
  • variables名称可以包含Unicode字符。
  • JavaScript正则expression式实际上并不经常。 它们基于Perl的正则expression式,并且可以用非常长时间来评估的构buildexpression式。
  • 块可以被标记并用作break的目标。 循环可以被标记并用作continue的目标。
  • 数组并不稀疏。 设置一个空的数组的第1000个元素应该用undefined填充。 (取决于实施)
  • if (new Boolean(false)) {...}将执行{...}
  • Javascript的正则expression式引擎是特定于实现的:例如,可以编写“不可移植的”正则expression式。

[回复好评,稍作更新; 请参阅评论]

我知道我迟到了,但我不敢相信+运营商的用处在“把任何东西变成数字”之外没有被提及。 也许这是如何隐藏它的function?

 // 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中的扩展方法 ”通过prototype属性。

 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"); 

要正确地从对象中删除一个属性,你应该删除该属性,而不是将其设置为undefined

 var obj = { prop1: 42, prop2: 43 }; obj.prop2 = undefined; for (var key in obj) { ... 

属性prop2仍然是迭代的一部分。 如果你想完全摆脱prop2 ,你应该做:

 delete obj.prop2; 

属性prop2将不再会在你遍历属性的时候出现。

with

它很less使用,坦率地说,很less有用……但在有限的情况下,它的确有用处。

例如:对象文字对于快速设置对象的属性非常方便。 但是如果你需要改变现有对象的一半属性呢?

 var user = { fname: 'Rocket', mname: 'Aloysus', lname: 'Squirrel', city: 'Fresno', state: 'California' }; // ... with (user) { mname = 'J'; city = 'Frostbite Falls'; state = 'Minnesota'; } 

Alan Storm指出,这可能有点危险:如果用作上下文的对象没有被赋值的属性之一,它将在外部作用域中parsing,可能会创build或覆盖全局variables。 如果您习惯于编写代码来处理具有缺省值或空值的属性的对象,那么这是特别危险的:

 var user = { fname: "John", // mname definition skipped - no middle name lname: "Doe" }; with (user) { mname = "Q"; // creates / modifies global variable "mname" } 

因此,避免使用with语句进行这种分配可能是一个好主意。

另请参阅: JavaScript的“with”语句是否有合法用途?

可以在对象上调用方法(或函数),这些对象不是它们devise用来处理的types。 在自定义对象上调用本机(快速)方法是非常好的。

 var listNodes = document.getElementsByTagName('a'); listNodes.sort(function(a, b){ ... }); 

此代码崩溃,因为listNodes不是一个Array

 Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]); 

此代码的工作原理是listNodes定义了sort()使用的足够数组类的属性(length,[]运算符sort()

原型inheritance (道格拉斯·克罗克福德(Douglas Crockford)所推广的)彻底改变了你在Javascript中思考负载的方式。

 Object.beget = (function(Function){ return function(Object){ Function.prototype = Object; return new Function; } })(function(){}); 

这是一个杀手! 可惜几乎没有人使用它。

它允许您“生成”任何对象的新实例,扩展它们,同时维持(实时)原型inheritance链接到其他属性。 例:

 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' 

有些人会把这叫做品味,但是:

 aWizz = wizz || "default"; // same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; } 

三元运算符可以像Scheme(cond …)一样被链接起来:

 (cond (predicate (action ...)) (predicate2 (action2 ...)) (#t default )) 

can be written as…

 predicate ? action( ... ) : predicate2 ? action2( ... ) : default; 

This is very "functional", as it branches your code without side effects. 所以,而不是:

 if (predicate) { foo = "one"; } else if (predicate2) { foo = "two"; } else { foo = "default"; } 

你可以写:

 foo = predicate ? "one" : predicate2 ? "two" : "default"; 

Works nice with recursion, too 🙂

Numbers are also objects. So you can do cool stuff like:

 // 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 

How about closures in JavaScript (similar to anonymous methods in C# v2.0+). You can create a function that creates a function or "expression".

Example of closures :

 //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); 

You can also extend (inherit) classes and override properties/methods using the prototype chain spoon16 alluded to.

In the following example we create a class Pet and define some properties. We also override the .toString() method inherited from Object.

After this we create a Dog class which extends Pet and overrides the .toString() method again changing it's behavior (polymorphism). In addition we add some other properties to the child class.

After this we check the inheritance chain to show off that Dog is still of type Dog, of type Pet, and of type 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 

Both answers to this question were codes modified from a great MSDN article by Ray Djajadinata.

You may catch exceptions depending on their type. Quoted from 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 } 

NOTE: Conditional catch clauses are a Netscape (and hence Mozilla/Firefox) extension that is not part of the ECMAScript specification and hence cannot be relied upon except on particular browsers.

Off the top of my head…

function

arguments.callee refers to the function that hosts the "arguments" variable, so it can be used to recurse anonymous functions:

 var recurse = function() { if (condition) arguments.callee(); //calls recurse() again } 

That's useful if you want to do something like this:

 //do something to all array items within an array recursively myArray.forEach(function(item) { if (item instanceof Array) item.forEach(arguments.callee) else {/*...*/} }) 

对象

An interesting thing about object members: they can have any string as their names:

 //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); } 

Strings

String.split can take regular expressions as parameters:

 "hello world with spaces".split(/\s+/g); //returns an array: ["hello", "world", "with", "spaces"] 

String.replace can take a regular expression as a search parameter and a function as a replacement parameter:

 var i = 1; "foo bar baz ".replace(/\s+/g, function() {return i++}); //returns "foo1bar2baz3" 

You can use objects instead of switches most of the time.

 function getInnerText(o){ return o === null? null : { string: o, array: o.map(getInnerText).join(""), object:getInnerText(o["childNodes"]) }[typeis(o)]; } 

Update: if you're concerned about the cases evaluating in advance being inefficient (why are you worried about efficiency this early on in the design of the program??) then you can do something like this:

 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)](); } 

This is more onerous to type (or read) than either a switch or an object, but it preserves the benefits of using an object instead of a switch, detailed in the comments section below. This style also makes it more straightforward to spin this out into a proper "class" once it grows up enough.

update2: with proposed syntax extensions for ES.next, this becomes

 let getInnerText = o -> ({ string: o -> o, array: o -> o.map(getInnerText).join(""), object: o -> getInnerText(o["childNodes"]) }[ typeis o ] || (->null) )(o); 

Be sure to use the hasOwnProperty method when iterating through an object's properties:

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

This is done so that you will only access the direct properties of anObject , and not use the properties that are down the prototype chain.

Private variables with a Public Interface

It uses a neat little trick with a self-calling function definition. Everything inside the object which is returned is available in the public interface, while everything else is private.

 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());