JavaScript语言精髓(1)之语法概要拾遗

 

逻辑运算

JavaScript中支持两种逻辑运算,“逻辑或(||)”和“逻辑与(&&)”,他们的使用方法与基本的布尔运算一致:

var str= ‘hello’;
var obj = {};
x = str || obj;
y = str && obj;

这种运算符的特殊之处在于,他既不改变运算元的数据类型,也不强制运算结果的数据类型。除此之外,还有两条特性:

  • 运算符会将运算元理解为布尔值;
  • 运算过程支持布尔短路。

因此上例中,

x运算结果为:str;

y运算结果为:obj。

比较运算

1.等值检测

等值检测的目的是判断两个变量是否相等或相同。

相等是指运算符“==”和“!=”,相同是指运算符“===” 和"!=="。

名称

运算符

说明

相等

==

比较两个表达式,看是否相等。

不等

!=

比较两个表达式,看是否不相等

相同

===

比较两个表达式,看值是否相等并具有相同的数据类型。

不同

!==

比较两个表达式,看是否具有不相等的值或不同的数据类型。

等值检测中“相等”的运算规则

类型

运算规则

两个值类型进行比较

转换成相同数据类型的值进行“数据等值”比较。

值类型与引用类型进行比较

将引用类型的数据转换成为与值类型数据相同的数据,再进行“数据等值”比较。

两个引用类型比较

比较引用地址。

等值检测中“相同”的运算规则

类型

运算规则

两个值类型进行比较

如果数据类型不同,则必然“不相同”;数据类型相同时,进行“数值等值”比较。

值类型与引用类型进行比较

必然“不相同”。

两个引用类型比较

比较引用地址。

特别举例说明引用类型的等值比较,“比较引用地址” 的实际意义是:如果不是同一个变量或其引用,则两个变量不相等,也不相同。


var str = 'abcdef';
var obj1 = new String(str);
var obj2 = new String(str); alert(obj1==obj2);//返回false
alert(obj1===obj2);//返回false

“===“和“!==”运算符的一般性用途

在JavaScript中false、‘’、0和Null、undefined是两两“相等”的:

alert(''==0);
alert(0==false);
alert(false=='');
alert(undefined==null);

以上的结果都是true,因此当比较的数据元中包含以上5种情况时,应使用“===”和“!==”。

PS:一个值应该与其自身“相等/相同”,但存在一个例外:一个NaN值,与自身不相等,也不相同。

2.序列比较(比较大小)

可比较序列的类型

序列值

Boolean

0~1

String

Number

负无穷大~正无穷大

类型

运算规则

两个值类型进行比较

直接比较数据在序列中的大小。

值类型与引用类型进行比较

将引用类型的数据转换为与值类型数据相同的数据,再进行“序列大小”比较。

两个引用类型比较

无意义。(*注1)

注1:其实,对引用类型进行序列检测运算仍然是等可能的,但这与valueOf()运算的效果有关。

以下是上述的例子:


//值类型的比较,布尔值与数值
var b0 = false;
var b1 = true;
var num = 1;
alert(b1<num);//false
alert(b1<=num);//true
alert(b1>b0);//true //值类型与引用类型
var num = 1;
var ref = new String();
alert(num > ref);//true //引用类型的比较
var o1={};
var o2={};
alert(o1 > o2 || o1 < o2  || o1 == o2);//false //两个运算元都是字符串,比较字符串中每个字符的大小
var s1 = "abc";
var s2 = "ab";
alert(s1 > s2);//true //当字符串与其他类型值比较时,将字符串转换为数值比较
var s3 = "101";
var i= "100";
alert(s3 > i);true //当字符串转换为数值时得到NaN,任何数据与NaN比较都为false
var s4 = "abc";
var i = "100";
alert(s4 > i);

 void运算符

使用void的语法格式是:[void 表达式]。

其作用是使后面的表达式立即执行,但忽略返回值(返回undefined)。

以下的例子很好的诠释了这句话的含义:


