函数在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]

你也可以声明一个“private”函数,它只存在于特定函数的作用域中:

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

with

它很少被使用,坦率地说,很少有用……但是,在有限的情况下,它确实有它的用途。

例如:对象字面量对于快速在对象上设置属性非常方便。但是如果你需要改变现有对象的属性一半怎么办?

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的“with”语句是否有合法的用途?

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

这允许您查找与变量匹配的属性。

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

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

为变量分配默认值

你可以在赋值表达式中使用逻辑或操作符||来提供一个默认值:

var a = b || c;

只有当bc1 (if为nullfalseundefined0empty stringNaN)时,a变量才会获得c的值,否则a将获得b的值。

这通常在函数中很有用,当你想在没有提供参数的情况下给参数一个默认值:

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

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

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

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

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中的闭包怎么样(类似于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);

你也可以使用前面提到的原型链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) {
//
}
我可以引用道格拉斯·克罗克福德这本好书的大部分内容 JavaScript:好的部分 . < / p >

但我只给你一个,总是使用===!==,而不是==!=

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

==是不可传递的。如果你使用===,它会给false for

.

.

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

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具有最高的堆叠上下文 & lt; select>或& lt; iframe>由于它们的元素 ”窗口的“自然;如果页面包含select元素, 覆盖它们的唯一方法是创建一个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 ?真:假……

所有函数实际上都是内置函数类型的实例,该类型有一个构造函数,该构造函数接受包含函数定义的字符串,因此您实际上可以在运行时通过例如连接字符串来定义函数:

//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 ()将函数定义作为文字字符串返回。

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

Javascript中的所有对象都实现为哈希表,因此它们的属性可以通过索引器访问,反之亦然。此外,你可以使用的/操作符枚举所有属性:

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

可以使用操作符检查对象中是否存在键:

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对象]。

另一种方法是使用变量参数作为“重载”函数的一种方式。你所做的只是使用一个表达式来检测参数的数量并返回重载输出:

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上的“简单”值。如果你修改了一个数组或另一个对象,那么原型就不再“保护”原始对象。当你在Class定义/原型中有{}或[]时要小心。

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

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

这段代码之所以有效,是因为listNodes定义了足够多的类数组属性(length,[]运算符)供sort()使用。

数字也是对象。所以你可以做一些很酷的事情,比如:

// 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 === undefined || someVar === null)…

只是做的事:

如果(! 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!
}

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

你所有的“隐藏”特性都在Mozilla wiki上:http://developer.mozilla.org/en/JavaScript

核心JavaScript 1.5参考JavaScript 1.6有什么新功能,什么是JavaScript 1.7新增功能,还有什么是JavaScript 1.8新增功能。查看所有这些实际工作的例子,并错误。

访问:

将以下JavaScript代码粘贴到浏览器的地址栏:

享受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";

也适用于递归:)

在我的脑海中…

功能

参数。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"]

. replace可以将正则表达式作为搜索参数,将函数作为替换参数:

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

你可以动态地重新定义运行时环境的大部分,比如修改Array构造函数或定义undefined。不是说你应该这样做,但它可以是一个强大的功能。

一种稍微不那么危险的形式是向现有对象添加helper方法。例如,你可以使IE6“原生”支持数组上的indexOf

