标签:
表达式:js中的一个短语,计算会得出一个结果。
原始表达式(简单表达式,一类表达式):表达式的最小单位,有常量,直接量,关键字和变量 /*直接量:直接在程序中出现的常数值*/
保留字中的原始表达式:true,false,null,this
如果变量名不存在,运算结果是:undefined,但在ECMAScript5的严格模式中,这回抛出一个引用错误异常。
复杂表达式:由简单表达式组成,例:
数组访问表达式=表示数组的表达式+左方括号+整数表达式+右方括号
函数调用表达式=表示函数对象的表达式+0个或多个参数表达式
对象和数组的初始化有时也被称为:"对象直接量"和"数组直接量",但不是原始表达式(因为他们所包含的成员或者元素都是子表达式)
数组初始化表达式:[],[[],[]],[,,,](省略的元素会由undefined填充,最后一个逗号的右边不会填充)……
对象初始化表达式:{x:1,y:2}…… /*这里的属性名都是字符串,可以不按标识符的规则来,如果不符合标识符的规范,不能通过.标识符来访问*/
函数定义表达式:"函数直接量",function(x) { return x*x;} /*另一种方式是通过函数语言来定义*/
运算符:(简单表达式->复杂表达式)的常用方法。
属性访问表达式:
expression1.identifier
expression1[expression1]
计算时,先计算expression1的结果,如果是null或者undefined,则抛出类型错误异常;否则非对象会将其转换成对象。
如果命名的属性不存在,则返回undefined。
调用表达式:以"函数表达式"开始,之后跟随一对圆括号,然后若干个参数(0~n)以逗号分开。
函数表达式->指代了要调用的函数 /* 如f(0)里的f,Math.max(a,b,c)里面的Math.max*/
过程:计算函数表达式 ---> 计算参数表达式 --->调用函数(非函数类-->类型错误) ---> 实参赋值给形参 --->执行函数体
调用表达式的值根据return来决定,如果没有return,值为:undefined
PS:参数不一致的函数调用问题以后再做讨论
以x.f()调用函数的,即以属性访问表达式来调用的函数被称为"方法调用"。(此时函数内的this可以用来调用宿主对象:非常非常的好用!!)
/* ECMAScript 5中,通过严格模式定义的函数在调用时,将使用undefined作为this的值 ---> 关于严格模式会在第五章讲到*/
对象创建表达式:创建一个对象并调用一个函数初始化新对象的属性。/* 例:new Object() */
PS:如果没有参数,括号去掉是可以省略的。 /* 例:var obj=new Object; */
构造函数的初始化机制(初窥门径):创建一个空对象,然后通过this对这个对象进行一系列初始化,如果构造函数返回了对象,那么这个对象就会被废弃,否则就表示这个对象值。
运算符概述:
运算符用于算术表达式,比较表达式,逻辑表达式,赋值表达式等。
参照表:http://blog.sina.com.cn/s/blog_c0f3bc520102va6z.html
说明:1.上面的优先级高于下面的优先级
2.被水平线分隔开来的运算符具有不同的优先级;分割线内的一组运算符具有相同的优先级。
3.标题A表示运算符的结合性,L(从左至右)或R(从右至左)
4.标题N表示操作数的个数
5.类型,->左边:操作数的期望类型;->右边:结果类型
操作数个数:根据运算符可操作的操作数的个数分为一元运算符,二元运算符,三元运算符 /*对应C语言里面的"元"->"目"*/
1,有的运算符既是一元也是二元运算符,比如:"-".
2,三元运算符只有一个:条件判断运算符"?:"
操作数类型和结果类型:
通常运算符对于操作数有期望的数据类型,因此js运算符会根据需要对操作数进行类型转换。
特殊说明:
1."+",如果是二元运算符的时候,两个操作数有一个是字符串,则会执行字符串的连接操作。
2."<",会根据操作数类型的不同对其做不同的比较。
左值:(lvalue),指表达式只能出现在赋值运算符的左侧。
PS:在JS中,变量、对象属性和数组元素均是左值。 /*后文阅读下来的情况,可以把左值定死在这几个类别上。*/
PS:ECMAScript规范允许内置函数返回一个左值,但自定义的函数则不能返回左值。/* 不是很理解这句话 ,可能在深入学习了函数机制之后可以了解……*/
运算符的副作用:说白了就是对原操作数有影响。(这会导致原操作数同时调用其他函数时会出现问题……)
1."++","--"会对操作数做赋值处理。
2.delete运算符删除属性相当于给这个属性赋值undefined。
3.函数调用表达式和对象创建表达式在使用了这些运算符并产生副作用的时候,称这些表达式是有副作用的。
运算符的优先级:优先级高的先执行,低的后执行。
1,属性访问表达式和调用表达式的优先级高于参照表中列出的所有的运算符。
2,赋值运算的优先级非常低,通常总是最后执行的。
运算符的结合性:它指定了在多个具有同样优先级的运算符表达式中的运算顺序。
1.从左往右大家都觉得很正常
2.从右往左,如一元操作符、赋值和三元条件运算符。
运算顺序:运算符的优先级和结合性规定了它们在复杂的表达式中得运算顺序。
经典的运算符的副作用:假设a=1,那么b=(a++)+a的结果是3。
算术表达式:含有算术运算符的表达式(个人理解)。
基本的算术运算符:*,/,%,+,-。除"+"之外,其他都是把操作数变成数字或者NaN进行运算 /*所有与NaN做的数字运算结果均为NaN*/
PS:所有的数字都是浮点型,所以5/2这种两个整数相除的结果不是整数,而是2.5。
PS:"%"计算的是第一个操作数对第二个操作数的余数。符号和被除数的符号保持一致;求余运算也适用于浮点数。
"+"运算符:这里特指二元运算符
1.可以作为数字的加法;
2.可以作为字符串的加法(优先)。
3.如果其中有该操作数是对象,则以valueOf方法执行转换,日期类通过toString()。
4.如果valueOf方法不具备,则再通过toString()方法来执行转换。
5.转换结果如果有字符串,则进行字符串连接;否则进行数字加法操作。
一元算术运算符:作用于单独的操作数,并产生一个新值。右结合的一元运算符具有很高的优先级。
"+":把操作数转换为数字(或者NaN)。
"-":在"+"的基础上改变运算结果的符号。
"++":对其操作数(数字)进行增量的操作,操作数是一个左值(变量,数组元素或对象属性)。
在操作数左边叫"前增量":它对操作数进行增量计算,并返回计算后的值。
在操作数右边叫"后增量":它对操作数进行计算,但返回未做增量计算的值。
"--":与"++"类似。
位运算符:可以对由数字表示的二进制数据进行更低层级的按位运算。
PS:位运算要求的操作数必须是32位的整数 。
PS:位运算符会将NaN、Infinity和-Infinity都转换为0。
按位与(&),只有两个都是1才是1,否则为0。例:0x1234&0x00FF=0x0034
0x1234 0001001000110100
0x00FF 0000000001110111
0x0034 0000000000110100
按位或(|),两个只要有一个是1,就是1,否则为0。
按位异或(^),两个操作数不同为1,相同为0。
按位非(~),一元运算符,将操作数的所有位取反。相当于改变它的符号并减1。
左移(<<),移动的位数(0~31之间),例7<<3=56 你可以理解为7乘以2的3次方。
带符号的右移(>>),移动位数与左移一样,不同的是填补的位根据原操作数的符号决定。正数0,负数1。
无符号右移(>>>),左边高位总是用0填补。-1 对应的就是0xFFFFFFFF。
PS:程序的处理:如果第一位是1,则先取反加1,计算的结果再加上符号;否则直接计算16进制得到一个正整数。
关系表达式:用于测试两个值之间的关系,根据关系是否存在返回true或false。 /*这里我不禁对"值"这个概念不确定了……*/
相等和不等运算符:根据结果返回true或false
"==":相等运算符。此前有提过,比较的是两个值是否相等。可进行类型变换的比较
1.如果类型相同,则严格比较下相等则相等,否则不等。
2.类型不同,如果值相同则相等。
3.如果一个为null,一个为undefined,则相等
4.一个值为对象,一个值为数字或字符串,则转换对象比较,可能相等。
"===":也称为严格相等运算符,或恒等运算符。不进行类型变换的比较。
1.如果类型不同,则不相等
2.如果两个值都是null或者都是undefined,则不相等
3.如果两个值都是布尔值true或都是布尔值false,则它们相等
4.如果其中一个值为NaN,则它们不相等
5.如果一个为0,另一个为-0,则它们相等
6.如果两个引用值指向同一对象,数组或函数,则相等,否则不等。
"! =":不相等,与"=="对应。
"!==":不严格相等
比较运算符:检测两个操作数的大小关系
小于(<),大于(>),小于等于(<=),大于等于(>=)
只有数字和字符串才能真正执行比较操作。其他类型需类型转换:
1.对象,类型转换,先valueOf,然后toString()
2.字符串比较根据16位Unicode字符的索引顺序,可根据ASCII进行比较
3.如果有操作数不是字符串,则将会转换为数字进行数值比较。
4.如果有一个结果转换为NaN,则比较操作符总是返回false
PS:再一次强调,16位整数值的意思是16位的二进制数表示的序列,最终还是比较数字…… 刚一瞬间这个概念又恍惚了,所以及时点醒一下。
PS:可参照String.localCompare()方法来获取更多字符串比较的相关信息。这个方法更加强壮可靠,参照本地语言的字母表定义的字符次序。
PS:再次回顾一下运算符"+"偏爱字符串,而比较运算符偏爱数字。
in运算符:希望左操作数是一个字符串或可以转换为字符串,希望右操作数是一个对象。如果右操作数对象有名为左操作数的属性名,则返回true。
1.每个对象都有toString()方法,所以 "toString" in Object 总是true。
2.对象是数组,则属性名是索引值。
instanceof运算符:希望左操作数是一个对象,希望右操作数是标识对象的类。如果左侧的对象是右侧类的实例,则返回true。
1.所有对象都是Object的实例。
2.判断是否是一个类的实例时,判断会包含对"父类"的检测。
PS:想了解它的工作机制,必须先理解"原型链"(prototypechain),它是js的继承机制。o instanceof f,js首先计算f.prototype,然后在原型链中查找o,如果找到,那么表达式返回true。这里有段注解有简单的解释,先暂时填充在这里:
对象o存在一个隐藏的成员,它指向其父类的原型,如果父类的原型是另外一个类的实例的话,则这个原型对象也存在一个隐藏的成员,总之最终会回到同一个祖宗上。f.prototype就是和o的这个隐藏成员所指向的一个个比较(至于比较什么,目前不得而知),然后确认是否找到……
逻辑表达式:"&&","||","!"对操作数进行布尔算术运算,经常是由逻辑运算符和关系表达式组合的表达式。
逻辑与(&&):all true->true,else -> false
1.两边都是布尔值,则根据上述的描述,可以得到对应的结果
2.当两边不是布尔值,而是真值假值的时候:(会提到它的工作机制)
先判断左边的是否是真值,如果是假值,则直接返回左操作数的值,然后结束(这种行为称做"短路"); /*好处是如果右操作数有意义是以左操作数有意义为前提的时候,这种写法可以避免程序抛出错误。*/
否则返回右操作数的值,然后结束。
案例:if(a==b) stop(); ===> (a==b) && stop(); /*大多数情况还是把它作为布尔值的计算,即和关系表达式组合的使用*/
逻辑或(||):all false->false,else -> true
与&&不同的地方,判断左边的是真值,则直接返回左操作数的值,然后结束。
1.可以用它来给函数设置默认值,例如: var max=max_width || preferences.max_width || 500.
逻辑非(!):与前两个逻辑运算符不同,它会直接把操作数转换为布尔值,然后进行求反。
赋值表达式:
即"="。它希望左操作数是一个左值(变量,对象属性或数组元素),右操作数可以是任意类型的任意值。
副作用就是改变了左值的值,会对后续使用这个左值有影响。
PS:因为它的结合性是从右至左。所以i=j=k=0的正确理解方式是:k=0,j=k,i=j。
带操作的赋值运算:将其他运算符和赋值运算符连接起来,从而提供更加快捷的运算方式。
表达式基本是: a op= b (a只计算了一次) 等价于 a=a op b (a计算了两次),因此在包含副作用的表达式,两者就不等价了。
例:data[i++]*=2;和data[i++]=data[i++]*2; 后者非常有意思的是,虽然赋值表达式是以从右向左结合,初学者会误以为先计算右边的表达式,但实际上是,赋值表达式优先级非常低,所以先不考虑赋值的情况下,需按从左往右的顺序,将属性访问表达式进行计算(优先计算)。
表达式计算:
js可以解析运行又js源代码组成的字符串,并产生一个值。通过全局函数eval()来完成这个工作。 /* 例:eval("3+2") 结果是5。 */
1.eval()是一个函数,但是基本把它当成运算符来对待了。函数本身允许如下操作:
var old_back = mui.back; mui.back = function(){ var btn = ["确定","取消"]; mui.confirm(‘确认关闭当前窗口?‘,‘Hello MUI‘,btn,function(e){ if(e.index==0){ //执行mui封装好的窗口关闭逻辑; old_back(); } }); }
这里的mui.back函数名,即函数可以有多个命名,而调用同一个函数。但如果eval也可以的话,解释器就无法放心优化其他与eval调用同一个函数的函数。
2.由于对eval()进行了诸多的限制,所以使得调用它的函数无法被解析器做进一步优化,需慎用啊……
eval():只有一个参数,如果该参数不是字符串,则直接返回改参数,否则会把字符串当成js代码进行编译。
如果编译错误则抛出一个语法错误异常;
如果编译成功则执行代码,并返回字符串中的最后一个表达式或语句的值,如果最后一个表达式或语句没有值,则最终返回undefined。
如果字符串执行抛出一个异常(非语法错误异常),这个异常将把该调用传递给eval()。
PS:关于eval()最重要的是,它使用了调用它的变量作用域环境。比如它定义一个局部变量,则这个局部变量的作用域是调用eval()函数的函数,而不是仅仅是eval()函数本身。
未完待续……
标签:
原文地址:http://www.cnblogs.com/luchaohai/p/4940469.html