var s1 = "1";
function testVoid()
{
   s1 = "2";
}
var s2 = void testVoid();
alert(s1);//2
alert(s2);//undefined

利用这种特性,我们可以使<a>标签中的Href属性失去作用,就像我们常看到的那样:

这里void后的括号并不是必须的,只不过它仍然满足[void 表达式]这一语法格式,表达式等于(0)而已,千万不要当成了函数调用。

由此应该可以想到,既然这里使用到的是void 忽略返回值这一特性,那么该链接么有执行默认行为的原因并不是我们原以为的0在起作用,如下形式会得到同样的效果:

<a href="javascript: void 1">I am a useless link</a>
<a href="javascript: void 'abcde'">I am a useless link</a>
<a href="javascript: void (1 + 2)">I am a useless link</a> <!-- 这里不加括号行不行呢,大家自己去验证吧~  -->

函数调用

最常用的函数调用方式:


//具名函数直接调用
function foo()
{ }
foo(); //匿名函数通过引用来调用
var fooRef = function(){}
fooRef();

下面来看看几种不常见的函数调用方式:


//没有引用的匿名函数调用1
( //这里的括号对表示强制运算符 
  function()
  {
    alert('test');
  }()//这里的括号对表示函数调用运算符 (*注)
);
//执行完后,弹出‘test’表明函数执行成功。
下面来分析一下这段代码,首先来看内部的函数,
function()
{
  alert('test');
}()
这样还不够清晰,这段代码等同于
function fun1()
{
  alert('test');
}()
如果还觉得不够清晰的话,其实它还等同于
function fun1()
{
  alert('test');
};
();
也就是说第1个分号前的函数申明是作为完整的语法结构被解释,而后面的“();”则显然是一段错误的语法,
那么是什么让上面的函数调用得以成功执行呢?答案是最外层的强制运算符。
我们知道强制运算符是把本不会产生运算关系的运算元强制进行运算,比如:1*2+3*4,不会按照2+3这样的逻辑进行运算,但1*(2+3)*4之后就可以了。
回到上面的例子,最外层的强制运算符把本应单独解释的两段语句强制执行,执行的效果就是产生函数调用!   //没有引用的匿名函数调用2
(
   function()
   {
    }
)();//这里的括号对表示函数调用运算符 (*注)
经过上面的例子,这个就比较好理解了。
这里强制运算符运算的是“函数直接量声明”这个表达式,并返回一个函数的引用,然后通过函数调用运算符“()”来操作这个函数引用。   //没有引用的匿名函数调用3
void function()
{ }();//这里的括号对表示函数调用运算符 (*注)
运算符void用于使其后的函数表达式执行运算,可理解为一种强制运算,类似示例1。  
上面3个例子中标(*注)的地方都是函数调用运算符,既然是函数调用的语法,那么和普通的函数调用一样,也是可以传递参数的。
void function(s)
{
  alert(s);
}('test');

 break的不常见用法

一般情况下,break子句作用于循环语句的内层或者switch语句中,但break子句也具有一种扩展的语法,以指示它所作用的范围。

该范围用已经声明过的标签来表示:

break some_label;

some_label:
{
   var s1 = "1";    if(s1 =="1")
   {
      break some_label;
   }
   s1 ="2"; }
alert(s1);// 结果为1,表明s1 ="2"并没有执行。 

continue的不常见用法

continue后面也可以带一个标签,这时它表明从循环体内部中止,并继续到标签指示处开始执行。


some_label:
for(var i = 1; i <3;i++)
{
    for(var k =11; k < 13;k++)
    {
    if(k==12)
    {
        continue some_label;
    }
    alert("i = " + i + ";k = " + k);
    }
}
//依次弹出i = 1;k = 11  i = 2;k = 11
//直接看代码不容易理解,可以加个断点调试一下,其实这里的continue some_label的意义是从内部循环中跳出来,
//并继续执行外部的下一次循环,普通的continue是执行当前循环的下一次循环。
//这里需要注意的是continue对应的标签后不能添加大括号。 JavaScript中没有GOTO语句,break和continue语句提供了另一种选择的可能。

