看完了javaScript数据类型表达式与运算符相关知识后以为可以对JavaScript笔试题牛刀小试一把了,没想到有一次次的死在逗号,冒号和括号上,不得已再看看这几个符号吧。

逗号

逗号我们常见的用法就是在连续声明一些变量的时候,可以少些很多var

var a=1,
b=2,
c=3;

方法参数我们使用逗号隔开,对象属性也是逗号隔开

function fbn(name,title){}
var person={
name:"Byron",
age:"24"
};

然而我们也会遇到这样的问题,在赋值表达式中出现的逗号

var a=(1,2,3);

表达式与运算符中提到过逗号运算符就是对应这种情况,这时表达式计算结果是最后一个子表达式结果,也就是3。千万不要误会前面的子表达式不会执行,每个子表达式都会执行,只不过“返回值”是最后一个表达式结果。

var a,b;
a=(b=1,2);
console.log(a);//2
console.log(b);//1

冒号

?:运算符

var p=gender ? 'male':female;

对象字面量

var obj={
name:"Byron",
age:24
};

switch语句

switch(t){
case 1:
console.log('xxx');
break;
case 2:
console.log('ooo');
break;
}

相信这是大家所熟知的用法了,那么我们可以个题目

x:y:z:1,2,3;

上面运算会不会报错?不报错运算结果是什么?很多同学初次看到这个会很惊讶,觉得肯定会出错,但结果却是3,来看看为什么

其实冒号还有个作用:声明label,JavaScript中语句可以有个标签前缀,我们称之为标记语句,break或continue可以和标记的语句结合使用,控制流程。如果标签有重复,就会出错。我们上面的语句可以翻译成这样

x:
y:
z:1,2,3

这样我们结合刚才说的逗号的知识就能明白为什么结果是3了,很多优化建议都是不提倡使用标签的,有没有想起C语言的goto,使用了标签控制流程,使程序相当难读懂。

var x=1;
foo:{
x=2;
break foo;
x=3;
}
console.log(x);

大括号

对象直接量声明

var obj={
name:"Byron",
age:24
};

整条语句使赋值语句,右值部分十个表达式,通过直接量构造出一个对象

函数声明或者函数直接量

function fn1(){
//....
}
var fn2=function(){
//...
};

相信这种用法不闭多说什么了

组织复合语句

with(obj){
//...
}
for(){
//...
}
if(){
//...
}else{
//...
}

大括号没有带来块级作用域

熟悉JavaScript的同学肯定对这点儿已经熟知了,大括号虽然能够组织复杂的语句等,是指算是同一“块”,with甚至提供了相近的功能,但遗憾的使JavaScript只有函数作用域,没有块作用域,再JavaScript中下面做法会声明全局变量,这个小小的知识点往往引英雄竞折腰

  1. 在function外使用声明变量(无论是否使用var)
  2. 在function内不是用var 声明变量
  3. 直接赋值于window属性
var a=2;
function fn(){
b=3;
window.c=4;
}

除了这三种剩下的就是function范围内的局部变量了,在很多JavaScript规范中都有提到,尽量提早声明变量正是由于其没有块作用域

function fn(n){
if(n>1){
var a=n;
}else{
var b=n;
}
console.log(a);
}

这样的代码在很多语言中有语法错误,因为if和else的大括号有块作用域,变量a、b在自己对应块作用域中,出了块就访问不到了。但在JavaScript中,没有块作用域,所以我们在if、else内声明的变量console.log依然能够访问,这确实是糟糕的设计,为了减少错误可能,尽量把变量声明提前。

很多笔试题目正是针对这方面知识出题的

{a:1};
var x={a:1};
{a:1,b:2};
var y={a:1,b:2};

亲自试试是不是发现很惊讶,我们分析一下

{a:1} JavaScript有传说中的“语句优先”,也就是当大括号既可以被理解为复合语句块也可以被理解为对象直接量的时候,JavaScript会将其理解为复合语句快。{a:1}其实就是 a: 1,想想冒号的作用是不是知道为什么返回值是1了。

var x={a:1} 当{a:1} 作为右值出现的时候,明显就不是语句,而是直接量表达式了,所以把大括号当作对象直接量语法处理,结果是个对象。

{a:1,b:2}; 看了上面这个就简单了,可以翻译为:a:1,b:2 结合逗号和冒号作用,结果似乎显而易见了,就是2嘛。然而其实报错了,这是为什么?在逗号运算符后面必须是表达式,而标签语句十个label statement,是条语句,所以就报错了。

了解了这些知识我们再来试几个题目(看答案在控制台上,不要试图alert)

