(转 Uncle Tom )深入理解javascript(1)学习笔记
2012-04-10 13:28
736 查看
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) { // 执行了... }
相关文章推荐
- <深入理解JavaScript>学习笔记(4)_立即调用的函数表达式
- 深入理解javascript学习笔记(一) 编写高质量代码
- <深入理解JavaScript>学习笔记(5)_强大的原型和原型链
- 韩顺平 javascript教学视频_学习笔记23_js事件驱动机制深入理解_js常用事件_js版计算器
- 深入理解javascript学习笔记(一) 编写高质量代码
- <深入理解JavaScript>学习笔记(3)_全面解析Module模式
- 深入理解 JavaScript 系列学习笔记
- 《Java编程思想_ 深入理解java虚拟机_Thinking in java__Effiect java__设计模式》学习笔记7——泛型编程基础
- 【css学习笔记】深入理解之float
- 《深入理解 Java 虚拟机》学习笔记(1)—— JVM 运行时数据区
- javascript oop深入学习笔记(二)--javascript的函数
- 【Java】深入理解JVM学习笔记(二) —— 对象
- Java学习笔记之一(深入理解Object类中方法)
- javascript学习记录(三)-this对象的深入理解
- Java学习笔记之深入理解关键字final
- 深入理解C++11 第二章兼容性稳定性 学习笔记
- 深入理解C指针学习笔记之常量和指针(六)
- 深入理解Java虚拟机—学习笔记(1)Java内存区域与内存溢出异常
- Java学习笔记之深入理解动态绑定和静态绑定
- 《深入理解LINUX内存管理》学习笔记 (四)