• JavaScript基础入门教程(三)


    说明

      前面的两篇博客介绍了js中的基本知识中的变量类型、标识符等。这篇博客主要谈表达式以及运算符。

    原始表达式

      原始表达式就是表达式中最小的,不能在分割的表达式,一般指变量、常数直接量、关键字(true、false、null、this),这里需要注意的是undefined是一个全局变量,而不是关键字。

    对象和数组的初始化表达式

      数组初始化表达式是一个方括号,中间放置数组中的元素,元素间用逗号分割。元素可以是表达式、数组、空。

    1 []    //一个空数组,数组里面没有元素
    2 [1+2,3+4]    //一个拥有两个元素的数组,第一个是3,第二个是7。
    3 [[1,2,3],[4,5,6],[7,8,9,]]    //数组中的元素还可以是数组。
    4 [1,,,,5]    //数组中的元素可以为空,这时空位会填充值undefined。

      在执行var a=[1,,,,5]这条语句后,在chrome中表现为下图所示:

      对象初始化表达式和数组初始化表达式非常类似。

    1 var stu={name:"张三", age:15};    //定义一个拥有两个属性的对象。

      上面这行初始化对象的表达式类似于下面的数组,事实上js也确实支持这种写法访问对象中的属性。

    1 stu["name"]="张三";
    2 stu["age"]=15;

      如果你学过java那么上面那句var stu={name:"张三", age:15};可以很容易的写成下面的java代码:

     1 class Student{
     2     String name;
     3     int age;
     4 
     5     Student(String name, int age){
     6         this.name=name;
     7         this.age=age;
     8     }
     9 }
    10 
    11 Student stu=new Student("张三", 15);

       不过js可以完美支持多层的内部类,而java却对内部类有好多限制。

    属性访问表达式

      在js中,属性访问有①expression.identifier、②expression[expression]这两种方式。其中第一种虽然方便却只能用于对象,而且要求访问的属性(identifier)不能是关键字、不能含有空格、不能是数字。而第二种访问方式不仅支持对象还能用于数组,并且用于对象访问自己的属性时没有第一种那么多的限制。

    1 var o={x:1, y:{z:3}};
    2 var a=[o, 4, [5, 6]];
    3 o.x   //1:访问o的x属性
    4 o.y.z    //3:访问o的y的z属性
    5 o["x"]    //1:访问o的x属性
    6 a[1]    //4:访问a的索引为1的元素
    7 a[2]["1"]    //6:访问a[2]中的索引为1的元素
    8 a[0].x    //1:通过访问a[0]得到o,再访问o的x属性

    运算符

      js所支持的运算符如上图所示,学过其它编程的同学基本上看上一眼就知道了。这里只对需要注意的部分做出说明。在基本算术运算符(加减乘除和求余)只有加法是相对有难度的,因为有时候它是加法,而有时候它又是字符串连接符,具体用法可概括为只有+运算符两边都是数字才是加法,更详细的用法请参考这篇博客。此外还需要谈的就是求余运算符,其运算结果的符号和左操作数相同,即5%1=1、-5%1=-1。

      在js的位运算时,会有一个隐式转换过程。默认情况下js中的数字都是存储为64位浮点数格式,在位运算的时候会转换为32位整数,所以隐式转换会截断小数部分和整数中超过32位能表示的部分。此外NaN、infinity和-infinity会转换为0。

      关于等于(==)运算符和严格等于(===)运算符请参考本系列教程的第一篇

    in运算符

      in运算符希望它的左操作数是一个字符串或者能转换为字符串,希望它的右操作数是一个对象。如果右侧对象拥有一个名为左操作数的属性则整个in表达式返回true。

    1 var point={x:1, y:1};
    2 "x" in point    //true:对象中有一个名为x的属性
    3 "z" in point    //false:对象中不存在一个名为z的属性
    4 "toString" in point    //true:对象继承了toString()方法
    5 
    6 var data=[7, 8, 9];
    7 "0" in data    //true:数字包含下标为0的元素
    8 1 in data    //true:数字转换为字符串
    9 3 in data    //false:没有索引为3的元素

    &&与||

      学过C和java等语言的同学应该知道这两个运算符有一个特性就是短路,这个特性在js中也同样存在,习惯性利用这种特性为函数提供默认值。

    1 function copy(o, p){
    2     o.p = p || {};
    3 }

      上面的代码中,如果copy函数被调用的时候p形参有值,则将p复制给o.p,否则将默认值{}复制给o.p。

    表达式计算

      在js中存在一个函数叫eval(),虽然这是一个函数,但一般被当成运算符来对待。一般来说函数是可以起别名的,比如var f=eval;再通过f调用eval函数。但是一般不推荐对eval函数起别名,ECMAScript3标准甚至直接禁止对eval函数起别名,所以说它几乎被当成运算符对待。之所以这样是因为这涉及到代码优化的问题,如果允许对eval函数起别名将严重影响代码的优化过程。

      上面谈了eval的特点,现在来说一下它的作用。这个函数只有一个参数,且一般是字符串。如果传入的参数是字符串,js解释器会把这个字符串当成js代码来执行,且其执行环境为直接调用这个eval函数的上下文(下面将详细解释),如果其传入的参数不是字符串,则eval函数直接返回这个参数值。

    1 var global=1;
    2 var foo = function(ele){
    3     var local = 2;
    4     eval(ele);
    5     console.log(where);
    6 }
    7 foo("console.log(global);console.log(local);var where='I am here!';");
    8 console.log(where);

      上面这行代码的执行结果(在chrome中)如下图所示:

      根据上面所说的eval的执行环境为直接调用eval的上下文,在上图显示的结果中为第8行所处的上下文,这样的话eval自然能访问global和local变量,但是其定义的where变量只能让foo函数内部且在第8行以后的代码访问,这样以来第9行代码能正确执行,而第12行代码抛出异常。

      前面谈过ECMAScript3禁止对eval起别名,但是现实中大多数解释器的实现并非如此,之后的ECMAScript5标准也允许别名调用,但规范了其行为。当eval被别名调用时,其运行环境永远为全局环境,与其调用者环境没有关系,也就是说间接调用(别名调用)无法读、写、定义局部变量。

     1 var geval = eval;
     2 var x = "global", y = "global";
     3 function f(){
     4     var x="local";
     5     eval("x += ' changed';");
     6     return x;
     7 }
     8 function g(){
     9     var y="local";
    10     geval("y += ' changed';");
    11     return y;
    12 }
    13 console.log(f(), x);
    14 console.log(g(), y);

      上面代码执行的结果(在chrome中)为:

  • 相关阅读:
    315前夜
    学习,真正地学习
    华仔成都2007
    如果建筑师必须如网页设计师一般工作[转]
    “散文”
    笨小孩
    幸存者游戏启示[摘录]
    通过配置获取客户端所属服务器IP或服务器名
    观《三国之见龙卸甲》
    散文(二)
  • 原文地址:https://www.cnblogs.com/flyor/p/8495260.html
Copyright © 2020-2023  润新知