{foo:[1,2,3]}[0];
{a:1}+2;
2+{a:1};

不知道小伙伴儿们做对了没有,这几个题目核心一样,大括号虽然看起来没什么作用,但起到了语句分隔符作用,{foo:[1,2,3]}[0]可以理解为

{foo:[1,2,3]};
[0];

所以返回值是[0],同样{a:1}+2变为

{a:1};
+2

但是!为什么2+{a:1}就不一样了呢?这时加法运算符导致的,加号是左结合的,{}被解析为表达式(得是表达式相加嘛),根据数据类型中知识对象{a:1}转换为NaN

小括号

在JavaScript中小括号有几种用法

函数声明或调用表达式参数表

这个好理解,函数定义的时候需要用小括号将其参数包裹,用逗号隔开,调用的时候也一样

function fn(name,age){
//...
}
fn('Byron',24);
var f=new fn('Byron',24)

与一些关键字组成条件语句

我们常见的if、switch、while中的小括号就是干这个用的

if(a>0){
//...
}
while(i<len){
//...
}
for(var i=0;i<len;i++){
//...
}

分组运算符

分组运算符内部只能包含表达式,可以改变运算符优先级,舍弃一些可能的语法树,最常见的

var x=(1+2)*3;

相信不用多解释,很多同学会认为小括号有强制表达式运算的功能,其实这时片面的理解,这只是改变了运算符优先级,生成新的语法树后的结果。

对于简单的json字符串转为对象的时候,因为浏览器兼容性原因,不能使用JSON对象,又懒得引入json2,所以就会用eval()处理,大概写法这样

var jsonStr=...;
var jsonObj=eval('(' + jsonStr + ')');

很多同学会问,为什么还要加上个小括号呢?像我们上面解释的大括号的作用,json字符串 "{a:1,b:2}" 这样的格式会被理解为语句,也就是传说中的label statement,语法树是这样的

{

a:1,

b:2

}

上面提到过逗号运算符不能在label statement后面,所以会报错,而加上括号后由于分组运算符只能包含表达式,所以{}变成直接量语法,这样就是我们希望的内容了。

立即调用的函数表达式

再来回头看看我们所谓的立即执行函数,一般有两种写法

(function(){})();
(function(){}());
!function(){}();

搜了很多资料,终于看到了靠谱解释,总结一下,首先我们需要搞清楚函数表达式和函数声明区别,ECMAScript规范中定义的相当模糊:

函数声明必须带有标示符(Identifier)(就是大家常说的函数名称),而函数表达式则可以省略这个标示符:

  函数声明:

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

  函数表达式:

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

其实我们常用的区分方式是根据上下文,如果function fn(){}作为右值出现(赋值表达式右边)那么就是表达式,否则就是函数声明。有几种看起来不常规的方式需要我们注意

new function fn(){}; //表达式,因为在new 表达式中
(function(){}());//表达式,在分组运算符中

这样我们就能理解第二种写法了,就是利用分组运算符改变了语法树。同样第三种写法其实是利用了一元运算符后面跟表达式的原理,我们也可以写成

+function(){}()
-function(){}()
~function(){}()

知乎上长天之云甚至写出了这么多

( function() {}() );
( function() {} )();
[ function() {}() ];
////////////////////////////////
~ function() {}();
! function() {}();
+ function() {}();
- function() {}();
////////////////////////////////
delete function() {}();
typeof function() {}();
void function() {}();
new function() {}();
new function() {};
/////////////////////////////////
var f = function() {}();
/////////////////////////////////
1, function() {}();
1 ^ function() {}();
1 > function() {}();

所以我们应该称立即执行函数为立即调用的函数表达式!

中括号

相对而言中括号是个最简单的符号了,一般有几种语义

***数组相关

我们知道数组可以通过中括号来直接量实例化

var a=[1,2,3];

***获取对象属性值

这也是很常见的用法

var a=[1,2,3];
var b={name:'Byron'};
a[2];
b['name'];

看几个有意思的小题目

[1,2,3,4,5][0..toString.length];//0.等同于0.0
'foo'.split('') + [];

最后

不总结不知道,一总结下一跳啊,几个小小的符号竟然这么百转回肠,总结完之后才知道自己以前不了解这些知识,死在笔试题上一点儿都不冤,准备换工作的同学也了解一下以备万一吧。

参考

Another JavaScript quiz

我是如何理解”Another JavaScript quiz”中的题目

JavaScript 匿名函数有哪几种执行方式?

深入理解JavaScript系列(2):揭秘命名函数表达式

