JavaScript中的小技巧和注意点(二)

来源:互联网 发布:大学生防网络诈骗 编辑:程序博客网 时间:2024/05/21 08:48

tip:自己收集的一些前端注意事项

https://github.com/Nirvana-cn/WebTechnology

1.函数优先

函数声明和变量声明都会被提升,但是一个值得注意的细节是函数会首先被提升,然后才是变量

foo();//1var foo;function foo(){    console.log(1);}foo=function(){    console.log(2);};

会输出1而不是2,这个代码片会被引擎理解如下形式:

function foo(){    console.log(1);}foo();//1foo=function(){    console.log(2);};

注意,var foo尽管出现在function foo()…的声明之前,但它是重复的声明(因此被忽略了),因为函数声明会被提升到普通变量之前。

2.循环和闭包

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

for(var i=1;i<=5;i++){    setTimeout(function timer() {        console.log(i);    },i*1000);}

正常情况下,我们对这段代码行为的预期是分别输出数字1-5,每秒一个,每次一个,但实际上,这段代码在运行时会以每秒依次的频率输出五次6。
(1)通过立即执行函数IIFE进行修正。

for(var i=1;i<=5;i++){    (function (j) {        setTimeout(function timer() {            console.log(j);        },j*1000);    })(i);}

(2)更简单的方法,使用let生成块级作用域。

for(let i=1;i<=5;i++){    setTimeout(function timer() {        console.log(i);    },i*1000);}

3.被忽略的this

如果你把null或者undefined作为this的绑定对象传入call,apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则

function foo(){    console.log(this.a);}var a=2;foo.call(null);//2

一种非常常见的做法是使用apply()来展开一个数组,并当作参数传入一个函数;类似的,bind()可以对参数进行柯里化。
然而,总是使用null来忽略this绑定可能产生一些副作用,导致一些难以分析和追踪的bug。更安全的做法是传入一个空对象。在JavaScript中创建一个空对象最简单的方法是Object.create(null)。
Object.create(null)和 { }很像,但是并不会创建Object.prototype这个委托,所以它比 { } “更空”

4.隐式丢失

一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,从而把this绑定到全局对象或者undefined上,取决去是否是严格模式。
参数传递就是一种隐式赋值,因此传入函数时会被隐式赋值。

function foo(){    console.log(this.a)}var obj={    a:2,    foo:foo};var a='oops,global';setTimeout(obj.foo,1000);//oops,global

JavaScript环境中内置setTimeout()函数实现和下面的伪代码类似,可以看出obj.foo被赋值给了fn,所以foo中的this被绑定到了全局对象上。

function setTimeout(fn,delay){    //等待delay毫秒    fn();}

5.对象属性存在性

var obj={    a:undefined}

对象属性访问返回值可能是undefined,但是这个值有可能是属性中存储的undefined,也可能是因为属性不存在而返回undefined

"a" in obj;//trueobj.hasOwnProperty("a");//true

但是有的对象可能没有连接到Object.prototype(比如通过Object.create(null)来创建的),这种情况下形如obj.hasOwnProperty()就会失败,这时可以使用一种更加强硬的方法来进行判断。

Object.prototype.hasOwnProperty.call(obj,"a");

6.计时器

由于历史原因,setTimeout()和setInterval()的第一个参数可以作为字符串传入。如果这么做,那么这个字符串就会在指定的间隔之后进行求值(相当于执行eval())

setTimeout('console.log(123)',1000)

7.值和类型

JavaScript中的变量是没有类型的,只有值才有,变量可以随时持有任何类型的值,即一个变量可以现在被赋值为字符串类型值,随后又被赋值为数字类型值。
在对变量执行typeof操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型,因为JavaScript中的变量没有类型。
已在作用域中声明但还没赋值的变量是undefined。相反,还没有在作用域中声明过的变量是undeclared,让人抓狂的是typeof处理undeclared变量的方式同样返回“undefined”

var a;typeof a;//undefinedtypeof b;//undefined