汤姆大叔博客笔记【随时更新】

1、忘记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"

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

 反例
myname = "global";  全局变量
 func() {
    alert(myname);  "undefined"
    var myname = "local";
    alert(myname);  "local"
}
func();
等价于下面这段代码
myname = "global";  global variable
 func() {
   var myname;  等同于 -> var myname = undefined;
   alert(myname);  "undefined"
   myname = "local";
   alert(myname);  "local"}
func();

for-in循环(for-in Loops)

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

从技术上将,你可以使用for-in循环数组(因为JavaScript中数组也是对象),但这是不推荐的。因为如果数组对象已被自定义的功能增强,就可能发生逻辑错误。另外,在for-in中,属性列表的顺序(序列)是不能保证的。所以最好数组使用正常的for循环,对象使用for-in循环。

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

for (var i in man) {
   if (man.hasOwnProperty(i)) {  过滤原型属性或方法
      console.log(i,":",man[i]);
   }
}

 反面例子:
// for-in loop without checking hasOwnProperty()
 man) {
   console.log(i,":"
if (Object.prototype.hasOwnProperty.call(man,i)) {  过滤
      console.log(i,man[i]);
   }
}

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

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

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

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

 警告: 通不过JSLint检测
in man)  过滤
    console.log(i,man[i]);
}

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

扩增构造函数的prototype属性是个很强大的增加功能的方法,但有时候它太强大了。

增加内置的构造函数原型(如Object(),Array(),或Function())挺诱人的,但是这严重降低了可维护性,因为它让你的代码变得难以预测。使用你代码的其他开发人员很可能更期望使用内置的 JavaScript方法来持续不断地工作,而不是你另加的方法。

另外,属性添加到原型中,可能会导致不使用hasOwnProperty属性时在循环中显示出来,这会造成混乱。

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

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

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

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

注释(Writing Comments)

你必须注释你的代码,即使不会有其他人向你一样接触它。通常,当你深入研究一个问题,你会很清楚的知道这个代码是干嘛用的,但是,当你一周之后再回来看的时候,想必也要耗掉不少脑细胞去搞明白到底怎么工作的。

很显然,注释不能走极端:每个单独变量或是单独一行。但是,你通常应该记录所有的函数,它们的参数和返回值,或是任何不寻常的技术和方法。要想到注 释可以给你代码未来的阅读者以诸多提示;阅读者需要的是(不要读太多的东西)仅注释和函数属性名来理解你的代码。例如,当你有五六行程序执行特定的任务, 如果你提供了一行代码目的以及为什么在这里的描述的话,阅读者就可以直接跳过这段细节。没有硬性规定注释代码比,代码的某些部分(如正则表达式)可能注释 要比代码多。

函数表达式和函数声明

在ECMAScript中,创建函数的最常用的两个方法是函数表达式和函数声明,两者期间的区别是有点晕,因为ECMA规范只明确了一点:函数声明必须带有标示符(Identifier)(就是大家常说的函数名称),而函数表达式则可以省略这个标示符:

  函数声明:

  function 函数名称 (参数:可选){ 函数体 }

  函数表达式:

  function 函数名称(可选)(参数:可选){ 函数体 }

所以,可以看出,如果不声明函数名称,它肯定是表达式,可如果声明了函数名称的话,如何判断是函数声明还是函数表达式呢?ECMAScript是通过上下文来区分的,如果function foo(){}是作为赋值表达式的一部分的话,那它就是一个函数表达式,如果function foo(){}被包含在一个函数体内,或者位于程序的最顶部的话,那它就是一个函数声明。

function foo(){}  声明,因为它是程序的一部分
  var bar = function foo(){};  表达式,因为它是赋值表达式的一部分

  new function bar(){};  表达式,因为它是new表达式

  ((){
    function bar(){}  声明,因为它是函数体的一部分
  })();

函数声明只能出现在程序函数体内。从句法上讲,它们 不能出现在Block(块)({ ... })中,例如不能出现在 if、while 或 for 语句中。因为 Block(块) 中只能包含Statement语句, 而不能包含函数声明这样的源元素。另一方面,仔细看一看规则也会发现,唯一可能让表达式出现在Block(块)中情形,就是让它作为表达式语句的一部分。但是,规范明确规定了表达式语句不能以关键字function开头。而这实际上就是说,函数表达式同样也不能出现在Statement语句或Block(块)中(因为Block(块)就是由Statement语句构成的)。 

 

 

相关文章

kindeditor4.x代码高亮功能默认使用的是prettify插件,prett...
这一篇我将介绍如何让kindeditor4.x整合SyntaxHighlighter代...
js如何实现弹出form提交表单?(图文+视频)
js怎么获取复选框选中的值
js如何实现倒计时跳转页面
如何用js控制图片放大缩小