全局变量
全局变量怎么来的
通过var声明 或未声明的变量都会在全局window,this上产生全局变量。
- 如写法:a = 0; // 全局对象的属性a
- 如写法:var a = b = 0; // 全局对象的属性b
对于 var a = b = 0;来说,全局变量a是不能被删除的,隐式全局变量b是可以删除的

全局变量有什么问题
对于一个系统的不同组成部分,如果都有相同命名的全局变量名,如partA,partB都有一个全局变量result,后者会替换前者,显而易见的,基于第一个全局变量的所有运算都会是错的,这当然不是我们想要的结果。

在ES5严格模式下,未声明的变量(如在前面的代码片段中的两个反面教材)工作时会抛出一个错误。
使用命名空间来避免全局变量
变量的声明
在js,可以在函数的任何位置声明多个var语句,并且它们就好像是在函数顶部声明一样发挥作用,这种行为称为 hoisting(悬置/置顶解析/预解析)。
globalVar = 'global';
function testGlobalVar() {
console.log('first', globalVar);
console.log('global', this.globalVar);
var globalVar = 'sofia';
console.log('second', globalVar);
console.log('global', this.globalVar);
}
testGlobalVar();
运行结果:
first undefined
global global
second sofia
global global
对于globalVar,生成了两个变量,一个是值为global的全局的globalVar,还有,作用域在testGlobalVar()函数内的局部变量globalVar,我们看到,局部变量globalVar一开始并未声明,所以first的值是undefined,随后被声明且附了值,second的时候值变为了Sofia。
为了代码的可读性,还是建议规范,声明统一放在首行,即:
function testGlobalVar() {
var globalVar;
globalVar = 'sofia';
console.log('second', globalVar);
console.log('global', this.globalVar);
}
For 循环的优化
For可以循环取得数组或是数组类似对象的值,譬如arguments和HTMLCollection对象,测试循环一组数组,这里举例,循环页面上所有的div元素(256个), 几种循环的写法耗时比较如下:
循环过程中每次读取页面元素
function fn1() {
console.time('for fn1');
for (var i = 0; i < document.querySelectorAll('div').length;i++) {
if (i === 256) {
console.log('complete!');
}
}
console.timeEnd('for fn2');
}
运行结果:
for fn2: 17.693115234375ms
在这个循环过程中,只检索了一次长度值,赋值给变量max,每次循环时根据变量做比较
function fn2() {
console.time('for fn2');
for (var i = 0, max = document.querySelectorAll('div').length; i<=max; i++) {
if (i === 256) {
console.log('complete!');
}
}
console.timeEnd('for fn2');
}
运行结果:
for fn2: 0.73388671875ms
转换一下循环的比较方式,递减与0做比较
function fn3() {
console.time('for fn3');
var i, max = document.querySelectorAll('div').length;
for (i = max; i--; ) {
if (i === 256) {
console.log('complete!');
}
}
console.timeEnd('for fn3');
}
运行结果:
for fn3: 0.177001953125ms
For-in循环
For-in循环应该用在非数组对象的遍历上,使用For-in进行循环也被称为“枚举”。
建议的是,数组循环用For,对象循环用For-in;
既然for-in是循环的对象的属性,那么,我们可以尝试循环遍历一遍man对象的属性;
var man = {hands: 2, legs: 2, heads: 1};
for(var i in man) {
console.log(i, man[i]);
}
运行结果:
hands 2
legs 2
heads 1
扩展一下,如果,Singer对象是继承别的对象而来或者Singer原型链上有别的属性方法,遍历出来的结果会是怎样的:codepen.io/sofia92/pen…
function Human() {
this.eyes = 1;
this.hands = 2;
this.heads = 1;
}
Human.prototype.walk = function () {
console.log('Human can walk');
};
function Singer() {
this.type = 'singer';
this.signing = function () {
console.log('Singer sings well!');
}
}
Singer.prototype = new Human();
var singer1 = new Singer();
console.log('signing1 eyes', singer1.eyes) // "signing1 eyes" 1
singer1.walk(); // "Human can walk"
singer1.signing(); // "Singer sings well!"
for(var attr in singer1) {
console.log(attr,':', singer1[attr]);
}
// type : singer
// signing : ƒ () {
// console.log('Singer sings well!');
// }
// eyes : 1
// hands : 2
// heads : 1
// walk : ƒ () {
// console.log('Human can walk');
// }
console.log('----------------------------');
for(var attr in singer1) {
if(singer1.hasOwnProperty(attr)) {
console.log(attr,':', singer1[attr]);
}
}
// type : singer
// signing : ƒ () {
// console.log('Singer sings well!');
// }
针对上述结果,我们可以发现,当对象继承自别的对象时,这个时候遍历对象属性会同时拥有父类当属性与方法,可以通过hasOwnProperty()方法过滤出自己的属性与方法。