最小全局变量

JavaScript通过函数管理作用域。在函数内部生命的变量只在这个函数内部,别的地方不可用。全局变量是指在函数外或是未声明直接简单使用的。每个Javascipt环境有一个全局对象,当你在任意函数外使用this都可以访问。你创建的每一个全部变量都成了这个全局对象的属性。在浏览器里,方便起见,该全局对象有个附件属性叫做window,此window指向该全局对象本身。

myglobal = "hello"; // 不推荐写法
console.log(myglobal); // "hello"
console.log(window.myglobal); // "hello"
console.log(window["myglobal"]); // "hello"
console.log(this.myglobal); // "hello"

全局变量问题

尽量少使用全局变量,并且必须使用var声明。

不推荐写法:

function sum(x, y) {
// 不推荐写法: 隐式全局变量
result = x + y;
return result;
}
// 反例,勿使用
function foo() {
var a = b = 0;
// ...
}

推荐写法:

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

Var的好处

1.通过var创建的全局变量是不能被删除的。

2.无var创建的隐式的全局变量是能被删除的。

如代码示例:

// 定义三个全局变量
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"

访问全局对象

在浏览中,全局对象可以通过window属性在代码的任何位置访问。但是在其他环境中,这个方便的属性可能被叫做其他东西,甚至在程序中不可用。如果你需要在没有硬编码的window标示符下访问全局对象,你可以在任何层级的函数作用域中做如下操作:

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

这种方法可以随时获得全局对象,因为其在函数中被当做函数调用了(不是通过new操作),this总是指向全局对象。

单Var模式

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

1.提供了一个单一的地方去寻找功能所需要的局部变量。

2.防止变量在定义之前使用了逻辑错误。

3.帮助你记住声明的全局变量,因此较少了全局变量。

4.少代码。

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

你可以使用一个var语句声明多个变量,并以都好分割。像这种初始化变量同时初始化值的做法很好。这样子可以防止逻辑错误和增加代码的可读性。

预解析:var散布的问题

Javascript中,你可以在函数中的任何位置声明多个var语句,并且他们就好像是在函数的顶部声明一样发挥作用,这种行为叫做hoisting(悬置,置顶解析,预解析)。当你使用了一个变量,然后不久在函数中又重新声明的话,就可能产生逻辑错误。对于Javascript,只要你的变量在同一个作用域中,他都被当做是声明的,即使是他在var声明前使用的时候。

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

var是预解析的,只有当实际的走到这里的时候,才会将var后边的常量放进堆栈中。但是var又是制定解析的并且后方替换前方已声明的变量时,所以才会有上边的undefiend。

for循环

针对for循环,要注意,特别是数组性能损失的问题,如:

// 次佳的循环
for (var i = 0; i < myarray.length; i++) {
// 使用myarray[i]做点什么
}

每次循环myarray.length都要向数组遍历一次,这样性能损失时很大的。

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

这样写最好,获取length时,仅仅获取了一次。并且这样的写法也很好的体现了单var模式。

for-in循环

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

从技术上将,你可以使用for-in循环数组(因为JavaScript中数组也是对象),但这是不推荐的。因为如果数组对象已被自定义的功能增强,就可能发生逻辑错误。另外,在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]);
}

要记住eval()是魔鬼、

此方法接受任意的字符串,并当做JavaScript代码来处理。

// 反面示例
var property = "name";
alert(eval("obj." + property)); // 更好的
var property = "name";
alert(obj[property]);

使用eval()也带来了安全隐患,因为被执行的代码(例如从网络来)可能已被篡改。这是个很常见的反面教材,当处理Ajax请求得到的JSON 相应的时候。在这些情况下,最好使用JavaScript内置方法来解析JSON相应,以确保安全和有效。若浏览器不支持JSON.parse(),你可 以使用来自JSON.org的库。

同样重要的是要记住,给setInterval(), setTimeout()和Function()构造函数传递字符串,大部分情况下,与使用eval()是类似的,因此要避免。在幕后,JavaScript仍需要评估和执行你给程序传递的字符串:

// 反面示例
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000); // 更好的
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);

使用新的Function()构造就类似于eval(),应小心接近。这可能是一个强大的构造,但往往被误用。如果你绝对必须使用eval(),你 可以考虑使用new Function()代替。有一个小的潜在好处,因为在新Function()中作代码评估是在局部函数作用域中运行,所以代码中任何被评估的通过var 定义的变量都不会自动变成全局变量。另一种方法来阻止自动全局变量是封装eval()调用到一个即时函数中。

考虑下面这个例子,这里仅un作为全局变量污染了命名空间。

console.log(typeof un);    // "undefined"
console.log(typeof deux); // "undefined"
console.log(typeof trois); // "undefined" var jsstring = "var un = 1; console.log(un);";
eval(jsstring); // logs "1" jsstring = "var deux = 2; console.log(deux);";
new Function(jsstring)(); // logs "2" jsstring = "var trois = 3; console.log(trois);";
(function () {
eval(jsstring);
}()); // logs "3" console.log(typeof un); // number
console.log(typeof deux); // "undefined"
console.log(typeof trois); // "undefined"