下面是一些有趣的事情:

  • NaN与任何东西(甚至NaN)进行比较总是错误的,包括==<>
  • NaN代表不是一个数字,但如果你问类型,它实际上返回一个数字。
  • Array.sort可以接受比较器函数,并由类似快速排序的驱动程序调用(取决于实现)。
  • 正则表达式“常量”可以维护状态,就像它们匹配的最后一个东西一样。
  • 某些版本的JavaScript允许你访问正则表达式中的$0$1$2成员。
  • null不同于其他任何东西。它既不是对象、布尔值、数字、字符串,也不是undefined。它有点像“alternate”undefined。(注:typeof null == "object")
  • 在最外层的上下文中,this产生了无法命名的[Global]对象。
  • 使用var声明变量,而不是仅仅依赖于变量的自动声明,这让运行时有机会优化对该变量的访问
  • with构造将破坏这样的优化
  • 变量名可以包含Unicode字符。
  • JavaScript正则表达式实际上并不是正则的。它们基于Perl的正则表达式,并且可以用需要花费非常非常长的时间来计算的lookhead来构造表达式。
  • 块可以被标记并用作break的目标。循环可以被标记并用作continue的目标。
  • 数组不是稀疏的。设置空数组的第1000个元素应该用undefined填充。(取决于实现)
  • if (new Boolean(false)) {...}将执行{...}
  • Javascript的正则表达式引擎是特定于实现的:例如,可以编写“不可移植”的正则表达式。

[更新了一点,以回应良好的评论;请参阅评论]

在Crockford的《Javascript: The Good Parts》中也提到过:

parseInt()是危险的。如果你给它一个字符串而不告诉它正确的进制,它可能会返回意想不到的数字。例如,parseInt('010')返回8,而不是10。传递一个base给parseInt可以使它正确工作:

parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.
在这个线程中有几个答案显示如何 通过Array对象的原型扩展Array对象。这很糟糕

. IDEA,因为它破坏了for (i in a)语句

所以如果你不碰巧使用for (i in a)是可以的 代码中的任何地方?除非你自己的代码是 只有您正在运行的代码,这是不太可能的 在浏览器内部。我担心如果人们开始扩张 他们的数组对象像这样,堆栈溢出将启动

参见有用的详细信息在这里

这里有一些捷径:

var a = []; // equivalent to new Array()
var o = {}; // equivalent to new Object()

我要说的是自执行函数。

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

因为Javascript 没有块作用域,如果你想定义局部变量,你可以使用一个自执行函数:

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

在这里,myvar is不会干扰或污染全局作用域,并且在函数结束时消失。

==运算符有一个非常特殊的属性,它创建了这个令人不安的等式(是的,我知道在其他动态语言中,如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);
}

let

Var缺乏块作用域对应的是let在JavaScript 1.7中引入

    let语句提供了一种将值与变量关联的方法 在一个块的范围内,没有 影响like-named的值
  • 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。

