文/谢传贵

在学习JavaScript的过程中,最需要搞清楚的10件事是什么?关于这个问题有人在 Quora上给出了的答案。其中提到了一些很有代表性的知识点(坑),但描述比较杂乱。下面我将在他的基础上进行重新编排和解释。希望对你学习 JavaScript有些帮助(为避免文章跑题,以下内容先不考虑ES5的 strict mode):

1. 使用关键字var声明变量

要使用一个变量,应该先声明它。在JavaScript中,声明变量非常简单,因为你不用关心它的类型。

1 function setLocal () {
2        var name = 'Tom';
3        alert('the name is ' + name);
4 }
5 setLocal(); // "the name is Tom"

如果不用var呢?让我们来看一个例子:

01 var sex = 'female';
02 function setGlobal () {
03        var age = '86';
04        name = 'John';
05        sex = 'male'//我们不知函数外也有一个变量sex
06        alert(name + ' is ' + age + ' years old.');
07 }
08 setGlobal(); // "John is 86 years old."
09 alert(name); // "John"
10 alert(sex);  // "male"
11 alert(age);  // ReferenceError: age is not defined

在 这个例子中,从setGlobal函数外部我们能够访问(可读写)变量name而不能访问变量age。事实上,函数内部使用var声明的变量是局部变量, 只有函数内部能够访问。但由于没有使用var声明变量name,从函数外部也能访问到它 ,这很不安全。同时,事例代码中还试图声明变量sex,仍然由于没有使用var,却无意将外部变量sex的值覆盖了,即污染了其他代码,这更是难以接受 的。

特别地,在使用一个未声明的变量时,程序将会报错;使用一个已声明而未赋值的变量时,或者使用一个并不存在的对象属性时,返回的值是undefined:

1 var name;
2 var obj = {};
3 alert(name);      // undefined
4 alert(obj.prop);  // undefined
5 alert(some);      // ReferenceError: some is not defined

总结:在声明一个变量时省去var,这个变量将变成一个全局变量(global variable),你程序的数据直接暴露给了全局,同时也可能覆盖函数外已有的同名变量,对他人代码构成了风险。所以,请一定记得使用关键字var。

2.单引号和双引号没有区别

一些传统的后端语言是明显区分单引号和双引号作用的,例如Java里表示字符需要使用单引号,而表示字符串需要使用双引号:

1 Char ch = 'a';
2 String str = "Cat";

然 而JavaScript没有char这样的数据类型,要表示文本数据类型,只能使用字符串(string)。创建字符串直接量最简单的方法是使用一对单引 号或双引号括起来你想表示的字符序列。选择使用单引号还是双引号从语法上没有区别,如果选择单引号,则字符串内部的单引号需要转义,如果选择双引号,则字 符串内部的双引号同理也需要转义。要表示单个字符,须使用长度为1的字符串。例如:

1 var name = "She said, \"Who is the president?\"";
2 var desc = 'What\'s that?';
3 var desc = 'e';

基于上面提到的转义关系,我个人比较喜欢使用单引号,这可以减少许多转义的麻烦,尤其是当你的代码中包含HTML代码时。试想如果下面的字符串选用双引号,那将增加很多反斜杠,而且还容易错:

1 $('div').append('
2 error info
3 ');

同理,如果HTML标签里嵌入JavaScript代码,也一样能减少反斜杠(不过,应尽量少在HTML标签上写JavaScript代码):

3.除了null和undefined,一切都像是对象(object)

JavaScript 的变量类型可以分为两类:原始类型(primitive types)和对象类型(object types)。原始类型有5种,分别是数字、字符串、布尔型,以及null和undefined两个特殊值。除了原始类型,其他的一切都是对象类型。除了 null和undefined,其他一切都“像是对象”。为什么说像是对象,因为接下来你会发现原始类型变量也能调用方法或属性,甚至连函数也拥有方法。

1 function f () {
2        var n = 10;
3        alert(n.toString()); // "10"
4 }
5 f.call(); // f是一个函数,依然能调用方法

我 们看到,n是一个原始类型的值,而f是一个函数,他们都可以调用方法,就像一个对象一样。事实上,在JavaScript中,除了null和 undefined,一切都可以调用方法或属性。这里有一个隐藏的逻辑,那就是当原始类型的变量试图调用方法或属性时,它会被动态地转成其对应的包装对 象。让我们来检测一下:

1 function f () {
2        var n = 123;
3        Number.prototype.toString = function() {
4               return typeof this;
5        }
6        alert(typeof n); //"number"
7        alert(n.toString()); //"object"
8 }
9 f();

4.undefined, null, 0, NaN, ''在作为布尔值使用时都代表false

当处于布尔环境时,undefined, null, 0, NaN, ''会被作为false使用,其他的一切作为true使用。

1 if (o) {
2   //当o的值不为undefined, null, 0, NaN, '', false时,进入该处 
3 }

要注意,这里的字符串是一个长度为1的空字符串。

5.使用全等号===

JavaScript中有两个判断相等的运算符:==(一般相等)和===(全等)。一般相等运算符由2个等号组成,使用它进行相等判断时会对运算符两边的值进行类型转换,以使两边类型相同后再进行相等比较。例如:

1 0 == ''  // true
2 0 == '0' // true
3 1 == true  // true
4 false == '0' // true
5 null == undefined // true
6 ' \t\r\n ' == 0  // true

正如上面的例子,由于会自动进行类型转换,一些我们直觉不会相等的值也相等了,这些结果大部分时候不是我们想要的。如果你想了解转换规则,推荐你看《ECMAScript. Equality operators.》,不过相当枯燥难记。

全 等运算符由3个连续的等号组成,使用它进行相等判断时不会对运算符两边的值进行类型转换,要满足相等,它不仅要求类型相同,还要求值相同,所以使用全等运 算符时上面的例子一个也不会相等。这里有一个特例,那就是NaN,它是唯一一个不与任何值相等的值,甚至包括它本身,要判断一个值是否等于NaN,你需要 使用全局函数isNaN:

1 NaN === NaN  // false
2 isNaN(NaN)  // true

所以,除非你很确定两个待比较值的类型是一样的,或者你很熟悉类型转换关系,否则我还是劝你使用全等号吧。

6.没有块作用域(block scope)

JavaScript 中没有块作用域或类作用域的概念,只有函数作用域,即函数内部定义的变量对于整个函数体以及函数内嵌套的函数都是可见的,而对于函数体外部是不可见的,与 花括号没有什么关系。例如下面的例子中变量i是定义在if条件的花括号代码块中的,我们依然能从花括号外访问:

1 function f () {
2        var c = 1;
3        if (true) {
4               var i = 2;
5               alert(c);   // 1
6        }
7        alert(i);  // 2
8 }

正是因为JavaScript只有函数作用域,所以我们可以通过函数来模拟实现私有属性。

7.变量声明会被提升到当前作用域的顶部

什么意思呢?还是先来看一个例子(参考犀牛书第6版):

1 var scope = 'global';
2 function f () {
3        alert(scope);  // "undefined", not "global"
4        var scope = 'local';
5        alert(scope);  //  "local"
6        var name = 'country';
7 }
8 f();

结 合前面的知识,你也许认为函数第一行会打印"global",因为var声明和赋值发生在其后面,而函数外已经有scope变量了。听起来很和逻辑。事实 却是打印了"undefined"。这里涉及一个JavaScript很有趣的现象—提升(hoisting),函数内部声明的所有变量对整个函数作用域 都是可见的,就好像在函数执行时,所有的变量声明会被提升到第一行一样。上面的代码等效于:

1 var scope = 'global';
2 function f () {
3        var scope, name;
4        alert(scope);  // "undefined"
5        scope = 'local';
6        alert(scope);  //  "local"
7        name = 'country';
8 }

所以,在定义一个函数时,请将所有变量声明放在函数的第一行,因为这样的代码比较真实地反映了变量声明的提升现象。同时,即使你已理解了提升现象,但阅读代码的人不一定理解,为了避免困惑,从代码的可阅读性上仍然推荐将所有的变量声明置于函数作用域的顶部。

8.函数的参数可以被省略

这个很好理解。函数声明时可以指定形参的个数,但调用时并不强求传参的个数。在调用时,如果对应形参的位置没有给值,则其值为undefined。

1 function hello (name, age) {
2        alert('name is : ' + name + ', age is ' + age);
3 }
4  
5 hello('Anon', 42);  // name is : Anon, age is 42
6 hello("Baptiste"); // name is : Baptiste, age is undefined
7 hello("Bulat", 24, 42); // name is : Bulat, age is 24

9.JavaScript是面向对象的语言,使用原型继承机制

JavaScript是一门面向对象的语言,使用基于原型的继承机制(原型编程),不存在类(class)的概念。使用JavaScript实现继承非常简单。

01 if (typeof Object.create != 'function') { //为低版本JavaScript提供create方法
02        Object.create = function (o) {
03               function F() {}
04               F.prototype = o;
05               return new F();
06        };
07 }
08 var a = { };
09 a.foo = 'hello';
10 a.do function () { alert('do something.'); };
11 var b = Object.create(a);  //对象b继承自对象a
12 b.bar = 'crazy';
13 b.bar  // "crazy"  b创建了自己的属性bar
14 b.foo  // "hello"  b从a继承了属性foo
15 b.do();  // "do something." b从a继承了方法do
16 b.foo = 'world';
17 b.foo  // "world"
18 a.foo  // "hello" // b创建了自己的属性foo, 这不会修改a的属性foo
19 a.do function () { alert('i am a letter.'); };
20 b.do();  // "i am a letter." a的方法do被更新,b动态响应了更新。

从 上面这个简短的示例可以看到,对象a直接从对象b继承了属性和方法,并且之后若是a有修改,b也动态更新继承。这个过程没有class什么事,一切都是对 象,按照Douglas Crockford的说法:“还有什么能比这更面向对象的呢?”(What could be more object oriented than that?)

10.JavaScript不仅限于浏览器

随着Web的发展,JavaScript在浏览器端可谓春风得意,但JavaScript绝不仅限于浏览器,它还可以用在其他地方,比如最近风头正劲的Node.js就是运行于服务器端的。

【嘉年华推荐】

也许你是一位后端工程师,日常使用的语言具有很完善的预检查能力,任何以上提到的语法错误都不会被IDE和编译器放过,但由于JavaScript通常运行在客户端,加上一些历史的原因,它并没有严格的预检查能力,所以良好的编程习惯是很重要的。

要学好一门语言当然不是掌握10点或20条就能做到的,这需要我们持续的学习和实践。若你有更好的建议,欢迎回复补充分享。

--

微信名称:阿里技术嘉年华

微信号:alibabatech

简介:传播原创高质量的技术内容

转自:http://chuansongme.com/n/172780

JavaScript 需要清楚的10件事相关推荐

  1. 保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事

    保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事 10 things to consider when securing an embedded 802.11 Wi-Fi device 随着 ...

  2. 35岁前必做10件事 让你少奋斗8年挣足钱

    35岁前必做10件事 让你少奋斗8年挣足钱(转)中财网 (2010-03-27 16:04:01) 转载 标签: 就业 求职 乘数 钱经 李彦宏 美国 杂谈 分类:转载 男人.女人都要过三十五岁这堵墙 ...

  3. [转载]出了国才明白的10件事~(MITBBS ZT)

    出了国才明白的10件事~(MITBBS ZT) 2007年10月16日 星期二 05:18 在国内时,了解"外面的世界"并不难,然而,认识的误区只有在国外住久了,慢慢地体会才能逐渐 ...

  4. 运维 服务器安装,IT服务器运维安装CentOS后,你要做的10件事

    IT服务器运维安装CentOS后,你要做的10件事 IT服务器小知识CentOS是一款社区驱动的免费Linux发行版,也是一款功能很强大的可替代红帽企业级Linux(RHEL)的发行版.它源自红帽企业 ...

  5. 大规模运行MongoDB应该知道的10件事

    MongoDB的首席解决方案架构师Asya Kamsky 最近发表了一篇文章,概括了大规模运行MongoDB需要知道的10件事. MongoDB也需要DevOps.MongoDB是一个数据库.和任何其 ...

  6. 关于Java你不知道的10件事

    转载自 关于Java你不知道的10件事 作为 Java 书呆子,比起实用技能,我们会对介绍 Java 和 JVM 的概念细节更感兴趣.因此我想推荐 Lukas Eder 在 jooq.org 发表的原 ...

  7. linux mint 19界面美化,安装完 LinuxMint 19.3 后必做的10件事

    安装完 LinuxMint 19.3 后必做的10件事 LinuxMint 发行版是一款基于Ubuntu的易用性好,特别适合入门者使用的一款Linux发行版,相比于Ubuntu,界面和操作更友好. 本 ...

  8. 作为一个新晋测试经理,在软件测试计划之前你必须知道的10件事

    有人喜欢创造世界,他们做了开发者:有的人喜欢开发者,他们做了测试员.什么是软件测试?软件测试就是一场本该在用户面前发生的灾难提前在自己面前发生了,这会让他们生出一种救世主的感觉,拯救了用户,也就拯救者 ...

  9. 自学成为程序员_成为程序员不需要的10件事

    自学成为程序员 Do you have what it takes to become a programmer? Chances are, you will base your answer on ...

最新文章

  1. pthread_cond_wait()函数的详解
  2. 静态程序分析chapter4 - 基于格(Lattice)理论的数据流分析
  3. 采购组织0001不对工厂1000负责
  4. wxWidgets:绘制自定义控件
  5. GDCM:gdcm::UIDGenerator的测试程序
  6. Nginx中gzip_static使用测试
  7. 学习多媒体开发的正确姿势?陈功:看书 啃代码 搞插件
  8. 洛谷 题解 P4955 【[USACO14JAN]Cross Country Skiing 越野滑雪】
  9. 就你这个求婚态度,能嫁给你才怪!
  10. MFC处理回车窗口消失
  11. java 线程变量put_Java线程(篇外篇):线程本地变量ThreadLocal
  12. Java基础—集合2Set接口和Map接口
  13. springboot 扫描jar包中bean_详解Spring Boot的Component Scan原理
  14. 2.晶晨A311D-编译Ubuntu/Debian固件
  15. 简洁代码实现pdf转word文档
  16. 学了CPDA数据分析师认证课程对工作有什么好处?
  17. 20世纪50年代到60年代 电子计算机,计算机基础知识习题标准答案
  18. 怎么两边同时取ln_回归分析为什么要取LN值
  19. “鉴别服务开创者”沦为笑柄?裁判文书曝出得物APP无法鉴别真伪
  20. 【反射机制】与 【xml解析 】之 巧夺天工 —————— 开开开山怪

热门文章

  1. 29 WM配置-策略-出库策略2-定义“紧急FIFO”策略(Stringent FIFO)
  2. python3.4和3.6的区别_详解Python3.6正式版新特性
  3. 基于qtc++设计文本编辑器的代码_文本编辑器Vim/Neovim被曝任意代码执行漏洞,Notepad:兄弟等你好久了...
  4. 类型 jpa mysql_Spring Boot集成JPA
  5. Window下更新python pip源
  6. java每过一段时间执行一次代码(方法)
  7. 1.2什么是神经网络
  8. mac下查看redis安装路径_干货!win10环境下Redis安装、启动教程
  9. 监听浏览器是否被缩放 - 案例篇
  10. swiper叠加轮播效果 (含源码) - 案例篇