ECMAScriptJavaScript另类用法

 

JavaScript 秘密花园

  • pl
  • en
  • ru
  • zh
  • tr
  • es
  • ja
  • ko
  • fi

#topHide
Menu

– 简介

-   [关于作者](http://bonsaiden.github.com/JavaScript-Garden/zh/#intro.authors)
-   [贡献者](http://bonsaiden.github.com/JavaScript-Garden/zh/#intro.contributors)
-   [许可](http://bonsaiden.github.com/JavaScript-Garden/zh/#intro.license)

– 对象

-   [对象使用和属性](http://bonsaiden.github.com/JavaScript-Garden/zh/#object.general)
-   [原型](http://bonsaiden.github.com/JavaScript-Garden/zh/#object.prototype)
-   [`hasOwnProperty`
    函数](http://bonsaiden.github.com/JavaScript-Garden/zh/#object.hasownproperty)
-   [`for in`
    循环](http://bonsaiden.github.com/JavaScript-Garden/zh/#object.forinloop)

– 函数

-   [函数声明与表达式](http://bonsaiden.github.com/JavaScript-Garden/zh/#function.general)
-   [`this`
    的工作原理](http://bonsaiden.github.com/JavaScript-Garden/zh/#function.this)
-   [闭包和引用](http://bonsaiden.github.com/JavaScript-Garden/zh/#function.closures)
-   [`arguments`
    对象](http://bonsaiden.github.com/JavaScript-Garden/zh/#function.arguments)
-   [构造函数](http://bonsaiden.github.com/JavaScript-Garden/zh/#function.constructors)
-   [作用域与命名空间](http://bonsaiden.github.com/JavaScript-Garden/zh/#function.scopes)

– 数组

-   [数组遍历与属性](http://bonsaiden.github.com/JavaScript-Garden/zh/#array.general)
-   [`Array`
    构造函数](http://bonsaiden.github.com/JavaScript-Garden/zh/#array.constructor)

– 类型

-   [相等与比较](http://bonsaiden.github.com/JavaScript-Garden/zh/#types.equality)
-   [`typeof`
    操作符](http://bonsaiden.github.com/JavaScript-Garden/zh/#types.typeof)
-   [`instanceof`
    操作符](http://bonsaiden.github.com/JavaScript-Garden/zh/#types.instanceof)
-   [类型转换](http://bonsaiden.github.com/JavaScript-Garden/zh/#types.casting)

– 核心

-   [为什么不要使用
    `eval`](http://bonsaiden.github.com/JavaScript-Garden/zh/#core.eval)
-   [`undefined` 和
    `null`](http://bonsaiden.github.com/JavaScript-Garden/zh/#core.undefined)
-   [自动分号插入](http://bonsaiden.github.com/JavaScript-Garden/zh/#core.semicolon)

– 其它

-   [`setTimeout` 和
    `setInterval`](http://www.cnblogs.com/zfc2201/admin/JavaScript%20%E7%A7%98%E5%AF%86%E8%8A%B1%E5%9B%AD_files/JavaScript%20%E7%A7%98%E5%AF%86%E8%8A%B1%E5%9B%AD.htm)

prev section其它next
sectionsetTimeout
setIntervalshow
menu

简介

JavaScript 秘密花园凡是一个不断更新,主要关注 JavaScript
一些奇用法的文档。
对于如何避免大规模的不当,难以觉察的题目,以及性能问题与糟糕的履为有建议,
初家可以籍此深入了解 JavaScript 的言语特征。

JavaScript 秘密花园不是所以来教而
JavaScript。为了还好之领悟这篇文章的内容, 你用事先学习 JavaScript
的基础知识。在 Mozilla 开发者网络中发生相同多元大硬的 JavaScript
学习向导。

译者注: 文中涉嫌的 ES5 是 ECMAScript 5 的简写,是 ECMAScript
标准语言的下一版本,正在开发被。 JavaScript 是是标准语言的一个方言。

有关作者

当下首文章的作者是简单各 Stack Overflow 用户,
伊沃·韦特泽尔 Ivo
Wetzel(写作) 和
张易江 Zhang Yi
Jiang(设计)。

贡献者

  • Caio Romão (拼写检查)
  • Andreas Blixt (语言修正)

中文翻译

  • 三生石上

以此中文翻译由三生石上单独完成,博客园首发,转载请注明出处。

许可

JavaScript 秘密花园当 MIT
license
许可协商下发表,并存放在
GitHub 开源社区。
如果你发现错误或者打字错误,请新建一个职责单或发一个抓取请求。
你吧足以于 Stack Overflow 的 JavaScript
聊天室找到我们。

对象

目标下以及总体性

JavaScript 中颇具变量都是目标,除了个别独例外
null

undefined

false.toString()// 'false'
[1,2,3].toString();// '1,2,3'

functionFoo(){}
Foo.bar =1;
Foo.bar;// 1

一个普遍的误解是数字之字面值(literal)不是目标。这是盖 JavaScript
解析器的一个荒唐, 它试图以接触操作符浅析为浮点数字面值的一模一样部分。

2.toString();// 出错:SyntaxError

产生为数不少变通方法可以给数字的字面值看起像对象。

2..toString();// 第二个点号可以正常解析
2.toString();// 注意点号前面的空格
(2).toString();// 2先被计算

目标作为数据类

JavaScript
的靶子好当哈希表运用,主要用来保存命名的键和价值的应和关系。

用对象的字面语法 – {} – 可以创建一个简约对象。这个新创办的靶子从
Object.prototype
继承下,没有另外自打定义属性。

var foo ={};// 一个空对象

// 一个新对象,拥有一个值为12的自定义属性'test'
var bar ={test:12}; 

走访属性

发生半点种植方式来拜访对象的特性,点操作符或者中括号操作符。

var foo ={name:'Kitten'}
foo.name;// kitten
foo['name'];// kitten

varget='name';
foo[get];// kitten

foo.1234;// SyntaxError
foo['1234'];// works

区区栽语法是等价格的,但是中括号操作符在脚两栽情况下依然有效 –
动态设置属性 –
属性名不是一个使得之变量名(译者注:照属性名遭蕴含空格,或者属于性名是
JS 的显要词)

译者注:
JSLint 语法检测工具中,点操作符是推荐做法。

删去属性

删除属性的唯一方式是采取 delete 操作符;设置属性为 undefined 或者
null 并无可知真的去除属性, 而仅仅是移除了性能和价值的涉。

var obj ={
    bar:1,
    foo:2,
    baz:3
};
obj.bar =undefined;
obj.foo =null;
delete obj.baz;

for(var i in obj){
    if(obj.hasOwnProperty(i)){
        console.log(i,''+ obj[i]);
    }
}

点的输出结果有 bar undefinedfoo null – 只有 baz
被真正的去了,所以由输出结果负没有。

属性名的语法

var test ={
    'case':'I am a keyword so I must be notated as a string',
    delete:'I am a keyword too so me'// 出错:SyntaxError
};

对象的性质名好用字符串或者普通字符声明。但是出于 JavaScript
解析器的其它一个左设计, 上面的亚栽声明方式以 ECMAScript 5 之前见面丢弃来
SyntaxError 的错误。

以此荒唐的原故是 delete 是 JavaScript
语言的一个关键词;因此为了在更低版本的 JavaScript 引擎下啊能够正常运作,
必须运用字符串字面值声明方式。

原型

JavaScript 不含传统的近乎继承模型,而是采用 prototypal 原型模型。

尽管如此当时常被作为是 JavaScript
的通病被提及,其实基于原型的累模型比传统的类继承还要强大。
实现传统的切近继承模型是大简短,但是贯彻 JavaScript
中的原型继承则要艰难的大半。 (It is for example fairly trivial to build a
classic model on top of it, while the other way around is a far more
difficult task.)

是因为 JavaScript
是绝无仅有一个叫大规模利用的依据原型继承的言语,所以理解两种持续模式之反差是索要自然时间的。

率先只不同之处在于 JavaScript 使用原型链的延续方式。

注意: 简单的使用 Bar.prototype = Foo.prototype
将会见导致个别独对象共享相同的原型。
因此,改变任意一个对象的原型都见面影响及其它一个靶的原型,在多数情形下就不是意在的结果。

functionFoo(){
    this.value =42;
}
Foo.prototype ={
    method:function(){}
};

functionBar(){}

// 设置Bar的prototype属性为Foo的实例对象
Bar.prototype =newFoo();
Bar.prototype.foo ='Hello World';

// 修正Bar.prototype.constructor为Bar本身
Bar.prototype.constructor =Bar;

var test =newBar()// 创建Bar的一个新实例

// 原型链
test [Bar的实例]
    Bar.prototype [Foo的实例] 
        { foo:'Hello World'}
        Foo.prototype
            {method:...};
            Object.prototype
                {toString:.../* etc. */};

上面的事例中,test 对象从 Bar.prototypeFoo.prototype
继承下来;因此, 它会访问 Foo 的原型方法
method。同时,它吗能够访问那个概念在原型上的 Foo 实例属性
value。 需要注意的凡 new Bar() 不会开创出一个新的 Foo
实例,而是 重复使用它原型上之挺实例;因此,所有的 Bar
实例都见面共享相同value 属性。

注意: 不要使用 Bar.prototype = Foo,因为就不会见实施 Foo
的原型,而是依靠于函数 Foo。 因此原型链将见面回忆至 Function.prototype
而不是 Foo.prototype,因此 method 将无见面当 Bar 的原型链上。

性查找

当找一个靶的性时,JavaScript
向上遍历原型链,直到找到给定名称的特性为止。

暨找寻到原型链的顶部 – 也就算是 Object.prototype
但是仍然没找到指定的习性,就会返回
undefined。

原型属性

当原型属性用来创造原型链时,可以将任何种的值赋给它们(prototype)。
然而将原子类型赋给 prototype 的操作以会让忽视。

functionFoo(){}
Foo.prototype =1;// 无效

比方以对象赋值给 prototype,正使上面的例证所示,将会动态的创建原型链。

性能

设一个性质在原型链的上面,则于查找时间将带来不利影响。特别的,试图取一个休设有的性能将会遍历整个原型链。

并且,当使用
for in
循环遍历对象的性质时,原型链上的所有属性都拿被聘。

壮大内置类型的原型

一个误特性深受经常以,那就是扩张 Object.prototype
或者其他内置类型的原型对象。

这种技术为称之为 monkey
patching
并且会毁掉封装。虽然它于大的动及有的 JavaScript 类库中仍
Prototype,
但是本身还不以为也停放类型丰富一些非标准的函数是个好主意。

扩张内置类型的唯一理由是为了跟初的 JavaScript 保持一致,比如
Array.forEach

译者注:立是编程领域常用的平栽艺术,称之为
Backport,也就是以新的补丁添加到老版本中。

总结

以形容复杂的 JavaScript 应用之前,充分领略原型链继承的办事法是每个
JavaScript 程序员必修的学业。
要提防原型链过长带来的特性问题,并懂得什么样通过缩短原型链来提高性能。
更进一步,绝对不要恢宏内置类型的原型,除非是以跟新的 JavaScript
引擎兼容。

hasOwnProperty 函数

为判定一个目标是否含有自定义属性而不是原型链达到的性质,
我们要利用持续自 Object.prototypehasOwnProperty 方法。

注意: 通过判断一个性能是否 undefined不够的。
因为一个性质可能真正在,只不过它的价值为装也 undefined

hasOwnProperty 是 JavaScript
中唯一一个拍卖属性但是摸原型链的函数。

// 修改Object.prototype
Object.prototype.bar =1; 
var foo ={goo:undefined};

foo.bar;// 1
'bar'in foo;// true

foo.hasOwnProperty('bar');// false
foo.hasOwnProperty('goo');// true

只有 hasOwnProperty
可以让有是和期之结果,这在遍历对象的属性时见面异常有因此。
没有任何方法可以为此来扫除原型链上的习性,而不是概念在对象自身上的性能。

hasOwnProperty 作为性能

JavaScript 不会保护 hasOwnProperty
被非法占有,因此若一个目标碰巧存在是特性, 就用运用外部
hasOwnProperty 函数来抱科学的结果。

var foo ={
    hasOwnProperty:function(){
        returnfalse;
    },
    bar:'Here be dragons'
};

foo.hasOwnProperty('bar');// 总是返回 false

// 使用其它对象的 hasOwnProperty,并将其上下为设置为foo
{}.hasOwnProperty.call(foo,'bar');// true

结论

当检查对象上某属性是否在时时,hasOwnProperty唯一可用的点子。
同时在应用 for in
loop
遍历对象时,推荐总是使用 hasOwnProperty 方法,
这将见面避免原型目标扩大带来的干扰。

for in 循环

in 操作符一样,for in
循环同样在摸对象属性时遍历原型链上的具有属性。

注意: for in 循环不会遍历那些 enumerable 设置为 false
的特性;比如数组的 length 属性。

// 修改 Object.prototype
Object.prototype.bar =1;

var foo ={moo:2};
for(var i in foo){
    console.log(i);// 输出两个属性:bar 和 moo
}

由于无容许移 for in
自身的表现,因此产生必不可少过滤出那些未指望出现在循环体中之属性, 这得经
Object.prototype 原型上之
hasOwnProperty
函数来形成。

注意: 由于 for in
总是要遍历整个原型链,因此要一个对象的后续层次太要命的说话会潜移默化性。

使用 hasOwnProperty 过滤

// foo 变量是上例中的
for(var i in foo){
    if(foo.hasOwnProperty(i)){
        console.log(i);
    }
}

其一本子的代码是绝无仅有正确的写法。由于我们使用了
hasOwnProperty,所以这次输出 moo。 如果非以
hasOwnProperty,则即时段代码在原生对象原型(比如
Object.prototype)被扩张时或许会见错。

一个广泛采取的类库 Prototype
就扩大了原生的 JavaScript 对象。
因此,但以此类库被含有在页面被时时,不动 hasOwnProperty 过滤的
for in 循环难免会有题目。

总结

推荐总是使用
hasOwnProperty。不要对代码运行的环境做其他要,不要使原生对象是不是业已给扩大了。

函数

函数声明和表达式

函数是JavaScript中的世界级对象,这象征可以把函数像其他值一样传递。
一个广的用法是管匿名函数作为回调函数传递对异步函数中。

函数声明

function foo(){}

点的方法会在尽前被
解析(hoisted),因此她在叫当下齐下文的任意一个地方,
即使在函数定义体的方面被调用也是针对的。

foo();// 正常运行,因为foo在代码运行前已经被创建
function foo(){}

函数赋值表达式

var foo =function(){};

是事例把一个匿名的函数赋值给变量 foo

foo;// 'undefined'
foo();// 出错:TypeError
var foo =function(){};

由于 var 定义了一个扬言语句,对变量 foo 的分析是在代码运行之前,因此
foo 变量在代码运行时早已于定义了了。

但出于赋值语句只当运转时实行,因此于对应代码执行之前, foo
的值缺省啊
undefined。

取名函数的赋值表达式

除此以外一个特别的情事是以命名函数赋值给一个变量。

var foo =function bar(){
    bar();// 正常运行
}
bar();// 出错:ReferenceError

bar 函数声明外是不可见的,这是盖咱们都拿函数赋值给了 foo
然而在 bar 内部还是凸现。这是出于 JavaScript 的
取名处理
所予, 函数号称以函数内总是可见的。

this 的工作规律

JavaScript 有同等仿了不同让任何语言的对 this 的拍卖体制。
栽不同的状下 ,this 指向的各不相同。

全局范围外

this;

当于整个范围外动 this,它将会晤针对全局对象。

译者注:浏览器中运作的 JavaScript
脚本,这个全局对象是 window

函数调用

foo();

这里 this 也会对全局对象。

ES5 注意: 在从严模式下(strict mode),不存在全局变量。 这种景象下
this 将会是 undefined

方调用

test.foo(); 

本条事例中,this 指向 test 对象。

调用构造函数

new foo(); 

而函数倾向被跟 new 关键词一片下,则我们遂是函数是
构造函数。
在函数内部,this 指向新创建的对象。

显式的安装 this

function foo(a, b, c){}

var bar ={};
foo.apply(bar,[1,2,3]);// 数组将会被扩展,如下所示
foo.call(bar,1,2,3);// 传递到foo的参数是:a = 1, b = 2, c = 3

当使用 Function.prototype 上的 call 或者 apply 方法时,函数内的
this 将会被 显式设置为函数调用的第一单参数。

因此函数调用的平整以上例中已休适用了,在foo 函数内 this
被安装成了 bar

注意: 在靶的字面声明语法中,this 不能于是来针对对象自我。 因此
var obj = {me: this} 中的 me 不会指向 obj,因为 this
只可能出现在上述的五种情形被。
译者注:其一例子中,如果是当浏览器中运作,obj.me
等于 window 对象。

大面积误解

尽管大部分之动静还说的过去,不过第一个规则(译者注:此处指的应该是第二独规则,也尽管是一直调用函数时,this
指向全局对象)
被看是JavaScript语言另一个误设计之地方,因为它从来即便没有实际的用。

Foo.method =function(){
    function test(){
        // this 将会被设置为全局对象(译者注:浏览器环境中也就是 window 对象)
    }
    test();
}

一个泛的误解是 test 中的 this 将见面指向 Foo
对象,实际上不是立规范的。

为了在 test 中获得对 Foo 对象的援,我们需要在 method
函数内部创立一个有的变量指向 Foo 对象。

Foo.method =function(){
    var that =this;
    function test(){
        // 使用 that 来指向 Foo 对象
    }
    test();
}

that 只是我们随便起的名,不过这个名字叫广泛的用来负于外部的 this
对象。 在
闭包
一节约,我们可见见 that 可以看成参数传递。

计的赋值表达式

另一个圈起竟然的地方是函数别名,也尽管是以一个方法赋值叫一个变量。

var test = someObject.methodTest;
test();

上例中,test 就像一个通常的函数被调用;因此,函数内之 this
将不再让针对到 someObject 对象。

虽然 this
的晚绑定特性似乎并无团结,但是就确实冲原型继承凭的土。

functionFoo(){}
Foo.prototype.method =function(){};

functionBar(){}
Bar.prototype =Foo.prototype;

newBar().method();

method 被调用时,this 将会指向 Bar 的实例对象。

闭包和援

闭包是 JavaScript
一个死主要的特征,这意味着当前作用域总是能访问外部作用域中之变量。
因为
函数
是 JavaScript 中绝无仅有抱有自己作用域的结构,因此闭包的开创依赖让函数。

如法炮制私有变量

functionCounter(start){
    var count = start;
    return{
        increment:function(){
            count++;
        },

        get:function(){
            return count;
        }
    }
}

var foo =Counter(4);
foo.increment();
foo.get();// 5

这里,Counter 函数回两单闭包,函数 increment 和函数 get
这半独函数都保持正 对外部作用域 Counter
的援,因此总好看这个打算域内定义的变量 count.

何以未得以在表面看私有变量

为 JavaScript 中未得以针对作用域进行引用或赋值,因此尚未辙于外表看
count 变量。 唯一的路子就经过那片单闭包。

var foo =newCounter(4);
foo.hack =function(){
    count =1337;
};

上面的代码不会转定义在 Counter 作用域中之 count 变量的价值,因为
foo.hack 没有 定义在深作用域内。它以会晤创或者覆盖全局变量
count

循环中之闭包

一个普遍的错出现于循环中利用闭包,假要我们要以历次循环中调用循环序号

for(var i =0; i <10; i++){
    setTimeout(function(){
        console.log(i);  
    },1000);
}

上面的代码不见面输出数字 09,而是会输出数字 10 十次。

console.log 被调用的时段,匿名函数保持对表面变量 i 的引用,此时
for巡回已经完结, i 的价值为涂改成了 10.

为博取想要之结果,需要在每次循环中创造变量 i拷贝

免引用错误

以是的得到循环序号,最好以
匿名包裹器(译者注:实际就是是咱便说之于实施匿名函数)。

for(var i =0; i <10; i++){
    (function(e){
        setTimeout(function(){
            console.log(e);  
        },1000);
    })(i);
}

标的匿名函数会应声实施,并把 i 作为它们的参数,此时函数内 e
变量就所有了 i 的一个拷贝。

当传递让 setTimeout 的匿名函数执行时,它就有着了针对性 e
的援,而这个价是不会叫循环改变的。

起另一个方式好同样的工作;那就是是由匿名包装器中回到一个函数。这与点的代码效果同样。

for(var i =0; i <10; i++){
    setTimeout((function(e){
        returnfunction(){
            console.log(e);
        }
    })(i),1000)
}

arguments 对象

JavaScript 中每个函数内且能够访问一个专门变量
arguments。这个变量维护在有传递到是函数中之参数列表。

注意: 由于 arguments 已经深受定义为函数内的一个变量。 因此通过 var
关键字定义 arguments 或者将 arguments 声明也一个款式参数,
都将促成原生的 arguments 不见面让创造。

arguments 变量不是一个数组(Array)。
尽管以语法上她产生数组相关的属于性 length,但它们不从 Array.prototype
继承,实际上她是一个目标(Object)。

因此,无法对 arguments 变量使用标准的数组方法,比如 push, pop 或者
slice。 虽然以 for
循环遍历也是得的,但是为了重新好的下数组方法,最好把她转发为一个真的的数组。

中转为数组

下的代码用会见创一个新的多次组,包含有 arguments 对象被的因素。

Array.prototype.slice.call(arguments);

此转化比较,在性质不好的代码中不推荐这种做法。

传递参数

下用参数从一个函数传递及任何一个函数,是推荐的做法。

function foo(){
    bar.apply(null, arguments);
}
function bar(a, b, c){
    // do stuff here
}

其它一个技巧是以采用 callapply,创建一个高速的解绑定包装器。

functionFoo(){}

Foo.prototype.method =function(a, b, c){
    console.log(this, a, b, c);
};

// Create an unbound version of "method" 
// 输入参数为: this, arg1, arg2...argN
Foo.method =function(){

    // 结果: Foo.prototype.method.call(this, arg1, arg2... argN)
    Function.call.apply(Foo.prototype.method, arguments);
};

译者注:上面的 Foo.method
函数和下部代码的成效是同等的:

Foo.method =function(){
    var args =Array.prototype.slice.call(arguments);
    Foo.prototype.method.apply(args[0], args.slice(1));
};

自动更新

arguments 对象呢该中间属性与函数形式参数创建 gettersetter
方法。

因而,改变形参的值会影响到 arguments 对象的价,反之亦然。

function foo(a, b, c){
    arguments[0]=2;
    a;// 2                                                           

    b =4;
    arguments[1];// 4

    var d = c;
    d =9;
    c;// 3
}
foo(1,2,3);

性能真相

arguments 对象总会叫创造,除了少数只非常情况 –
作为有变量声明与作形式参数。 而不管它是不是来给应用。

argumentsgetterssetters 方法总会受创佳;因此下
arguments 对性能不会见起啊影响。 除非是内需对 arguments
对象的性能进行频繁做客。

ES5 提示: 这些 getterssetters 在严格模式下(strict
mode)不会见受创造。

译者注:
MDC 中对
strict mode 模式下 arguments 的叙说有助于我们的掌握,请看下代码:

// 阐述在 ES5 的严格模式下 `arguments` 的特性
function f(a){
  "use strict";
  a =42;
  return[a, arguments[0]];
}
var pair = f(17);
assert(pair[0]===42);
assert(pair[1]===17);

可,的确有同种状况会明白的震慑现代 JavaScript 引擎的属性。这便是运用
arguments.callee

function foo(){
    arguments.callee;// do something with this function object
    arguments.callee.caller;// and the calling function object
}

function bigLoop(){
    for(var i =0; i <100000; i++){
        foo();// Would normally be inlined...
    }
}

地方代码中,foo 不再是一个只的内联函数
inlining(译者注:这里因的是解析器可以举行内联处理),
因为她需理解她自己和其的调用者。
这不光抵消了内联函数带来的性提升,而且破坏了包,因此今函数可能要赖让特定的上下文。

因此强烈提议大家不要使用 arguments.callee 和它们的性。

ES5 提示: 在严格模式下,arguments.callee 会报错
TypeError,因为其既为废弃了。

构造函数

JavaScript 中的构造函数和其余语言中的构造函数是殊的。 通过 new
关键字方式调用的函数都给认为是构造函数。

于构造函数内部 – 也就是是被调用的函数内 – this 指向新创造的靶子
Object。 这个新创建的对象的
prototype
被针对到构造函数的 prototype

要被调用的函数没有显式的 return 表达式,则隐式的会返回 this 对象 –
也就是初创的靶子。

functionFoo(){
    this.bla =1;
}

Foo.prototype.test =function(){
    console.log(this.bla);
};

var test =newFoo();

点代码把 Foo 作为构造函数调用,并安装新创造目标的 prototype
Foo.prototype

显式的 return 表达式将见面影响返回结果,但仅限被返回的凡一个目标。

functionBar(){
    return2;
}
newBar();// 返回新创建的对象

functionTest(){
    this.value =2;

    return{
        foo:1
    };
}
newTest();// 返回的对象

译者注:new Bar()
返回的是初创办的目标,而休是数字之字面值 2。 因此
new Bar().constructor === Bar,但是只要回到的是数字对象,结果虽差了,如下所示

functionBar(){
    returnnewNumber(2);
}
newBar().constructor ===Number

译者注:此处得到的
new Test()凡函数返回的对象,而未是透过new重要字新创建的对象,因此:

(newTest()).value ===undefined
(newTest()).foo ===1

如果 new 被留漏了,则函数不会回去新创建的目标。

functionFoo(){
    this.bla =1;// 获取设置全局参数
}
Foo();// undefined

虽上例在小情况下吧克正常运行,但是由 JavaScript 中
this
的工作规律, 这里的 this 指向大局对象

工厂模式

以不动 new 关键字,构造函数必须显式的回到一个价值。

functionBar(){
    var value =1;
    return{
        method:function(){
            return value;
        }
    }
}
Bar.prototype ={
    foo:function(){}
};

newBar();
Bar();

上面两种对 Bar 函数的调用返回的价值了一致,一个新创建的兼具 method
属性的对象被归,
其实这里创办了一个闭包。

还需要留意, new Bar()
不会变更返回对象的原型(译者注:啊就是回到对象的原型不会见指向
Bar.prototype)。
因为构造函数的原型会受指向到刚创建的初目标,而此的 Bar
没有拿这新目标回来(译者注:而是回到了一个含
method 属性的自定义对象)。

以上面的事例中,使用或无以 new 关键字没有功能性的分。

译者注:面两种方法创造的靶子非能够访问
Bar 原型链上之习性,如下所示:

var bar1 =newBar();
typeof(bar1.method);// "function"
typeof(bar1.foo);// "undefined"

var bar2 =Bar();
typeof(bar2.method);// "function"
typeof(bar2.foo);// "undefined"

经工厂模式创造新目标

咱俩常常听到的同漫漫忠告是不要使用 new
关键字来调用函数,因为若忘记行使其便见面造成错误。

以创造新目标,我们得创建一个工厂方法,并且于章程外组织一个新目标。

functionFoo(){
    var obj ={};
    obj.value ='blub';

    varprivate=2;
    obj.someMethod =function(value){
        this.value = value;
    }

    obj.getPrivate =function(){
        returnprivate;
    }
    return obj;
}

则上面的法子较起 new
的调用方式不轻失误,并且可充分利用个人变量带来的利,
但是乘兴而来的凡局部坏的地方。

  1. 会晤占更多之内存,因为新创建的靶子不能共享原型上之不二法门。
  2. 为了促成连续,工厂方法需要由另外一个目标拷贝所有属性,或者将一个对象作为新创建目标的原型。
  3. 舍原型链仅仅是为防止遗漏 new
    带来的问题,这犹如同言语本身的思相背弃。

总结

尽管如此遗漏 new
关键字也许会见促成问题,但眼看并不是舍采用原型链的借口。
最终采取啊种办法在应用程序的需要,选择同一种代码书写风格并坚持下来才是极度要紧之。

作用域与命名空间

尽管 JavaScript 支持一对准花括号创建的代码段,但是连无支持块级作用域;
而光支持 函数作用域

function test(){// 一个作用域
    for(var i =0; i <10; i++){// 不是一个作用域
        // count
    }
    console.log(i);// 10
}

注意: 如果无是当赋值语句被,而是于 return
表达式或者函数参数中,{...} 将会当代码段解析,
而不是作为对象的字面语法解析。如果考虑到
电动分号插入,这或会见导致部分科学发现的错。

译者注:如果 return 对象的左括如泣如诉以及
return 不在一行上就是见面出错。

// 译者注:下面输出 undefined
function add(a, b){
    return 
        a + b;
}
console.log(add(1,2));

JavaScript
中尚无显式的命名空间定义,这就算表示所有目标都定义在一个全局共享的命名空间下面。

每次引用一个变量,JavaScript 会向上遍历整个作用域直到找到这个变量为止。
如果到达全局作用域但是这变量仍无找到,则会丢来 ReferenceError 异常。

隐式的全局变量

// 脚本 A
foo ='42';

// 脚本 B
var foo ='42'

地方两段落脚论效果不同。脚本 A 在全局用意域内定义了变量
foo,而脚本 B 在当前作用域内定义变量 foo

复强调,上面的效益净两样,不使用 var
声明变量将见面促成隐式的全局变量产生。

// 全局作用域
var foo =42;
function test(){
    // 局部作用域
    foo =21;
}
test();
foo;// 21

在函数 test 内不使用 var 关键字声明 foo
变量将会见蒙外部的同名变量。
起初即看起并无是甚题目,但是当起众多行代码时,不下 var
声明变量将会晤带来不便跟踪的 BUG。

// 全局作用域
var items =[/* 数组 */];
for(var i =0; i <10; i++){
    subLoop();
}

function subLoop(){
    // subLoop 函数作用域
    for(i =0; i <10; i++){// 没有使用 var 声明变量
        // 干活
    }
}

外表循环在率先不良调动用 subLoop 之后就是见面停止,因为 subLoop
覆盖了全局变量 i。 在亚个 for 循环中使 var
声明变量可以免这种似是而非。 声明变量时切不要遗漏 var
关键字,除非这就是期望的熏陶外部作用域的行事。

片变量

JavaScript
中有的变量只恐通过个别栽艺术宣示,一个凡是用作函数参数,另一个是经过
var 关键字声明。

// 全局变量
var foo =1;
var bar =2;
var i =2;

function test(i){
    // 函数 test 内的局部作用域
    i =5;

    var foo =3;
    bar =4;
}
test(10);

fooi 是函数 test 内的组成部分变量,而对 bar
的赋值将会见盖全局作用域内的同名变量。

变量声明提升(Hoisting)

JavaScript 会提升变量声明。这意味着 var 表达式和 function
声明还以见面吃提升至手上作用域的顶部。

bar();
var bar =function(){};
var someValue =42;

test();
function test(data){
    if(false){
        goo =1;

    }else{
        var goo =2;
    }
    for(var i =0; i <100; i++){
        var e = data[i];
    }
}

点代码在运行前用见面被转发。JavaScript 将会见把 var 表达式和
function 声明提升及手上作用域的顶部。

// var 表达式被移动到这里
var bar, someValue;// 缺省值是 'undefined'

// 函数声明也会提升
function test(data){
    var goo, i, e;// 没有块级作用域,这些变量被移动到函数顶部
    if(false){
        goo =1;

    }else{
        goo =2;
    }
    for(i =0; i <100; i++){
        e = data[i];
    }
}

bar();// 出错:TypeError,因为 bar 依然是 'undefined'
someValue =42;// 赋值语句不会被提升规则(hoisting)影响
bar =function(){};

test();

没块级作用域不仅招 var 表达式被打循环外转移到表面,而且只要一些 if
表达式更难看懂。

在本来代码中,if 表达式看起修改了方方面面变量
goo,实际上在升级规则为应用后,却是在修改一部分变量

而无晋级规则(hoisting)的知识,下面的代码看起会丢来深
ReferenceError

// 检查 SomeImportantThing 是否已经被初始化
if(!SomeImportantThing){
    varSomeImportantThing={};
}

骨子里,上面的代码正常运行,因为 var
表达式会叫升级到全局作用域的顶部。

varSomeImportantThing;

// 其它一些代码,可能会初始化 SomeImportantThing,也可能不会

// 检查是否已经被初始化
if(!SomeImportantThing){
    SomeImportantThing={};
}

译者注:当 Nettuts+ 网站有平等篇介绍
hoisting
的文章,其中的代码很有启发性。

// 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则
var myvar ='my value';  

(function(){  
    alert(myvar);// undefined  
    var myvar ='local value';  
})();  

名解析顺序

JavaScript 中的富有作用域,包括大局作用域,都来一个特意之名称
this
指向当前目标。

函数作用域内为闹默认的变量
arguments,其中蕴含了传递及函数中的参数。

遵照,当访问函数内的 foo 变量时,JavaScript 会按照下面顺序查找:

  1. 脚下打算域内是否发生 var foo 的定义。
  2. 函数形式参数是否生采取 foo 名称的。
  3. 函数自身是否叫做 foo
  4. 追思至齐一级作用域,然后从 #1 重新开。

注意: 自定义 arguments 参数将见面拦原生的 arguments 对象的创立。

取名空间

独自来一个大局作用域导致的广大错误是命名冲突。在 JavaScript中,这足以经过
匿名包装器 轻松解决。

(function(){
    // 函数创建一个命名空间

    window.foo =function(){
        // 对外公开的函数,创建了闭包
    };

})();// 立即执行此匿名函数

匿名函数被看是
表达式;因此为可调用性,它们首先会叫实施。

(// 小括号内的函数首先被执行
function(){}
)// 并且返回函数对象
()// 调用上面的执行结果,也就是函数对象

生一部分别样的调用函数表达式的主意,比如下面的鲜栽艺术语法不同,但是意义一样型一样。

// 另外两种方式
+function(){}();
(function(){}());

结论

推介以匿名包装器译者注:呢便是从实施之匿名函数)来创造命名空间。这样不仅可防范命名冲突,
而且有利于程序的模块化。

另外,使用全局变量被看是不好的习惯。这样的代码倾向被来错误和牵动大之护本。

数组

数组遍历与性

虽然在 JavaScript 中数组是是目标,但是没有好的理由去用 for in
循环
全套历数组。 相反,有一些好之理由不去使用 for in 全体历数组。

注意: JavaScript 中数组不是 关联数组。 JavaScript
中只有对象
来管理键值的照应关系。但是关乎数组是保持顺序的,而对象不是

由于 for in 循环会枚举原型链上的所有属性,唯一过滤这些性之方是动
hasOwnProperty
函数, 因此会面于普通的 for 循环慢上很多倍。

遍历

为达到遍历数组的特等性能,推荐下藏的 for 循环。

var list =[1,2,3,4,5,......100000000];
for(var i =0, l = list.length; i < l; i++){
    console.log(list[i]);
}

上面代码来一个甩卖,就是经过 l = list.length 来缓存数组的长度。

虽然 length 是多次组的一个属性,但是在每次循环中做客它还是发生性能开销。
可能新型的 JavaScript
引擎在就点上开了优化,但是咱无奈保证自己的代码是否运行于这些新近底发动机之上。

其实,不行使缓存数组长度的法门较缓存版本要舒缓很多。

length 属性

length 属性的 getter 方式会简单的归数组的长,而 setter
方式会截断数组。

var foo =[1,2,3,4,5,6];
foo.length =3;
foo;// [1, 2, 3]

foo.length =6;
foo;// [1, 2, 3]

译者注: 在 Firebug 中翻此时 foo 的值是:
[1, 2, 3, undefined, undefined, undefined]
但是其一结果并无可靠,如果你当 Chrome 的控制台查看 foo
的结果,你会意识是这么的: [1, 2, 3] 因为在 JavaScript 中 undefined
是一个变量,注意是变量不是关键字,因此地方两个结实的含义是了不等同之。

// 译者注:为了验证,我们来执行下面代码,看序号 5 是否存在于 foo 中。
5in foo;// 不管在 Firebug 或者 Chrome 都返回 false
foo[5]=undefined;
5in foo;// 不管在 Firebug 或者 Chrome 都返回 true

length 设置一个重新小之价值会截断数组,但是增大 length
属性值不会见指向数组产生影响。

结论

为了还好的特性,推荐用普通的 for 循环并缓存数组的 length 属性。
使用 for in
百分之百历数组被看是坏的代码习惯并支持被产生错误和造成性问题。

Array 构造函数

由于 Array
的构造函数在怎么样处理参数时有点模棱两可是,因此老是推荐下频繁组的字面语法 –
[] – 来创造数组。

[1,2,3];// 结果: [1, 2, 3]
newArray(1,2,3);// 结果: [1, 2, 3]

[3];// 结果: [3]
newArray(3);// 结果: [] 
newArray('3')// 结果: ['3']

// 译者注:因此下面的代码将会使人很迷惑
newArray(3,4,5);// 结果: [3, 4, 5] 
newArray(3)// 结果: [],此数组长度为 3

译者注:此的模棱两只是凭借的是几度组的鲜栽构造函数语法

鉴于只有来一个参数传递到构造函数中(译者注:指的凡 new Array(3);
这种调用方式),并且是参数是数字,构造函数会回去一个 length
属性被装也这个参数的空数组。 需要特别注意的是,此时单独来 length
属性被装,真正的数组并从未转变。

译者注:在 Firebug 中,你见面盼
[undefined, undefined, undefined],这实质上是颠三倒四的。在直达同样节发生详尽的解析。

var arr =newArray(3);
arr[1];// undefined
1in arr;// false, 数组还没有生成

这种先为安数组长度属性的做法才当个别几种植情况下有因此,比如用循环字符串,可以免
for 循环的劳动。

newArray(count +1).join(stringToRepeat);

译者注: new Array(3).join('#') 将会晤回到 ##

结论

当尽量避免使用数组构造函数创建新数组。推荐以频繁组的字面语法。它们更匮乏和精简,因此增加了代码的可读性。

类型

顶与比

JavaScript 有半点栽艺术判断两独价值是否当。

齐操作符

相当于操作符由片只相当号组合:==

JavaScript
弱类型言语,这虽表示,等于操作符会为了比较有限只价如果开展强制类型转换

""           ==   "0"           // false
0            ==   ""            // true
0            ==   "0"           // true
false        ==   "false"       // false
false        ==   "0"           // true
false        ==   undefined     // false
false        ==   null          // false
null         ==   undefined     // true
" \t\r\n"    ==   0             // true

点的表格展示了赛类型转换,这也是运用 ==
被大规模认为是不好编程习惯的机要因,
由于其的扑朔迷离转换规则,会招难以跟踪的题材。

除此以外,强制类型转换也会带动性能消耗,比如一个字符串为了与一个数组进行较,必须事先被劫持转换为数字。

严等操作符

从严等操作符由只顶号组合:===

切莫思普通的抵操作符,严格等操作符不会进展强制类型转换。

""           ===   "0"           // false
0            ===   ""            // false
0            ===   "0"           // false
false        ===   "false"       // false
false        ===   "0"           // false
false        ===   undefined     // false
false        ===   null          // false
null         ===   undefined     // false
" \t\r\n"    ===   0             // false

面的结果更加清晰并方便代码的辨析。如果少独操作数类型不同就得不抵也促进性能的晋级。

正如对象

虽然 =====
操作符都是当操作符,但是当内有一个操作数为对象时,行为即便差了。

{}==={};                   // false
newString('foo')==='foo';// false
newNumber(10)===10;       // false
var foo ={};
foo === foo;                 // true

此处当操作符比较的不是价值是否等于,而是是否属同一个身份;也就是说,只有靶的及一个实例才为当是等的。
这发生接触像 Python 中之 is 和 C 中之指针比较。

结论

强烈推荐使用严厉等操作符。如果类型需要更换,应该以可比前面显式的变,
而未是运用语言本身复杂的要挟转换规则。

typeof 操作符

typeof 操作符(和
instanceof
一起)或许是 JavaScript 中尽老的宏图缺陷,
因为几乎无容许从它那里拿走想要的结果。

尽管 instanceof 还有有最为少数底使场景,typeof
只发生一个实在的下(译者注:斯其实使用是故来检测一个目标是不是就定义或者是不是都赋值),
而这个利用可不是之所以来检查对象的品种。

注意: 由于 typeof 也得以像函数的语法被调用,比如
typeof(obj),但眼看并是一个函数调用。
那片单小括号只是用来算一个表达式的价值,这个返回值会作为 typeof
操作符的一个操作数。 实际上不存在名为 typeof 的函数。

JavaScript 类型表格

Value               Class      Type
-------------------------------------
"foo"               String     string
newString("foo")   String     object
1.2                 Number     number
newNumber(1.2)     Number     object
true                Boolean    boolean
newBoolean(true)   Boolean    object
newDate()          Date       object
newError()         Error      object
[1,2,3]             Array      object
newArray(1,2,3)  Array      object
newFunction("")    Function   function
/abc/g              RegExp     object(functioninNitro/V8)
newRegExp("meow")  RegExp     object(functioninNitro/V8)
{}                  Object     object
newObject()        Object     object

地方表格中,Type 一列表示 typeof
操作符的演算结果。可以看,这个价值当大部景象下还归 “object”。

Class 一列表示对象的中属性 [[Class]] 的值。

JavaScript 标准文档中定义: [[Class]]
的值就恐是下面字符串中的一个: Arguments, Array, Boolean,
Date, Error, Function, JSON, Math, Number, Object,
RegExp, String.

为赢得对象的 [[Class]],我们用运用定义在 Object.prototype
上之方式 toString

靶的类定义

JavaScript 标准文档只受闹了平栽获得 [[Class]] 值的点子,那便是使用
Object.prototype.toString

functionis(type, obj){
    var clas =Object.prototype.toString.call(obj).slice(8,-1);
    return obj !==undefined&& obj !==null&& clas === type;
}

is('String','test');// true
is('String',newString('test'));// true

上面例子中,Object.prototype.toString
方法让调用,this
被设置为用获得 [[Class]] 值的对象。

译者注:Object.prototype.toString
返回一栽标准格式字符串,所以上例可以经 slice
截取指定位置的字符串,如下所示:

Object.prototype.toString.call([])  // "[object Array]"
Object.prototype.toString.call({})  // "[object Object]"
Object.prototype.toString.call(2)   // "[object Number]"

ES5 提示: 在 ECMAScript 5 中,为了好,对 nullundefined
调用 Object.prototype.toString 方法, 其返回值由 Object 变成了
NullUndefined

译者注:这种转移可由 IE8 和 Firefox
4 中扣有分,如下所示:

// IE8
Object.prototype.toString.call(null)    // "[object Object]"
Object.prototype.toString.call(undefined)   // "[object Object]"

// Firefox 4
Object.prototype.toString.call(null)    // "[object Null]"
Object.prototype.toString.call(undefined)   // "[object Undefined]"

测试为定义变量

typeof foo !=='undefined'

点代码会检测 foo 是否已经定义;如果无概念而直接使用会促成
ReferenceError 的异常。 这是 typeof 唯一有因此底地方。

结论

为检测一个目标的类型,强烈推荐使用 Object.prototype.toString 方法;
因为这是唯一一个但依靠之法门。正而上面表格所示,typeof
的组成部分归值当专业文档中没定义, 因此不同之引擎实现可能不同。

只有为了检测一个变量是否业已定义,我们承诺尽量避免以 typeof 操作符。

instanceof 操作符

instanceof
操作符用来比单薄独操作数的构造函数。只有当可比起定义的靶子时才来意义。
如果用来比内置类型,将会晤和 typeof
操作符
一样用处不雅。

比较起定义对象

functionFoo(){}
functionBar(){}
Bar.prototype =newFoo();

newBar()instanceofBar;// true
newBar()instanceofFoo;// true

// 如果仅仅设置 Bar.prototype 为函数 Foo 本省,而不是 Foo 构造函数的一个实例
Bar.prototype =Foo;
newBar()instanceofFoo;// false

instanceof 比较内置类型

newString('foo')instanceofString;// true
newString('foo')instanceofObject;// true

'foo'instanceofString;// false
'foo'instanceofObject;// false

起某些消留意,instanceof 用来比较属于不同 JavaScript
上下文的对象(比如,浏览器被不同的文档结构)时将会晤拧,
因为它的构造函数不会见是与一个靶。

结论

instanceof 操作符应该仅仅因而来比来自和一个 JavaScript
上下文的自定义对象。 正而
typeof
操作符一样,任何其他的用法都应是避免的。

类型转换

JavaScript
弱类型言语,所以会以任何也许的图景下行使强制类型转换

// 下面的比较结果是:true
newNumber(10)==10;// Number.toString() 返回的字符串被再次转换为数字

10=='10';           // 字符串被转换为数字
10=='+10 ';         // 同上
10=='010';          // 同上 
isNaN(null)==false;// null 被转换为数字 0
                      // 0 当然不是一个 NaN(译者注:否定之否定)

// 下面的比较结果是:false
10==010;
10=='-10';

ES5 提示:0 开头的数字字面值会被看成八进制数字分析。 而当
ECMAScript 5 严格模式下,这个特性深受移除了。

为避免上面复杂的强制类型转换,强烈引进以适度从紧的相当于操作符。
虽然就足以免大部分底问题,但 JavaScript
的弱类型系统依然会造成一些另问题。

置类型的构造函数

坐类型(比如 Number
String)的构造函数在叫调用时,使用或无使用 new 的结果了两样。

newNumber(10)===10;     // False, 对象与数字的比较
Number(10)===10;         // True, 数字与数字的比较
newNumber(10)+0===10;// True, 由于隐式的类型转换

运用内置类型 Number 作为构造函数将会创造一个初的 Number 对象,
而在非使 new 关键字之 Number 函数还像是一个数字转换器。

此外,在比较受引入对象的字面值将会晤促成更为扑朔迷离的强制类型转换。

顶好之挑选是管要于的价值显式的转移为老三栽可能的色有。

更换为字符串

''+10==='10';// true

以一个值加上空字符串可以轻松转移为字符串类型。

变为数字

+'10'===10;// true

使用一元的加号操作符,可以将字符串转换为数字。

译者注:字符串转换为数字之常用方法:

+'010'===10
Number('010')===10
parseInt('010',10)===10  // 用来转换为整数

+'010.2'===10.2
Number('010.2')===10.2
parseInt('010.2',10)===10

易为布尔型

经过运用 操作符两不成,可以拿一个值转换为布尔型。

!!'foo';   // true
!!'';      // false
!!'0';     // true
!!'1';     // true
!!'-1'     // true
!!{};      // true
!!true;    // true

核心

怎不要用 eval

eval 函数会以此时此刻作用域中履行同样截 JavaScript 代码字符串。

var foo =1;
function test(){
    var foo =2;
    eval('foo = 3');
    return foo;
}
test();// 3
foo;// 1

但是 eval 只在被直接调用并且调用函数就是 eval
本身时,才在当前作用域中执。

var foo =1;
function test(){
    var foo =2;
    var bar =eval;
    bar('foo = 3');
    return foo;
}
test();// 2
foo;// 3

译者注:面的代码等价于在全局作用域中调用
eval,和底下两栽写法效果同样:

// 写法一:直接调用全局作用域下的 foo 变量
var foo =1;
function test(){
    var foo =2;
    window.foo =3;
    return foo;
}
test();// 2
foo;// 3

// 写法二:使用 call 函数修改 eval 执行的上下文为全局作用域
var foo =1;
function test(){
    var foo =2;
    eval.call(window,'foo = 3');
    return foo;
}
test();// 2
foo;// 3

别情形下俺们且应有避免用 eval 函数。99.9% 使用 eval
的现象都出不使用 eval 的化解方案。

伪装的 eval

定时函数
setTimeoutsetInterval 都可以接受字符串作为其的率先只参数。
这个字符串总是于全局作用域中施行,因此 eval
在这种情景下没于直接调用。

有惊无险题材

eval 也设有安全题材,因为它会履行任意染于它们的代码,
在代码字符串未知或者是来自一个不信任的源时,绝对不用使用 eval 函数。

结论

切不要采用
eval,任何利用她的代码都见面以它们的干活方法,性能和安全性方面受到质询。
如果有些状必使及 eval
才能够正常办事,首先她的设计会被质疑,这不应该凡首选的缓解方案,
一个又好之非行使 eval 的缓解方案应得到充分考虑并先使用。

undefinednull

JavaScript 有些许独象征‘空’的价值,其中较中的凡 undefined

undefined 的值

undefined 是一个价值吗 undefined 的类型。

是语言也定义了一个全局变量,它的价值是 undefined,这个变量也深受称为
undefined
但是此变量不是一个常量,也无是一个重点字。这表示它们的好随便给挂。

ES5 提示: 在 ECMAScript 5 的严酷模式下,undefined 不再是
可写的了。 但是她的名目还可以于隐形,比如定义一个函数名吧
undefined

下面的动静会回去 undefined 值:

  • 做客未修改的全局变量 undefined
  • 是因为并未定义 return 表达式的函数隐式返回。
  • return 表达式没有显式的回来外内容。
  • 走访不存的属性。
  • 函数参数没有让显式的传递值。
  • 外被装也 undefined 值的变量。

处理 undefined 值的反

出于全局变量 undefined 只是保留了 undefined 类型实际的副本,
因此对她赋予新值不会变动类型 undefined 的值。

不过,为了便利其它变量和 undefined 做比较,我们用先获取类
undefined 的值。

为了避免或对 undefined
值的改观,一个常用的技能是下一个传递到匿名包装器的附加参数。
在调用时,这个参数不会见得其它价值。

varundefined=123;
(function(something, foo,undefined){
    // 局部作用域里的 undefined 变量重新获得了 `undefined` 值

})('Hello World',42);

此外一种植及平等目的方法是当函数内用变量声明。

varundefined=123;
(function(something, foo){
    varundefined;
    ...

})('Hello World',42);

这边唯一的区别是,在抽后还要函数内并未外要使用 var
声明变量的情事下,这个版的代码会多起 4 个字节的代码。

译者注:此处小绕口,其实大粗略。如果这个函数内并未其它得声明的变量,那么
var 总共 4 个字符(包含一个空白字符) 就是专门为 undefined
变量准备的,相比上单例证多起了 4 独字节。

null 的用处

JavaScript 中的 undefined 的施用状况类似于其它语言中的 null,实际上
JavaScript 中的 null 是另外一种多少列。

它们以 JavaScript 内部有局部用状况(比如声明原型链的结束
Foo.prototype = null),但是大部分景象下还可以使用 undefined
来代替。

机动分号插入

尽管 JavaScript 有 C
的代码风格,但是它强制要求以代码中使分号,实际上可以省略它们。

JavaScript 不是一个没分号的语言,恰恰相反上它们需要分号来就解析源代码。
因此 JavaScript
解析器在碰到由于缺少分号导致的解析错误时,会自动以源代码中插入分号。

var foo =function(){
}// 解析错误,分号丢失
test()

自动插入分号,解析器重新分析。

var foo =function(){
};// 没有错误,解析继续
test()

电动的分公司插入被认为是 JavaScript
语言最大的设计缺陷之一,因为它改变代码的一言一行。

办事规律

下的代码没有分号,因此解析器需要好看清用以哪些地方插入分号。

(function(window,undefined){
    function test(options){
        log('testing!')

        (options.list ||[]).forEach(function(i){

        })

        options.value.test(
            'long string to pass here',
            'and another long string to pass'
        )

        return
        {
            foo:function(){}
        }
    }
    window.test = test

})(window)

(function(window){
    window.someLibrary ={}
})(window)

下是解析器”猜测”的结果。

(function(window,undefined){
    function test(options){

        // 没有插入分号,两行被合并为一行
        log('testing!')(options.list ||[]).forEach(function(i){

        });// <- 插入分号

        options.value.test(
            'long string to pass here',
            'and another long string to pass'
        );// <- 插入分号

        return;// <- 插入分号, 改变了 return 表达式的行为
        {// 作为一个代码段处理
            foo:function(){} 
        };// <- 插入分号
    }
    window.test = test;// <- 插入分号

// 两行又被合并了
})(window)(function(window){
    window.someLibrary ={};// <- 插入分号
})(window);//<- 插入分号

注意: JavaScript 不能够对的拍卖 return 表达式紧跟换行符的情形,
虽然当时不克算是自动分号插入的错误,但当时实在是相同栽不期望的副作用。

解析器显著改观了点代码的行为,在另外有情况下吧会见做出左的处理

眼前置括号

每当前方置括号的情形下,解析器不会自动插入分号。

log('testing!')
(options.list ||[]).forEach(function(i){})

方代码被解析器转换为同行。

log('testing!')(options.list ||[]).forEach(function(i){})

log 函数的实行结果极大可能不是函数;这种状态下就是见面并发
TypeError 的失实,详细错误信息可能是 undefined is not a function

结论

建议绝对毫无简单分号,同时为发起将花括号与对应的表达式放在一行,
对于只有发一行代码的 if 或者 else 表达式,也无应省略花括号。
这些漂亮的编程习惯不仅可提到代码的一致性,而且好预防解析器改变代码行为之错误处理。

其它

setTimeoutsetInterval

出于 JavaScript 是异步的,可以行使 setTimeoutsetInterval
来计划实施函数。

注意: 定时处理不是 ECMAScript 的标准,它们在 DOM
(文档对象模型)
被实现。

function foo(){}
var id = setTimeout(foo,1000);// 返回一个大于零的数字

setTimeout 被调用时,它会返回一个 ID 标识并且计划在将来大约
1000 毫秒后调用 foo 函数。 foo 函数只有会受实施一次

据悉 JavaScript
引擎的计时策略,以及精神上之单线程运行方式,所以任何代码的运行可能会见死此线程。
因此迫于保证函数会在 setTimeout 指定的随时让调用。

作第一个参数的函数将见面在大局作用域遭实践,因此函数内之
this
将见面因为这个大局对象。

functionFoo(){
    this.value =42;
    this.method =function(){
        // this 指向全局对象
        console.log(this.value);// 输出:undefined
    };
    setTimeout(this.method,500);
}
newFoo();

注意: setTimeout
的率先独参数是函数对象,一个常犯的错误是这么的
setTimeout(foo(), 1000), 这里回调函数是 foo
返回值,而不是foo自己。
大部分景下,这是一个机密的谬误,因为要是函数返回
undefinedsetTimeout不会报错。

setInterval 的堆调用

setTimeout 只会实施回调函数一次于,不过 setInterval – 正使名字建议的 –
会每隔 X 毫秒执行函数一软。 但是也不鼓励使用这个函数。

当回调函数的尽为封堵时,setInterval
仍然会揭晓重多之毁指令。在老有些之定时间隔情况下,这会造成回调函数被堆积起来。

function foo(){
    // 阻塞执行 1 秒
}
setInterval(foo,1000);

上面代码中,foo 会执行同一次等就于封堵了一样分钟。

foo 被卡住的时候,setInterval 仍然以社前本着回调函数的调用。
因此,当第一浅 foo 函数调用结束时,已经有 10
次函数调用在等候执行。

拍卖可能的封堵调用

绝简易吗是最好易控制的方案,是当回调函数内部以 setTimeout 函数。

function foo(){
    // 阻塞执行 1 秒
    setTimeout(foo,1000);
}
foo();

这样不光封装了 setTimeout
回调函数,而且阻止了调用指令的积聚,可以有重新多之操纵。 foo
函数现在得操纵是否继续执行还是已执行。

手工清空定时器

可透过将定时时产生的 ID 标识传递给 clearTimeout 或者 clearInterval
函数来消除定时, 至于使用谁函数取决于调用的下以的是 setTimeout
还是 setInterval

var id = setTimeout(foo,1000);
clearTimeout(id);

除掉所有定时器

鉴于没有坐的消所有定时器的点子,可以采用相同种暴力的主意来达成这无异于目的。

// 清空"所有"的定时器
for(var i =1; i <1000; i++){
    clearTimeout(i);
}

想必还出把定时器不见面在地方代码中叫免去(译者注:如定时器调用时返回的
ID 值大于 1000), 因此我们可优先保存有的定时器 ID,然后同拿消除。

藏使用 eval

setTimeoutsetInterval 也经受第一独参数为字符串的图景。
这个特点绝对不用用,因为她于里边用了 eval

注意: 由于定时器函数不是 ECMAScript
的科班,如何分析字符串参数在不同之 JavaScript 引擎实现中恐怕不同。
事实上,微软的 JScript 会使用 Function 构造函数来代替 eval 的使用。

function foo(){
    // 将会被调用
}

function bar(){
    function foo(){
        // 不会被调用
    }
    setTimeout('foo()',1000);
}
bar();

由于 eval
在这种情况下未是给直接调用,因此传递至
setTimeout 的字符串会于全局作用域受推行;
因此,上面的回调函数使用的免是概念在 bar 作用域中之有些变量 foo

建议不要当调用定时器函数时,为了为回调函数传递参数而采取字符串的样式。

function foo(a, b, c){}

// 不要这样做
setTimeout('foo(1,2, 3)',1000)

// 可以使用匿名函数完成相同功能
setTimeout(function(){
    foo(a, b, c);
},1000)

注意: 虽然为堪下这样的语法 setTimeout(foo, 1000, a, b, c)
但是匪引进这么做,因为当利用对象的性方法不时或者会见错。
译者注:此说的凡性质方法外,this 的针对错误)

结论

绝不用采用字符串作为 setTimeout 或者 setInterval
的第一单参数,
这么写的代码明显质量很不同。当得向回调函数传递参数时,可以创建一个匿名函数,在函数内推行实际的回调函数。

除此以外,应该避免采用 setInterval,因为其的定时执行不会见受 JavaScript
阻塞。

Copyright © 2011. Built with Node.jsusing a
jadetemplate. Hosted by Cramer
Development.