JavaScript面试时候的坑洼沟洄——逗号、冒号与括号的更多相关文章

  1. JavaScript面试时候的坑洼沟洄——数据类型

    前些日子写了篇关于最近找工作的一些感受的博客 找工作的一些感悟--前端小菜的成长,没想到得到了很多园友的共鸣,得到了很多鼓励,也有园友希望我分享一些笔试.面试的经验.我觉得分享一些笔试题没太多价值,对 ...

  2. JavaScript面试时候的坑洼沟洄——表达式与运算符

    上篇博客JavaScript面试时候的坑洼沟洄--数据类型总结了一下JavaScript数据类型几转换的相关知识,很多朋友可能和我一样,买了书后对数据类型啊.运算符啊.语句啊都是扫两眼或直接略过的,自 ...

  3. JS逗号、冒号与括号

    JavaScript面试时候的坑洼沟洄——逗号.冒号与括号   看完了javaScript数据类型和表达式与运算符相关知识后以为可以对JavaScript笔试题牛刀小试一把了,没想到有一次次的死在逗号 ...

  4. 26个精选的JavaScript面试问题

    译者按: 从各个平台精选整理出26道由浅入深的题目助你面试 原文: Top 26 JavaScript Interview Questions I Wish I Knew 译者: Fundebug 为 ...

  5. 5个经典的JavaScript面试基础问题

    JavaScript程序员在IT领域中的需求量非常巨大.如果你非常精通JavaScript,你会有很多换工作.涨薪水的机会.但是在一家公司录用你之前,你必须顺利通过面试,证明你的技能.在本文中,我将向 ...

  6. JavaScript面试的完美指南(开发者视角)

    为了说明 JS 面试的复杂性,首先,请尝试给出以下结果: onsole.log(2.0 == "2" == new Boolean(true) == "1") ...

  7. 快速掌握JavaScript面试基础知识(三)

    译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...

  8. 快速掌握JavaScript面试基础知识(二)

    译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...

  9. 攻破javascript面试的完美指南【译】

    攻破javascript面试的完美指南(开发者视角) 0. 前言 本文适合有一定js基础的前端开发人员阅读.原文是我google时无意发现的, 被一些知识点清晰的解析所打动, 决定翻译并记录下来.这个 ...

随机推荐

  1. Windows下搭建Wordpress博客网站

    一:安装wamp Windows下的Apache+Mysql/MariaDB+Perl/PHP/Python,一组常用来搭建动态网站或者服务器的开源软件,本身都是各自独立的程序,但是因为常被放在一起使 ...

  2. hibernate框架int和Integer类型区别

    hibernate 框架在定义实体时,int类型最好定义为Inttger类型,因为在注入时int是值类型不允许为空.

  3. Future和Promise

    Future用于获取异步操作的结果,而Promise则比较抽象,无法直接猜测出其功能. Future Future最早来源于JDK的java.util.concurrent.Future,它用于代表异 ...

  4. Arc GIS engine10.2与VS2012的安装及匹配步骤

      本文章已收录于:   .embody { padding: 10px 10px 10px; margin: 0 -20px; border-bottom: solid 1px #ededed } ...

  5. 【转】查询oracle比较慢的session和sql

    -查询最慢的sql select * from ( select parsing_user_id,executions,sorts command_type,disk_reads,sql_text f ...

  6. git: 常用功能等

    1. an very useful simple git guide link: http://rogerdudler.github.io/git-guide/index.zh.html

  7. unity 协程

    StartCoroutine在unity3d的帮助中叫做协程,意思就是启动一个辅助的线程. 在C#中直接有Thread这个线程,但是在unity中有些元素是不能操作的.这个时候可以使用协程来完成. 使 ...

  8. U-Boot Driver Model领域模型设计

    需求分析 在2014年以前,uboot没有一种类似于linux kernel的设备驱动模型,随着uboot支持的设备越来越多,其一直受到如下问题困扰: 设备初始化流程都独立实现,而且为了集成到系统,需 ...

  9. [杂谈]冲NOIP一等奖。。

    唉不想多说了. 真是一段“传奇”的经历啊. 还是那句话..“是的我上次什么都没说就走了...”这次也一样. 我还是太单纯的以为我们是肯定能够参加北大的夏令营的..然而结果真是意料之外啊. 本来我以为我 ...

  10. 【BZOJ1911】[Apio2010]特别行动队 斜率优化DP

    想了好久啊....——黑字为第一次更新.——这里是第二次更新,维护上下凸包据题而论,第一种方法是化式子的方法,需要好的化式子的方法,第二种是偏向几何,十分好想,纯正的维护凸包的方法,推荐. 用了我感觉 ...