运算符的二义性

一、加号“+”的二义性

加号作为运算符在JavaScript中有三种作用:

1.字符串连接符

2.表达式求和运算符

3.数字取正运算符

第3点不常用,他的意思是表示数学里的正号。

var n = 1;
alert(+n);//表示+(1),其结果还是1。可与alert(-n),进行比较,这里的结果是-1 

这三种情况中,1、2两点最容易出现二义性问题。因为这两种处理运算处理方式依赖与数据类型,而无法从运算符上进行判断。如:

c = a + b;

是根本无法知道他真实的含义是在求和还是在做字符串连接。

由于这种二义性的存在会出现一些问题。浏览器中,由于DOM模型的许多值看起来是数字,但实际上确实字符串。因此试图做加法运算时,实际却变成了字符串连接。

<img id = "testImg" style="border: 1 solid red" />
<script>
alert(testImg.style.borderWidth + 10);//1px10
</script>

另外,还有一条关于加号运算符的规则:如果表达式中存在字符串,则优先按字符串连接进行运算。

alert("123"+456);//123456
alert(123+"456");//123456

二、逗号“,”的二义性

先看下面几个示例,分析其表达的含义:


//示例1
a=(1,2,3); //示例2
a=1,2,3; //示例3
a=[1,2,(3,4,5),6]; //示例4
var a = 1,2,3;

示例1中,逗号被作为连续运算符使用,连续运算符(也叫逗号表达式)的意义是返回最后一个表达式的值,所以示例1的效果是“变量a赋值为3”。

示例2整个表达式为一个逗号表达式,因此整个表达式的值为最后一个表达式的值3,而变量a被赋值为1.

示例3中的逗号有两中含义(3,4,5)中的逗号表示连续运算符,而在1,2中则表示数组声明时的语法分隔符,因此示例3为将a声明为一个[1,2,5,6]的数组。

示例4并不会像示例2一样正常执行。因为这里的逗号被解释成了语句var声明时用来分隔多个变量的语法分隔符(var a, b,c),而数字2,3为不合法的语法声明,因此会在解释期就提示出错。

三、方括号“[]”的二义性

//示例1
a = [ [1][1] ];

这个奇怪的语句并没有语法错误,尽管我们几乎不能理解,但JavaScript解释器可以理解。为了弄清这个问题,必须了解方括号的作用:

1.用于声明数组直接量。

2.存取数组下标的运算符。

3.对象成员存取。

因此第一个"[1]"被理解成了一个数组的直接量,它只有一个元素。接下来,由于他是对象,所以第二个“[1]”就被理解为取下标为1的元素,很显然,这个元素还没有声明。

因此“[1][1]”的结果为undefined,而a就变成了a =[undefined],只有一个元素的数组,该元素为undefined.

下面再来看一个稍微复杂一点的例子:

var a = [ ['a',1,2]['b',3,4] ];

['a',1,2]仍然是一个数组的直接量,但['b',3,4]怎么解释呢?

首先要了解'b',3,4被解释成为了一个逗号表达式,而他的值为4,因此[4]表示数组成员的存取,后面的过程就与上一个示例一样了。

