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. 《Advanced Bash-scripting Guide》学习(八):从一个目录移动整个目录树到另一个目录

    本文所选的例子来自于<Advanced Bash-scripting Gudie>一书,译者 杨春敏 黄毅 ABS书上的例子: 从一个目录移动整个目录树到另一个目录 #!/bin/bash ...

  2. JAVA动态代理的全面深层理解

    Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过 ...

  3. deep learning (六)logistic(逻辑斯蒂)回归中L2范数的应用

    zaish上一节讲了线性回归中L2范数的应用,这里继续logistic回归L2范数的应用. 先说一下问题:有一堆二维数据点,这些点的标记有的是1,有的是0.我们的任务就是制作一个分界面区分出来这些点. ...

  4. (转)msys2使用教程

    一.安装 官方下载地址 http://www.msys2.org/ 指定好安装路径(一般D根目录即可),一路下一步就好. 二.配置国内镜像.设置窗体修改颜色 使用[清华大学开源软件镜像站]中的地址,修 ...

  5. Buffer 和Cache 的区别

    Buffer 和Cache 的区别buffer 与cache 操作的对象就不一样.buffer

  6. 我的 Linux 配置

    系统版本 Ubuntu 18.04 一名老年弱智 OI 选手的 Linux 配置 文本编辑器: Sublime Text 中文补丁,关闭自动补全,自动联想,括号匹配,字号 15 编译器: g++ (然 ...

  7. 学习动态性能表(19)--v$undostat

    学习动态性能表 第19篇--V$UNDOSTAT  2007.6.14 本视图监控当前实例中undo空间以及事务如何运行.并统计undo空间开销,事务开销以及实例可用的查询长度. V$UNDOSTAT ...

  8. word 2007,以不同颜色突出显示文本的快捷键,highlight命令

    命令:highlight 默认快捷键:Ctrl+Alt+H   查询或自定义快捷键的方法: 打开一个文档→单击左上角的office图标→word选项 左边的列表中选择自定义→在右边的窗口中,底部有个“ ...

  9. c# Chart 服务器端动态创建ChartArea

    1 aspx <x:ContentPanel ShowBorder="true" ShowHeader="false" ID="ContentP ...

  10. [C++] 动态规划之矩阵连乘、最长公共子序列、最大子段和、最长单调递增子序列、0-1背包

    一.动态规划的基本思想 动态规划算法通常用于求解具有某种最优性质的问题.在这类问题中,可能会有许多可行解.每一个解都对应于一个值,我们希望找到具有最优值的解. 将待求解问题分解成若干个子问题,先求解子 ...