(转 Uncle Tom )深入理解javascript(1)学习笔记

1、最小全局变量(Minimizing Globals)                                     

Javascript 通过函数管理作用域,在函数内部声明的变量只能在内部使用,函数外面不可用。

所谓“全局变量”:

      1、任何在函数外面声明的变量

      2、未声明直接简单使用的(隐式全局变量)

 

所谓“全局对象”window

每个javascript环境有一个全局对象,当你在任意的函数外面使用this时可以访问到。

你创建的每一个全局变量都成为这个全局对象的属性。

在浏览器中,该全局对象有个附加属性叫:window。通常window指向该全局对象本身。

下面的代码片段显示如何在浏览器环境中创建和访问的全局变量:

myglobal="hello"

console.log(myglobal); //不推荐写法

console.log(window.myglobal);
console.log(window["myglobal"]);
console.log(this.myglobal);

 

 

 

2、全局变量的问题                                                                             

第三方js库,广告方脚本代码,不同类型组件等,均会定义全局变量,有可能造成命名冲突

 

3、应尽量避免使用全局变量                                                                    

function sum(x, y) {   // 不推荐写法: 隐式全局变量 
   result = x + y;
   return result;
}

 

1) 经验法则是始终使用var声明变量,正如改进版的sum()函数所演示的:

function sum(x, y) {
var result = x + y;
return result;
}

 

 但是b确实全局变量,这可能不是你希望发生的:

// 反例,勿使用 
function foo() {
var a = b = 0;  // 不推荐写法: b为隐式全局变量

   var c = (d = 0);// 不推荐写法: d为隐式全局变量
  
}

正确写法:

function foo() {
var a, b;
// ... a = b = 0; // 两个均局部变量
}

4、忘记var的副作用(Side Effects When Forgetting var)

隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力。

  • 通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的。
  • 无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的。

这表明,在技术上,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性。属性是可以通过delete操作符删除的,而变量是不能的:

// 定义三个全局变量
var global_var = 1;
global_novar = 2; // 反面教材
(function () {   
  global_fromfunc = 3; // 反面教材
}());

// 试图删除
delete global_var; // false
delete global_novar; // true
delete global_fromfunc; // true

// 测试该删除typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"

 

在ES5严格模式下,未声明的变量(如在前面的代码片段中的两个反面教材)工作时会抛出一个错误。

5、访问全局对象(Access to the Global object  )

1、全局对象使用window属性访问

2、自己写代码创建全局对象,作用是写自己的js库时,通过此种方法获取全局对象

 

    var global=(function(){
        return this;
     }());

 

 

单var形式(Single var Pattern)                 

在函数顶部使用单var语句是比较有用的一种形式,其好处在于:

1、提供一个单一的地方寻找所有局部变量

2、防止变量在定义之前使用的逻辑错误(先声明,后使用)

3、帮助你记住全局变量

单var形式长得就像下面这个样子:

function func() {
var a = 1,
b = 2,
sum = a + b,
myobject = {},
i,
j;
// function body...
}

 

function updateElement() {
var el = document.getElementById("result"),
style = el.style;
// 使用el和style干点其他什么事...
}

预解析:var散布的问题(Hoisting: A Problem with Scattered vars)

预解析(hosting): javascript中,你可以在函数的任何位置声明多个var语句,并且它们就好像在函数顶部声明一样发挥作用。这种行为叫预解析。当你使用了一个变量,之后在函数中又重新声明,就可以产生逻辑错误。只要你的变量在同一作用域(函数)内,它都被当成声明的。即使是它在var声明前使用。

// 反例
myname = "global"; // 全局变量
function func() {
alert(myname); // "undefined"
var myname = "local";
alert(myname); // "local"
}
func();

为什么第一次alert myname值为:undefined?
由于预解析(func函数中有变量myname,因此myname变量声明当被悬置到函数的顶部),myname被当成函数局部变量.

上面的代码片段执行的行为可能就像下面这样:

myname = "global"; // global variable
function func() {
var myname; // 等同于 -> var myname = undefined;
alert(myname); // "undefined"
myname = "local";
alert(myname); // "local"}
func();


结论:由于javascript默认预解析函数内所有变量,并将其声明悬挂在函数顶端,
   因此建议函数内最好预先声明好所有要使用的变量 


for循环(for Loops)


//
次佳的循环
for (var i = 0; i < myarray.length; i++) {
// 使用myarray[i]做点什么
}
如果myarrary.length值每次通过计算获取,则建议使用var max存储使用
特别当myarrary 为htmlCollection时,这种建议就更有必要了

记住Dom操作是比较昂贵的

修改后:
function looper() {
var i = 0,
max,
myarray = [];
// ...
for (i = 0, max = myarray.length; i < max; i++) {
// 使用myarray[i]做点什么
}
}

for-in循环(for-in Loops)


for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为“枚举”。

所以最好数组使用正常的for循环,对象使用for-in循环。

  有个很重要的hasOwnProperty()方法,当遍历对象属性的时候可以过滤掉从原型链上下来的属性

 

思考下面一段代码:

 

// 对象
var man = {
hands: 2,
legs: 2,
heads: 1
};

// 在代码的某个地方
//
一个方法添加给了所有对象
if (typeof Object.prototype.clone === "undefined") {
Object.prototype.clone = function () {};
}

 

 

 

在这个例子中,我们有一个使用对象字面量定义的名叫man的对象。在man定义完成后的某个地方,在对象原型上增加了一个很有用的名叫 clone()的方法。此原型链是实时的,这就意味着所有的对象自动可以访问新的方法。为了避免枚举man的时候出现clone()方法,你需要应用hasOwnProperty()方法过滤原型属性。如果不做过滤,会导致clone()函数显示出来,在大多数情况下这是不希望出现的。

 

// 1.
//
for-in 循环
for (var i in man) {
if (man.hasOwnProperty(i)) { // 过滤
console.log(i, ":", man[i]);
}
}
/* 控制台显示结果
hands : 2
legs : 2
heads : 1
*/
// 2.
//
反面例子:
//
for-in loop without checking hasOwnProperty()
for (var i in man) {
console.log(i, ":", man[i]);
}
/*
控制台显示结果
hands : 2
legs : 2
heads : 1
clone: function()
*/

 

 

 

另外一种使用hasOwnProperty()的形式是取消Object.prototype上的方法。像是:

 

for (var i in man) {
if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤
console.log(i, ":", man[i]);
}
}

 

 

 

其好处在于在man对象重新定义hasOwnProperty情况下避免命名冲突。也避免了长属性查找对象的所有方法,你可以使用局部变量“缓存”它。

 

var i, hasOwn = Object.prototype.hasOwnProperty;
for (i in man) {
if (hasOwn.call(man, i)) { // 过滤
console.log(i, ":", man[i]);
}
}

 

 

 

严格来说,不使用hasOwnProperty()并不是一个错误。根据任务以及你对代码的自信程度,你可以跳过它以提高些许的循环速度。但是当你对当前对象内容(和其原型链)不确定的时候,添加hasOwnProperty()更加保险些。

 

格式化的变化(通不过JSLint)会直接忽略掉花括号,把if语句放到同一行上。其优点在于循环语句读起来就像一个完整的想法(每个元素都有一个自己的属性”X”,使用”X”干点什么):

 

// 警告: 通不过JSLint检测
var i, hasOwn = Object.prototype.hasOwnProperty;
for (i in man) if (hasOwn.call(man, i)) { // 过滤
console.log(i, ":", man[i]);
}

 

 

 

(不)扩展内置原型((Not) Augmenting Built-in Prototypes)

 

不增加内置原型是最好的。你可以指定一个规则,仅当下面的条件均满足时例外:

  • 可以预期将来的ECMAScript版本或是JavaScript实现将一直将此功能当作内置方法来实现。例如,你可以添加ECMAScript 5中描述的方法,一直到各个浏览器都迎头赶上。这种情况下,你只是提前定义了有用的方法。
  • 如果您检查您的自定义属性或方法已不存在——也许已经在代码的其他地方实现或已经是你支持的浏览器JavaScript引擎部分。
  • 你清楚地文档记录并和团队交流了变化。

 

如果这三个条件得到满足,你可以给原型进行自定义的添加,形式如下:

 

if (typeof Object.protoype.myMethod !== "function") {
Object.protoype.myMethod = function () {
// 实现...
};
}

 

 

避免隐式类型转换(Avoiding Implied Typecasting )

JavaScript的变量在比较的时候会隐式类型转换。这就是为什么一些诸如:false == 0 或 “” == 0 返回的结果是true。为避免引起混乱的隐含类型转换,在你比较值和表达式类型的时候始终使用===和!==操作符。

var zero = 0;
if (zero === false) {
// 不执行,因为zero为0, 而不是false
}

// 反面示例
if (zero == false) {
// 执行了...
}

 

 

 

 

posted on 2012-04-10 13:28  俊树  阅读(345)  评论(0编辑  收藏  举报

导航