另一间eval()和Function构造不同的是eval()可以干扰作用域链,而Function()更安分守己些。不管你在哪里执行 Function(),它只看到全局作用域。所以其能很好的避免本地变量污染。在下面这个例子中,eval()可以访问和修改它外部作用域中的变量,这是 Function做不来的(注意到使用Function和new Function是相同的)。

(function () {
var local = 1;
eval("local = 3; console.log(local)"); // logs "3"
console.log(local); // logs "3"
}()); (function () {
var local = 1;
Function("console.log(typeof local);")(); // logs undefined
}());
 

-高级Javascript编程学习笔记----Javascript编程及架构设计最应该注意的基本点的更多相关文章

  1. Linux Shell编程学习笔记——目录(附笔记资源下载)

    LinuxShell编程学习笔记目录附笔记资源下载 目录(?)[-] 写在前面 第一部分 Shell基础编程 第二部分 Linux Shell高级编程技巧 资源下载 写在前面 最近花了些时间学习She ...

  2. 多线程编程学习笔记——异步调用WCF服务

    接上文 多线程编程学习笔记——使用异步IO 接上文 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端 接上文 多线程编程学习笔记——异步操作数据库 本示例描述了如何创建一个WCF服务,并宿主 ...

  3. javascript正则表达式 - 学习笔记

    JavaScript 正则表达式 学习笔记 标签(空格分隔): 基础 JavaScript 正则表达式是用于匹配字符串中字符组合的模式.在javascript中,正则表达式也是对象.这些模式被用于Re ...

  4. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  5. DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)

            本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com         注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...

  6. ufldl学习笔记和编程作业:Feature Extraction Using Convolution,Pooling(卷积和汇集特征提取)

    ufldl学习笔记与编程作业:Feature Extraction Using Convolution,Pooling(卷积和池化抽取特征) ufldl出了新教程,感觉比之前的好,从基础讲起.系统清晰 ...

  7. ufldl学习笔记和编程作业:Softmax Regression(softmax回报)

    ufldl学习笔记与编程作业:Softmax Regression(softmax回归) ufldl出了新教程.感觉比之前的好,从基础讲起.系统清晰,又有编程实践. 在deep learning高质量 ...

  8. 多线程编程学习笔记——async和await(一)

    接上文 多线程编程学习笔记——任务并行库(一) 接上文 多线程编程学习笔记——任务并行库(二) 接上文 多线程编程学习笔记——任务并行库(三) 接上文 多线程编程学习笔记——任务并行库(四) 通过前面 ...

  9. 多线程编程学习笔记——async和await(二)

    接上文 多线程编程学习笔记——async和await(一) 三.   对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步 ...

随机推荐

  1. [转载]--Ubuntu下修改DNS重启也能用的方法

    安装好Ubuntu之后设置了静态IP地址,再重启后就无法解析域名.想重新设置一下DNS,打开/etc/resolv.conf cat /etc/resolv.conf# Dynamic resolv. ...

  2. WPF.UIShell UIFramework之自定义窗口的深度技术 - 模态闪动(Blink)、窗口四边拖拽支持(WmNCHitTest)、自定义最大化位置和大小(WmGetMinMaxInfo)

    无论是在工作和学习中使用WPF时,我们通常都会接触到CustomControl,今天我们就CustomWindow之后的一些边角技术进行探讨和剖析. 窗口(对话框)模态闪动(Blink) 自定义窗口的 ...

  3. GNU make 规则

    clean : rm *.tmp 规则格式: targets : prerequisites recipe ... targets : prerequisites : recipe recipe .. ...

  4. zoj 2112 Dynamic Rankings

    原题链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1112 #include<cstdio> #include ...

  5. Windows Phone动画

    从事Windows Phone开发已经有一段时间了,但是一直没有好好的静下心来梳理一下自己这段时间的知识,一是怕自己学问不到家,写不出那些大牛一般的高屋建瓴:二是以 前一直没有写博客的习惯:好了废话不 ...

  6. Asp.Net MVC如何返回401响应码

    需求:     在默认创建的Asp.Net MVC项目中(这里使用VS2013),需要手动返回一个401响应码给浏览器.我们的代码可能是下面这样子的.   public ActionResult Un ...

  7. require.js的用法

    我采用的是一个非常流行的库require.js. 一.为什么要用require.js? 最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了.后来,代码越来越多,一个文 ...

  8. 以Lockbits的方式访问bitmap

    转载自:http://www.cnblogs.com/xiashengwang/p/4225848.html 2015-01-15 11:38 by xiashengwang, 585 阅读, 0 评 ...

  9. Qt 延时

    第一部分: 关于sleep函数,我们先来看一下他的作用:sleep函数是使调用sleep函数的线程休眠,线程主动放弃时间片.当经过指定的时间间隔后,再启动线程,继续执行代码.sleep函数并不能起到定 ...

  10. 理解JavaScript中的事件路由冒泡过程及委托代理机制

    当我用纯CSS实现这个以后.我开始用JavaScript和样式类来完善功能. 然后,我有一些想法,我想使用Delegated Events (事件委托)但是我不想有任何依赖,插入任何库,包括jQuer ...