生成器和迭代器(仅适用于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 . xml的函数 关键字是一个生成器。当你打电话时 它的形式参数必然是 实际的参数,但它的主体不是 实际评估。相反,一个 返回Generator-iterator。每一个 调用生成器-迭代器的 next()方法执行另一个传递 通过迭代算法。每一个 Step的值是由 yield关键字。将yield视为 的生成迭代器版本 返回,表示边界 在每次迭代之间 算法。每次调用next()时, 生成器代码从 yield后面的语句 在正常使用中,迭代器对象为 “看不见的”;你不需要做手术 明确的,但将取而代之 使用JavaScript的for...infor each...in语句自然循环 的键和/或值 对象。< / p >

var objectWithIterator = getObjectSomehow();


for (var i in objectWithIterator)
{
document.write(objectWithIterator[i] + "<br>\n");
}

要正确地从对象中删除属性,你应该删除该属性,而不是仅仅将其设置为未定义的:

var obj = { prop1: 42, prop2: 43 };


obj.prop2 = undefined;


for (var key in obj) {
...

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

delete obj.prop2;

当你遍历属性时,属性prop2将不再出现。

如果您在google上搜索关于给定主题的像样的JavaScript参考,在查询中包含“mdc”关键字,那么您的第一个结果将来自Mozilla Developer Center。我没有随身携带任何离线参考资料或书籍。我总是使用“mdc”关键字来直接找到我要找的东西。例如:

< p >谷歌:Javascript数组排序MDC < br > (在大多数情况下,你可以省略“javascript”)

更新: Mozilla Developer 中心已重命名为Mozilla Developer 网络。"mdc"关键字技巧仍然有效,但很快我们可能不得不开始用“mdn”代替

如果你盲目地eval()一个JSON字符串来反序列化它,你可能会遇到问题:

  1. 这并不安全。该字符串可能包含恶意函数调用!
  2. 如果你没有将JSON字符串括在括号中,属性名可能会被误认为标签,导致意外的行为或语法错误:

    eval("{ \"foo\": 42 }"); // syntax error: invalid label
    eval("({ \"foo\": 42 })"); // OK
    

函数l (f, n) {n&, l (n - 1 f, f (n));}

l(函数(循环){警报(循环);}, 5); < / p >

警报5、4、3、2、1

要将浮点数转换为整数,您可以使用以下神秘的技巧之一(请不要这样做):

  1. 3.14 >> 0(通过2.9999999999999999在祝辞5吗?)
  2. 3.14 | 0(通过在JavaScript中将浮点数转换为整数的最佳方法是什么?)
  3. 3.14 & -1
  4. 3.14 ^ 0
  5. ~~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

所有函数语句在代码运行之前都会被解析——JavaScript文件底部的函数将在第一个语句中可用。另一方面,它将无法利用某些动态上下文,例如围绕with语句——解析函数时with尚未执行。

函数表达式在遇到它们的地方内联执行。在此之前它们是不可用的,但它们可以利用动态上下文。

原型继承(由Douglas Crockford推广)完全改变了你在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'

window.name的值在页面更改时保持不变,如果在同一个域中(如果在iframe中,使用document.getElementById("your frame's ID").contentWindow.name访问它),可以由父窗口读取,并且仅受可用内存的限制。

在遍历对象的属性时,一定要使用hasOwnProperty方法:

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

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

当你想从数组中移除一个元素时,可以使用< em > < / em >删除操作符,如下所示:

var numbers = [1,2,3,4,5];
delete numbers[3];
//numbers is now [1,2,3,undefined,5]

正如你所看到的,元素被删除了,但是数组中留下了一个洞,因为元素被替换为未定义的值。

因此,要解决这个问题,不要使用删除,而是使用< em >拼接< / em >数组方法…是这样的:

var numbers = [1,2,3,4,5];
numbers.splice(3,1);
//numbers is now [1,2,3,5]

< em >拼接< / em >的第一个参数是数组[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-condition和反向条件下更快——也就是说,如果循环的顺序对你不重要。在我大约50%的代码中,它通常不存在。

即。

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的建议语法扩展。接下来,这变成了

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

您可以根据异常的类型捕获异常。引用自争取民主变革运动:

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
}

注意:条件捕获子句是Netscape(因此是Mozilla/Firefox)扩展,它不是ECMAScript规范的一部分,因此不能依赖,除非在特定的浏览器上。

您可以将“任何具有整数属性和长度属性的*对象转换为适当的数组,从而赋予它所有数组方法,如push、pop、splice、map、filter、reduce等。

Array.prototype.slice.call({"0":"foo", "1":"bar", 2:"baz", "length":3 })

//返回["foo", "bar", "baz"]

这适用于jQuery对象,html集合和来自其他框架的数组对象(作为整个数组类型的一种可能的解决方案)。我说,如果它有一个长度属性,你可以把它变成一个数组,这没有关系。有很多非数组对象都有length属性,在arguments对象之外。

语法糖:内联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

因为数组也是对象,你可以用同样的方法迭代其他数组:

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的一部分,但不知为何,直到今年我才意识到。

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

arguments. called .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。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——)form:

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"快得多。

合并操作符非常酷,可以生成一些干净简洁的代码,特别是当你将它们链接在一起时:a || b || c || "default";问题在于,由于它是通过计算为bool而不是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];,它是按名称一般实例化对象的关键。我两个月前才算出来的。

