一、函数定义表达式
表达式的值是 js 函数的表达式称之为 函数定义表达式,或“函数直接量”。典型的函数定义表达式就是一个包含关键字function和括号加上函数体,如
var square = function (x) { return x*x }
二、属性访问表达式
属性访问表达式运算得到一个对象属性或一个数组元素的值。js 为属性访问定义了两种语法:
expression.identifier
expression[expression]
注意,第一种方式只适用于要访问的属性名称是合法的标识符,并且需要知道要访问属性的名字。如果属性名称是一个保留字或包含空格和标点符号,或是一个数字(对于数组来说),则必须使用第二种方法。当属性名是通过运算的出的值而不是笃定的值的时候,也是必须使用第二种方括号写法。
三、调用表达式
js 中的调用表达式是一种调用(或执行)函数或方法的语法表示。以一个函数表达式开始,这个函数表达式指代了要调用的函数。
方法调用:属性访问表达式的调用。在方法的调用中,执行函数体的时候,作为属性访问主题的对象和数组便是其调用方法内this的指向。
四、对象创建表达式
对象创建表达式:创建一个对象并调用一个函数(这个函数称作构造函数)初始化新对象的属性。如:
new Object()
new Point(2,3)
new Object
可以看出,对象创建表达式和函数调用表达式非常类似,且若一个对象创建表达式不需要传入任何参数给构造函数的话,空圆括号是可以省略的。
五、运算符
in 运算符
in 运算符的左操作数是一个字符串或可以转化为字符串,右操作数是一个对象。如果右侧的对象拥有一个名为左操作数的属性名,那么表达式返回true
var point = { x:1, y:1 };
"x" in point // true
"z" in point // false 对象中不存在名为“z”的属性
"toString" in point // true 对象继承了toString()方法
instanceOf 运算符左操作数是一个对象,右操作数标志对象的类。如果左侧的对象是右侧类的实例,则表达式返回true,否则返回 false。
var d = new Date();
d instanceOf Date; // true,d是由Date()创建的
d instanceOf object; // true ,所有对象都是Object的实例
d instanceOf Number; // false ,d不是Number对象
var a =[1,2,3];
a instanceOf Array; // true,a 是一个数组
a instaceOf Object; // true,所有的数组都是对象
a instanceOf RegExp; // false,数组不是正则表达式
注意:所有的对象都是Object的实例,当通过instanceof判断一个对象是否是一个类的实例的时候,这个判断也会包含对“父类”的检测。如果instanceof的左操作数不是对象的话,instanceof 返回 false 。如果右操作数不是函数,则抛出一个类型错误异常。
这里先说下原型链。
为了计算表达式 o instanceof f。js 会先计算 f.prototype,然后在原型链中查找o,如果找到,那么 o 是 f(或者f的父类)的一个实例,表达式返回true。 如果 f.prototype不在 o 的原型链中的话,那么 o 就不是 f 的实例,instanceof 返回 false。
对象 o 中存在一个隐藏的成员,这个成员指向其父类的原型,如果父类的原型是另外一个类的实例的话,则这个原型对象也存在一个隐藏成员指向另外一个类的原型,这种链条将许多对象或类串接起来,即是原型链。
六、eval()
eval() 是一个函数,可以当作运算符来对待。eval() 只有一个参数,如果传入的参数不是字符串,它直接返回这个参数。如果参数是字符串,它会把字符串当成js代码进行编译,若编译失败则抛出一个语法错误(SyntaxRrror)异常。如果编译成功则开始执行这段代码,并返回字符串中最后一个表达式语句的值,如果最后一个表达式或语句没有值,则最终返回undefined。如果字符串抛出一个异常,这个异常将把该调用传递给 eval()。
关于eval() 最重要的是,它使用了调用它的变量作用域环境。也就是说,它查找变量的值和定义新变量和函数的操作和局部作用域中的代码完全一样。如果一个函数定义了一个局部变量x,然后调用eval("x") ,它会返回局部变量的值。如果它调用 eval("x=1"),它会改变局部变量的值。如果函数调用了eval("var y = 3;"),它声明一个新的局部变量y。同样地,一个函数可以通过如下代码声明一个局部函数:
eval("function f() { return x+1; }");
如果在最顶层代码中调用eval(),它会作用于全局变量和全局函数。
全局eval()
eval()具有更改局部变量的能力。当通过别名调用时,eval() 会将其字符串当成顶层的全局代码来执行。执行的代码可能会定义新的 全局变量和全局函数,或给全局变量赋值,但却不能使用或修改主调函数中的局部变量。ES5规范了eval()的行为。当直接使用非限定的““eval”名称(eval看起来像是一个保留字)来调用eval()函数时,称为“直接eval"。直接调用eval()时,它总是在调用它的上下文作用域内执行。其他的间接调用则使用全局的对象作为其上下文作用域,并且无法读、写、定义局部变量和函数。
var geval = eval; //使用别名调用eval将是全局eval
var x = "global", y = 'global'; //两个全局变量
function f() { // 函数内执行是局部eval
var x = "local"; // 定义局部变量
eval("x += 'changed';"); // 直接eval 更改了局部变量的值
return x;
}
function g() { // 这个函数内执行全局eval
var y = "local"; // 定义局部变量
eval("y += 'changed';"); // 直接调用改变了全局变量的值
return y;
}
console.log(f(), x); // 更改了局部变量,输出“local changed global”
console.log(g(), y); // 更改了全局变量,输出“local globalchanged”
严格eval()
在严格模式下,eval执行的代码段可以查询或更改局部变量,但不能再局部作用域中定义新的变量或函数。此外,严格模式将“eval”列为保留字,这让eval() 更像一个运算符。不能用一个别名覆盖eval() 函数。并且变量名、函数名、函数参数或者异常捕获的参数都不能取名为“eval”。