JavaScript语言精髓(1)之语法概要拾遗(转)的更多相关文章

  1. 试读《JavaScript语言精髓与编程实践》

    有幸看到iteye的活动,有幸读到<JavaScript语言精髓与编程实践_第2版>的试读版本,希望更有幸能完整的读到此书. 说来读这本书的冲动,来得很诡异,写一篇读后感,赢一本书,其实奖 ...

  2. 《JavaScript语言精髓与编程实践》读书笔记

    JavaScript语言精髓与编程实践读书笔记 function v1(v1){ v1 = 100; alert('v1:'+v1); } function v2(name){ v1.apply(th ...

  3. JavaScript语言精粹 笔记01 语法 对象

    内容比较简单,只是从头梳理一下JS的知识 语法空白标识符数字字符串语句 对象对象字面量检索更新引用原型反射枚举删除减少全局变量污染  语法 1 空白 空白可能表现为格式化字符或注释的形式.空白通常没有 ...

  4. 《JavaScript语言精髓与编程实践》读书笔记二

    第3章非函数式语言特性 这一章首先介绍了语言的分类,命令式(结构化编程,面向对象编程),说明式(函数式等).而这一章,主要介绍JS的非函数式特点. 在开始之前,首先介绍了由“结构化编程”向“面向对象编 ...

  5. 《JavaScript语言精髓与编程实践》读书笔记一

    受到狗哥书单的影响,看到了豆瓣上的评论,买了这本书,然后囫囵吞枣似地用一个月的时间看完了.回头想想自己做的js项目,感觉都羞愧-什么东西都是拿来尝试了一下就用了,其实有很多写得超级丑的地方,看完这个让 ...

  6. [已读]JavaScript语言精髓与编程实践

    推荐第二章的内容,关于表达式和运算符的内容很独到.

  7. (转)JavaScript二:JavaScript语言的基本语法要求

    摘自:http://blog.csdn.net/erlian1992 要学习好JavaScript,首先我们要懂JavaScript语言的一些基本语法要求: 一,区分大小写 JavaScript语言区 ...

  8. javascript中正则表达式的基础语法

    × 目录 [1]定义 [2]特点 [3]元字符[4]转义字符[5]字符组[6]量词[7]括号[8]选择[9]断言[10]模式[11]优先级[12]局限性 前面的话 正则表达式在人们的印象中可能是一堆无 ...

  9. javascript 学习一(概述+基本语法)

    http://js.do/ 概述 JavaScript :脚本语言 诞生于1995年. javascript 1.0,发布于Netscape Navigator 2  @1996年3月 布兰登·艾奇( ...

随机推荐

  1. redis_学习_02_redis 可视化工具 Redis Desktop Manager

    二.参考资料 1.Redis可视化工具Redis Desktop Manager使用 2.超好用的Redis管理及监控工具,使用后可大大提高你的工作效率!

  2. BEC listen and translation exercise 12

    More than 220 cities now have air quality monitoring systems and 42 others will have systems in plac ...

  3. BEC translation exercise 4

    People have long known that nuts are part of a healthy diet.人们早就知道坚果是健康饮食的一部分.People, who you know w ...

  4. Brackets (区间DP)

    个人心得:今天就做了这些区间DP,这一题开始想用最长子序列那些套路的,后面发现不满足无后效性的问题,即(,)的配对 对结果有一定的影响,后面想着就用上一题的思想就慢慢的从小一步一步递增,后面想着越来越 ...

  5. C# 使用API检查域用户名和密码是否正确

    添加引用: using System.Runtime.InteropServices; public class VerifyUserByDomain { ; ; ); [DllImport(&quo ...

  6. QString的拼接

    QString的append()函数则提供了类似的操作,例如: 1. str = "User: ";     2. str.append(userName);     3. str ...

  7. Erlang pool management -- Emysql pool optimize

    在上一篇关于Emysql pool (http://www.cnblogs.com/--00/p/4281938.html)的分析的最后提到 现在的emysql_conn_mgr gen_server ...

  8. 发RTX通知

    安装sdk 在RTXServer目录下找到WebRoot目录,找到里面的SendNotify.cgi(就是一个php页面,默认是pc - ascii编码).打开页面,在头部加上编码信息 header( ...

  9. 升级 AngularJS 至 Angular

    Victor Savkin 大神撰写了一系列文章详细介绍如何升级 AngularJS 应用: NgUpgrade in Depth Upgrade Shell Two Approaches to Up ...

  10. java 多线程系列基础篇(一)

    多线程状态图: Thread类的两个方法比较: yield方法: Yield是一个静态的原生(native)方法 Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程. Yiel ...