JavaScript深入【表达式和运算符(上集)】你能过我8关js运算符的题目吗?
博主留言 (茗洋芳竹)
每一个高手成功之路,都要学会怎样与孤独打交道 。我是个傻孩子,孤独竟然是我的夫人,每天晚上都会坐在我旁边看我学习,写博客。马上要离开ASP.NET生涯,投奔PHP了,一切都是那么突然,因此我的《ASP.NET MVC4 In Action》学习竟然不像以前那么有动力开始小题大做了。一句话:生活不会去适应你,所以你要去适应生活。
虽然现在没有上班,很累很堕落,但是每到了晚上,我就会去学习,不是因为坚持,而是我别无选择------茗洋芳竹
当我写完这篇博客的时候,已经是第二天的6点半了,经过6个小时的整理与demo的编写,有些疲惫,但是还是很有收获。
1. 原来最简单的 变量名也是一个表达式,我脑子有了表达式的一个概念了,每次的复杂运算都是表达式的结合。
2. 原来在JavaScript中,数组中的列表逗号之间的元素可以省略,这时省略的空位会填充值undefined。数组直接量的元素列表结尾处可以留下单个逗号,这时并不会创建一个新的值为undefined的元素。所以我们当读数组中根本不存在的值的时候,会提示读取的元素为undefined。
3. 三元运算符的来源,举一反三地理解了,一元运算符,二元运算符。最终结合 运算顺序的规律,我真正的理解了下面这个表达式:
q=a?b:c?d:e?f:g;
4. 我自己可以成功 完成 递增运算的 充分讲解,以后不怕这种考 运算顺序的题目了
5. 我自己出了 8 道js运算符题目,如果你想挑战来证明自己的js能力, 请点击我就能快速到达
6. ~运算符的使用 点击快速到达
7. 数组有个sort方法,调用后,竟然排序了原来的数组,还有排序的规律
上篇博客地址 :JavaScript深入【词法结构,类型,值和变量】原来也不简单
表达式 (茗洋芳竹)
表达式的做小单位--它们不在包含其他表达式,JS中的原始表达式:常量或者直接量、关键字和变量
例如:
//原始表达式 第一种原始表达式 --直接量
var a="hello world"; //字符串直接量
var b=/pattern/; //正则表达式直接量
//第二种原始表达式 --保留字 true,false,null,this(返回当前对象)
var c=true;
var d=null;
var global=this; //this并不是常量,它在程序的不同地方返回的值也不相同。在一个方法体内,this返回调用这个方法的对象
//第三种原始表达式 --变量
var i;
var sum;
var c=undefined; //undefined是全局变量
常见错误解释:当JavaScript代码中出现了标识符,JavaScript会将其当做变量而去查找它的值。如果变量名不存在,表达式运算结果为undefined。然而,在ECMAScript5的严格模式中,对不存在的变量进行求值会抛出一个引用错误异常。
①对象和数组初始化表达式实际上是一个新创建的对象和数组。这些初始化表达式有时称作“对象直接量”和“数组直接量”
它们所包含的成员或者元素都是子表达式。
例如:
//对象和数组的初始化表达式
var h=[]; //创建一个空数组:[]代表空数组
var j=[1+2,3+4]; //拥有两个元素的数组,第一个是3,第二个是7
var k=[[1,2,3],[4,5,6],[7,8,9]]; //js中的数组中的元素可以是对象
数组直接量中的列表逗号之间的元素可以省略,这时省略的空位会填充值undefined。数组直接量的元素列表结尾处可以留下单个逗号,这时并不会创建一个新的值为undefined的元素。
例如:
var l=[1,,,,5]; // 等同于 var l [1,undefined,undefined,undefined,5]
alert(l[3]); //undefined
②对象直接量类似,用 “:” 隔开 key:value
例如:
var p={x:2,y:-1.2,z:"哈哈哈"};
var q={};
q.x=2.3;
q.y=-1.2;
alert(q.x+" "+q.y);
//对象直接量也可以嵌套
var rectangle1={ "up_left":{x:2,y:2},
"up_right":{x:8,y:2},
"down_left":{x:2,y:10},
"down_left":{x:8,y:10}
};
var rectangle2={ "top_left":{x:0,y:0},
"width":p.x,
"height":p.y
};
var area_square=function(x){
return x*x;
}
alert(area_square(10));
var area_round=function GetArea_Round(r){
return (Math.PI*r*r).toFixed(2);
}
// alert(GetArea_Round(10)); //这样是错的
alert(area_round(10));
用个变量保存一个函数,调用的时候,直接变量名(参数),而不能是函数function后面的那个名字。
函数定义表达式,var 变量名=function(参数,参数){…}
var o={x:1,y:{z:3}};
var a=[o,4,[5,6]];
alert(o.x); //1
alert(o.y.z); //3
alert(o["x"]); //1
alert(a[1]); //4 索引是从0开始的,1代表第二个值,即4
alert(a[2]["1"]); //6 表达式a[2]中索引为1的元素
alert(a[2][1]); //6 表达式a[2]中索引为1的元
alert(a[0].x); //1 表达式a[0]的x属性
//调用表达式
//1. 调用自己的函数
function ReturnString(name){
return "我的姓名是:"+name;
}
var c=ReturnString("茗洋芳竹");
alert(c);
//2.调用系统的函数
var maxNumber=Math.max(1,2,3,4,5); //参数至少1个,可以多个数字
alert(maxNumber);
//3.调用对象的方法
var unordered_array=[34,51,11,35,633,25];
var unordered_array2=["banana","apple","group","Play","zero","continue","1s","11s","2p"];
var ordered_array=unordered_array.sort();
var ordered_array2=unordered_array2.sort();
alert(ordered_array.toString());
alert(unordered_array2.toString()); //sort(),改变了原数组,说明是数组引用类型,并不是复制了数组
alert(ordered_array2.toString()); //排序字符的时候,按照数字,英文大写字母A-Z,然后小写字母a-z顺序排序
某些东东,我就用图片顺便表示
概念:对象创建表达式创建一个对象并调用一个函数(构造函数)初始化新对象的属性。类似于 函数调用表达式,只是对象创建表达式之前多了一个关键字new:
var obj=new Object(); //创建一个对象
// var point=new Point(2,3); //Point类是自己定义的
var obj2=new Object;
var date=new Date;
alert(date);
如果一个对象创建表达式不需要传入任何参数传给构造函数的话,那么这对空圆括号是可以省略掉的
运算符 (茗洋芳竹)
大多数运算符都是由标点符号表示的,比如 “+”和“=”。还有一些运算符是由关键字表示的,比如delete和instanceof。
下面一张表是按照 运算符优先级排序的,从高到低。
水平分割线分隔开来的运算符具有不同的优先级。
标题为A的列 表示运算符的结合性,L(从左到右)或R(从右到左)
标题为N的列 表示操作数的个数
标题为“类型”的列 表示期望的操作数类型,以及运算符的结果类型(在“→”符号之后)
①lval是left-value的简写,意思是“左值” |
2.1.1 操作数的个数
运算符可以根据其操作数的个数分类:
①一元运算符:它们将一个表达式转换为另一个稍复杂的的表达式。比如表达式“-X”中的“-”运算符就是个一元运算符,是将操作数X求负数。 ②二元运算符:比如“*”乘号,将两个表达式合并成一个稍复杂的表达式,换言之,它们的操作数均是两个,比如5*2 ③三元运算符:比如“?:”,它将三个表达式合并成一个表达式。例如: var name="茗洋芳竹"; var sanyuan_surname=name.indexOf("茗洋")>-1?"茗洋":"未知"; //等同于下面的if语句
var if_surname="无名"; if(name.indexOf("茗洋")>-1){ if_surname="茗洋";
}else{
if_surname="未知";
} alert(sanyuan_surname+" "+if_surname); //茗洋
2.1.2 操作数类型和结果类型
一些运算符可以作用于任何类型数据,但仍然希望它们的操作数是指定类型的数据,并且大多数运算符返回(或计算出)一个特定类型的值。 左图中“类型”列中列出了运算符操作数的类型(箭头左)和运算结果(箭头右) JavaScript运算符通常会对操作数进行类型转换。比如乘号,表达式“3”*“5”是合法的。因为JavaScript会将操作数转换为数字,返回值是15,而不是字符串“15”。有些运算符比如“+”号,可以做加法运算,也可以做字符串连接。 2.1.3 左值
它是指:表达式只能出现在赋值运算符的左侧.
|
有一些的表达式具有很多副作用,前后的表达式运算会相互影响。赋值运算符最明显:如果给一个变量或属性赋值,那么那些使用这个变量或属性的表达式的值都会发生改变。“++”和“--”递增和递减运算符与此类似,因为它们包含隐式的赋值delete运算符同样有副作用:删除一个属性就像(但不完全一样)给这个属性赋值undefined。其他的JavaScript运算符都没有副作用,但函数调用表达式和对象创建表达式有些特别,在函数体或者构造函数内部运用了这些运算符并产生了副作用的时候,我们说函数调用表达式和对象创建表达式是有副作用的。
那张图表是有规律的。运算符优先级控制着运算符的执行顺序。优先级高的运算符(图片的顶部)的执行总是先于优先级低(图片的底部)的运算符
例1:
var w=x+y*z;
运算符优先级:“*”>“+”>赋值运算符“=”,因此赋值操作是在右侧的表达式计算出结果后进行的
例2:(我自己额外的题目,答案的字体颜色已被我设成白色,你可以鼠标选中答案后面的空白的地方查看,只是想帮助那些面试者解决心里的一个疑惑)
如果7题中,你做错了很多,就说明你真的还不理解运算符的计算顺序,没关系,下面我会让你掌握的,也会让你结合那张图片,理解和学会 计算含有 递增,递减运算符的题目。先学习下面的,先不要纠结那几道我出的题目。
var j=1;
j=j=j+1;
答案是2.
第二关:
var j=1;
j=j++;
alert(j);
答案是1.
(重点)第三关:
var j=1;
j=(j++)+j+(j++)+(++j)+j;
alert(j);
答案是13.
第四关:
var j=1;
j+=j++;
alert(j);
答案是2 .
第五关 :
var j=1;
j+=++j;
alert(j);
答案是3 .
var j=1;
j=j=j+1;
j=j++;
j+=++j;
j+=j++;
alert(j);
var i=1;
i+=i/i+i++;
alert(i); //
i++;
++i;
i+=i/i+(++i);
alert(i); //
答案是3和12.
第八关:
var i=1;
i+=(i++)+(++i)/i;
alert(i);
答案是3.
其他说明:
首先说明一下单独使用时i++等同于i=i+1;而i+=等同于 i=i+后面的表达式
顺便说明一下 i++和 ++i的区别,i++单独使用时候和++i一模一样,i++是等i这个表达式运算结束后再运算i+1,然后改变i的值,
而++i是先运算i+1,然后改变i的值,然后再和外面的表达式结合,进行整体运算。
乘法和除法的优先级高于加法和减法,赋值运算符的优先级非常低,通常总是最后执行的。
例3:(涉及到属性访问表达式和方法调用时候,优先执行方法调用和属性访问表达式,它们优先级高于上图列出的所有运算符)
var o=[1,3,5];
var c=[o,2,4,6];
function GetExpression(k){
return k/k*k+k/k-k;
}
var g=5;
g=g/GetExpression(g)+12/c[0][1]*g;
alert(g);
答案是25. (答案的字体已被我设成白色,你可以鼠标选中“答案是”后面的空白处查看)
如果7题中,你做错了很多,就说明你真的还不理解运算符的计算顺序,没关系,下面我会让你学会的,也会让你结合那张图片,理解和学会 计算含有 递增,递减运算符的题目。先学习下面的,先不要纠结那几道我出的题目。
上张图中的 标题为A的列说明了运算符的结合性质。L指从左往右结合,R指从右往左结合。结合性指定了在多个具有同样优先级的运算符表达式中的运算顺序。从左往右是指运算的执行是按照由左往右顺序进行。例如:
第四个例子是重点,以后会经常遇到,不一定是JavaScript语言,C#,Java中也有
①减法运算符是从左往右的结合性
因此 w=x-y-z和这段代码一模一样:w=((x-y)-z)
②~ 按位非运算符和 “-”求反运算符结合,这两个运算符都是从右往左结合的
首先我们先讲解一下 ~ 的运算过程:首先改变符号,再减去1的一个运算
例如:
var x=10;
var y=~x;
alert(y); // x先求反,得到-10,然后再减去1,得到-11,这就是最终结果
最终我们弹出的框显示的是 –11
下面我们来结合一下~和-
var x=10;
var y=~-x;
alert(y);
答案是9。 (答案的字体已被我设成白色,你可以鼠标选中“答案是”后面的空白处查看)
理解过程:
-号和~号都是从右往左结合,所以上面的第二行代码等同于 var y=~(-x);
-x的值就是 –10,然后再求反减去1,也就是 -10变成10,然后减一变成了 9。
③w=x=y=z中的 赋值运算,是从右往左结合的,所以代码等同于w=(x=(y=z));
④?: 三元运算符的结合,它也是从右往左的结合性
代码如下:
q=a?b:c?d:e?f:g;
效果就等同于
q=a?b:(c?d:(e?f:g));
现在看懂了吗?所以运算符的结合性很重要。
运算符的优先级和结合性规定了它们在复杂的表达式中的运算顺序,但是并没有规定子表达式中的计算过程中的运算顺序。
JavaScript总是严格按照从左往右的顺序来计算表达式。例如
在表达式w=x+y*z中,首先计算子表达式w,然后计算x,y和z,然后y的值和z的值相乘,再加上x的值,最后将其赋值给表达式w所指代的变量或属性。给表达式添加括号将会改变乘法、加法和赋值运算的关系,但从左往右的顺序是不会改变的
例1:(书中的一个例子,我觉得讲解方式有问题,再例3中我会反驳)
var a=1;
var b=(a++)+a;
alert(b);
答案是3 。 (答案的字体已被我设成白色,你可以鼠标选中“答案是”后面的空白处查看)
讲解:
1. 计算b
2. 计算a++(我们假设为c);
3. 计算c;
4. 计算c+a;
5. 将c+a的结果赋值给b。按照“++”的定义,在第二步中的a++结果依然是1,也就是c为1,随后a立即增长1,因此在执行第三步的时候,a的值已经是2了,所以c+a 也就是 1+2,所以b的最终值等于3.
例2:我们就我出的第三关我例子,在讲解一下
题目如下:
var j=1;
j=(j++)+j+(j++)+(++j)+j;
alert(j);
答案是13. (答案的字体已被我设成白色,你可以鼠标选中“答案是”后面的空白处查看)
讲解:
1.我们先把第二行代码 转换如下
j=a+j+b+c+j;
a,b,c分别先替换掉那几个递增运算,此时都是+号运算,根据+号的结合性是从左往右(L)
2. 首先计算a,所以a=1,随后此时j递增1,所以j=2了,然后再计算a后面的那个j,此时代码可以替换如下
j=1+2+b+c+j;
3. 然后计算b,b=j++,即b=2;随后,j递增1,所以j=3了,所以 此时代码可以替换如下
j=1+2+2+c+j;
4. 然后计算c,c=++j,即c=++3,即c=4;所以 此时代码可以替换如下
j=1+2+2+4+j;
此时j已经变成4了,所以代码可以替换成 j=1+2+2+4+4;
5. 将最终的结果赋值给j,所以最终 j 最终 13
例3:(改一下例1)
例1:
var a=1;
var b=a+(a++);
alert(b);
答案是2。 (答案的字体已被我设成白色,你可以鼠标选中“答案是”后面的空白处查看)
讲解:
1. 第二行代码可替换成 b=a+c,其中c=a++;
2. 如果按照例1的讲解方式,应该先执行a++,然后a变成2了,最终的形式是b=2+1才对,也就是还是等于3
但是最终结果如下:
而例1中的结果是
所以说按照 先++递增运算符,然后在+号运算感觉不对,某些情况我觉的还是从左往右对,毕竟事实胜于雄辩
3. 所以说 var b=a+(a++)的理解思路是 先假设b=a+c; 其中c=a++;然后由于+号是从左往右的结合性,所以
①b=1+c;
②b=1+(a++); 即b=1+1;随后 a增长1,然后 b=2
如果不相信这个思路,下面图片还可以证明
都是从左往右,不在乎 先递增运算符,如果在乎的话,var b=a+(++a);这个题目
就是先++1; 即c部分已经等于2了,然后此时a递增变成2,那么此时var b=2+2才对,但是结果并不是
顺便我们再解决一下疑惑,a运算完了,是够递增了,答案是递增了,不管你是a++还是++a,代码如图
所以借此机会,我们可以讲解一下我的 第七关题目:
var i=1;
i+=i/i+i++;
alert(i); //
i++;
++i;
i+=i/i+(++i);
alert(i); //
答案是3和12.
讲解:
1. i+=i/i+i++; 即i=i+i/i+i++; 根据+号从左往右的结合性,i=1+1/1+i++;即i=2+i++;
2. 然后i=2+1;不过随后,受i++的影响,此时i等于2了;但是2+1=3最终进行了赋值操作,原本i=2,此时又等于3了
3. 所以第一弹出框显示3
4. 经过两次递增,到达 第6行代码时候,i已经变成5了
5. i+=i/i+(++i); 即i=i+i/i+(++i); 根据+号从左往右的结合性,i=5+5/5+(++i);即i=6+(++5);
6. 然后 i=6+(6),经过递增后,此时i=6了,但是又因为赋值(=)运算,原本的i=6,此时经过赋值,变成12了。
我还做了几个测试
本次博客,花大手笔讲解 运算符的前一部分,重点原理性的解决关于递增运算符的困扰。如果你看懂了,觉得还不错,给个推荐好吗?或者给个评论,你们的关心是我的动力。为了方便阅读,所以运算符还剩的一点内容,在下一篇讲解
相关代码下载:点击直接下载