深入理解javascript-基础笔记1

158 阅读3分钟

全局变量

全局变量怎么来的

通过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()方法过滤出自己的属性与方法。