本着同样的精神,在使用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

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 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转换,通常不能正常工作。解决这个问题留给读者练习。

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

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


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


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


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

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

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


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


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

模块模式 . 0模块模式

<script type="text/javascript">
(function() {


function init() {
// ...
}


window.onload = init;
})();
</script>

在没有var语句或在函数外部声明的变量和函数将在全局作用域中定义。如果一个同名的变量/函数已经存在,它将被无声地覆盖,这可能导致很难发现错误。一种常见的解决方案是将整个代码体封装到一个匿名函数中并立即执行它。这样,所有变量/函数都定义在匿名函数的作用域中,不会泄漏到全局作用域中。

要显式地在全局作用域中定义变量/函数,它们必须以window作为前缀:

window.GLOBAL_VAR = 12;
window.global_function = function() {};

命名空间

在较大的JavaScript应用程序或框架中,在名称空间中组织代码可能很有用。JavaScript没有内置模块或名称空间概念,但使用JavaScript对象很容易模拟。这将创建一个名为__abc0的命名空间,并将函数__abc1附加到它。

if (!window.ns) {
window.ns = {};
}


window.ns.foo = function() {};

通常在整个项目中使用相同的全局名称空间前缀,并为每个JavaScript文件使用子名称空间。子名称空间的名称通常与文件名匹配。

名为__abc0的文件头可能是这样的:

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'将引用该函数的未来对象实例,通常使用operator 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'都指向该对象。

我们需要明确,即使使用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 in object)"返回该属性的名称,则该属性是可枚举的。

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

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" in null)。

将任何东西转换为数字:

+anything
Number(anything)

将任何东西转换为无符号四字节整数:

anything >>> 0

将任何东西转换为字符串:

'' + anything
String(anything)

将任何东西转换为布尔值:

!!anything
Boolean(anything)

同样,使用不带“new”的类型名对于String、Number和Boolean的行为不同,返回一个基本数字、字符串或布尔值,但使用“new”将返回“盒装”的对象类型,这几乎是无用的。

我最喜欢的技巧是使用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被认为非常擅长于暴露它的所有对象,所以不管它的窗口对象本身。

因此,如果我想用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;
}
})()

现在,三个函数incrementdecrementvalue共享变量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

*注:我不知道这里的正确术语。

我知道我迟到了,但我只是无法相信+运算符的有用性除了“将任何东西转换为数字”之外没有被提及。也许这就是隐藏得有多好?

// 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的通用性-覆盖默认功能

< p >
下面是用jQuery UI的对话框小部件覆盖window.alert函数的代码。我把它作为一个jQuery插件来实现。你可以在我的博客上读到;altAlert,一个用于个性化警报消息的jQuery插件 . < / p >
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的工厂的框架似乎已经实现了这个功能;参见这个话题的例子。

闭包:

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


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


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

这里的闭包不是所谓的解构赋值([c,d] = [1,3]等价于c=1; d=3;),而是在closureGet和closurereset中出现的a仍然指向同一个变量。即使在closureSet给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是一个数组,当选择器有一个参数或多个名称时非常有用:
    • args[0]是整个选择器文本(例如:foo("bar, baz"))
    • args[1]是选择器名称(例如foo)
    • args[2]是用于包装参数的引号字符 (例如" for :foo("bar, baz")),如果没有引号,则为空字符串 (:foo(bar, baz))或未定义(如果没有参数
    • )
    • args[3]是实参,包括任何引号,(例如"bar, baz") 如果没有参数
    • 则为undefined 李< / ul > < / >
    • 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 ()的回调版本达到类似的效果,但自定义选择器要灵活得多,通常更易于阅读。

当你写回调时,你有很多代码,看起来像这样:

callback: function(){
stuff(arg1,arg2);
}

您可以使用下面的函数使它更简洁。

callback: _(stuff, arg1, arg2)

它使用javascript的function对象中一个不太为人所知的函数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);
}
}