zoukankan      html  css  js  c++  java
  • 六天玩转javascript:javascript变量与表达式(2)

    本系列内容为本人平时项目实践和参照MDN,MSDN,《javascript语言精粹》,《Effective Javascript》等资料,并且整理自己EverNote的日常积累整理所写,时间太长有一些代码样例来源不可考。本系列不允许任何形式的转载,谢谢。 from yeanzhi

    大纲

    第一天:javascript变量,操作符与变量作用域

    第二天:javascript函数

    第三天:对象与原型

    第四天:再谈函数与继承

    第五天:内置对象

    第六天:特殊性质与技巧

    第一天:javascript变量与表达式(2)

    运算符

    JavaScript中的运算符有以下几种

    • 赋值运算符
    • 比较运算符
    • 算术运算符
    • 位运算符
    • 逻辑运算符
    • 字符串运算符
    • 特殊运算符

    本文我们只对几个重要或者有特殊技巧的运算符进行介绍,着重简介特殊运算符(操作符)

    逻辑运算符

    逻辑运算符通常用于布尔型(逻辑)值;这种情况,它们返回一个布尔型值。然而,&&和||运算符实际上返回一个指定操作数的值,因此这些运算符也用于非布尔型,它们返回一个非布尔型值。
    技巧:

    在条件中使用逻辑与或

    var foo = 10;  
    foo == 10 && doSomething(); //等同于if (foo == 10) doSomething(); 
    foo == 5 || doSomething(); //等同于if (foo != 5) doSomething();

    逻辑或还可用来设置默认值,比如函数参数的默认值。
    arg1 = arg1 || 10;

    特殊运算符

    JavaScript有下列特殊运算符:

    • 条件运算符
    • 逗号运算符
    • delete
    • in
    • instanceof
    • new
    • this
    • typeof
    • void

    1,条件运算符(唯一的三目运算符)
    condition ? val1 : val2
    2,逗号操作符
    逗号操作符(,)对两个操作数进行求值并返回第二个操作数的值。它常常用在for循环中,在每次循环时对多个变量进行更新。比如:

    for (var i = 0, j = 9; i <= 9; i++, j--)
        expression

    关于循环技巧

    var sum = 0;  
    for (var i = 0, len = arrayNumbers.length; i < len; i++) {  
        sum += arrayNumbers[i];  
    }
    //还有一种写法
    for (var i = 0, item; item = a[i++];) {
        // Do something with item
    }
    //这里我们使用了两个变量。
    //for 循环中间部分的表达式仍然用来判断是否为真—如果为真,那么循环继续。
    //因为i每次递增 1,这个数组的元素会被逐个传递给 item 变量。
    //当遇到一个假值元素(如undefined)时,循环结束。

    这里并不推荐for in,如果有人向 Array.prototype 添加了新的属性,使用这样的循环这些属性也同样会被遍历:

    for (var i in a) {
      // Do something with a[i]
    }

    3,delete
    删除操作符删除一个对象的属性.它不会触及原型链中的任何属性
    注意:
    3.1,在严格模式中,如果属性是一个不可配置(non-configurable)属性,删除时会抛出异常,非严格模式下返回 false。其他情况都返回 true。
    3.2,delete 操作符与直接释放内存(只能通过解除引用来间接释放)没有关系
    3.3,一些对象的属性不能被delete.ECMA262规范中把这些属性标记为DontDelete

    x = 42;        // 隐式声明的全局变量
    var y = 43;    // 显式声明的全局变量
    myobj = new Number();
    myobj.h = 4;    // 添加属性h
    myobj.k = 5;    // 添加属性k
    
    delete x;       // 返回 true (隐式声明的全局变量可以被删除)
    delete y;       // 返回 false (显式声明的全局变量不能被删除,该属性有DontDelete标记)
    delete Math.PI; // 返回 false (内置对象的内置属性不能被删除, 该属性有DontDelete标记)
    delete myobj.h; // 返回 true (用户定义的属性可以被删除)
    with(myobj) { 
      delete k;    // 返回 true (相当于delete myobj.k)
    } 
    delete myobj;   // 返回 true (隐式声明的全局变量可以被删除)

    3.4,不能删除一个对象从原型继承而来的属性(不过可以从原型上直接删掉它).

    function Foo(){}
     Foo.prototype.bar = 42;
     var foo = new Foo();
     delete foo.bar;           // 无效的操作
     alert(foo.bar);           // alerts 42, 继承的属性
     delete Foo.prototype.bar; // 直接删除原型上的属性
     alert(foo.bar);           // alerts "undefined",已经没有继承的属性

    3.5,删除数组元素
    当删除一个数组元素时,数组的 length 属性并不会变小。例如,如果删除了a[3], a[4]仍然是a[4], a[3]成为undefined.
    当用 delete 操作符删除一个数组元素时,被删除的元素已经完全不属于该数组。下面的例子中, name[3] 被使用delete彻底删除。

    var name = ["ye","an","z","h","i"];
    delete name[3];
    console.log(name);//[ 'ye', 'an', 'z', , 'i' ]
    console.log(name[3]);//undefined
    console.log(name.length);//5
    for(var v in name){
        console.log(v);//0 1 2 4 
    }

    如果你想让一个数组元素的值变为 undefined 而不是删除它,可以使用 undefined 给其赋值而不是使用 delete 操作符。
    数组元素删除应使用splice函数。

    4,函数表达式
    function 关键字可用来在一个表达式中定义一个函数。上一篇在闭包的地方有函数表达式的例子
    函数表达式(function expression)非常类似于函数声明(function statement),并且拥有几乎相同的语法。函数表达式与函数声明的最主要区别是函数名称,在函数表达式中可忽略它,从而创建匿名函数。

    匿名函数创建递归技巧
    匿名函数可以通过arguments.callee 的属性来完成递归调用。这个属性通常指向当前的(调用)函数,因此它可以用来进行递归调用:

    var charsInBody = (function(elm) {
        if (elm.nodeType == 3) { // TEXT_NODE
            return elm.nodeValue.length;
        }
        var count = 0;
        for (var i = 0, child; child = elm.childNodes[i]; i++) {
            count += arguments.callee(child);
        }
        return count;
    })(document.body);

    5,get,set操作符
    get,set语法将对象属性绑定到该属性查找时将调用的函数。

    var log = ['test'];
    var obj = {
        get latest () {
            if (log.length == 0) return undefined;
            return log[log.length - 1]
        },
        set current (str) {
            log[log.length] = str;
        }
    }
    obj.current = 'fa';
    console.log (obj.latest);//输出 "test".

    7,in
    如果指定的属性存在于指定的对象中,则 in 运算符会返回 true。

    // 数组
    var trees = new Array("ye", "an", "zhi");
    0 in trees        // true
    6 in trees        // false
    "bay" in trees    // false (必须使用索引号,而不是数组元素的值)
    "length" in trees // true (length是一个数组属性)

    使用delete运算符和将属性赋值为undefined

    var name = ["ye","an","z","h","i"];
    delete name[3];
    3 in name; // 返回false

    8,instanceof
    instanceof 运算符可以用来判断某个构造函数的prototype属性是否存在另外一个要检测对象的原型链上。
    这个运算符将在《对象与原型》中讲解

    9,let 操作符
    在上一篇变量作用域中有讲解

    10,new 操作符
    这个运算符将在《对象与原型》中讲解

    11,this操作符
    JavaScript函数中的this关键字的行为相比起其他语言有很多不同。在JavaScript的严格和非严格模式下也略有区别。在绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,在每次函数被调用时this的值也可能会不同。ES5引入了bind方法来设置函数的this值,而不用考虑函数如何被调用的。
    11.1 Global context 全局上下文
    在全局运行上下文中(在任何函数体外部),this 指代全局对象,无论是否在严格模式下。
    this===window //true

    11.2 Function context 函数上下文
    在函数内部,this的值取决于函数是如何调用的。
    非严格模式下,this的值是一个对象且默认是全局对象

    function f1(){
      return this;
    }
    
    f1() === window; // global object

    严格模式下 上面应该返回 undefined ,但是这个并没有在浏览器中的到广泛的支持,一般会返回一个window 对象

    11.3As an object method 作为对象方法
    函数以对象里的方法的方式进行调用时,它们的this由调用该函数的对象进行设置。

    var o = {
      prop: 37,
      f: function() {
        return this.prop;
      }
    };
    
    console.log(o.f()); // logs 37

    11.4 As a constructor 作为构造函数
    所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。

     var x = 2;
     function test(){
       this.x = 1;
     }
     var o = new test();
     alert(x); //2
     alert(o.x); //1

    11.5 apply调用
    apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。也就是说js里面的this是可以改变的

    var val = hi;
        function Test(hello){
            this.val = hello;
        }
        Test.prototype.printval = function(){
            console.log(this.val);
        }
        var o = new Test('hello');
        o.printval();//hello
        o.printval.apply();//0
        o.printval.apply(o);//hello

    apply()的参数为空时,默认调用全局对象。
    最开始输出hello 证明 this为对象o的
    然后输出hi 证明this是 全局变量
    最后输出hello 证明this被重置为对象o的
    另外

    apply有一个孪生兄弟 call,call 和 apply 都是为了改变某个函数运行时的 context 即上下文而存在的,换句话说,就是为了改变函数体内部 this 的指向。因为 JavaScript 的函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。
    二者的作用完全一样,只是接受参数的方式不太一样。
    JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时,用 call,而不确定的时候,用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。
     var x = 0;
      function test(){
        alert(this.x);
      }
      var o={};
      o.x = 1;
      o.m = test;
      o.m.apply(); //0

    12,typeof 操作符
    typeof操作符返回一个字符串,代表一个未被求值的操作对象(unevaluated operand)的类型。
    这个在js的反射中用的比较多
    值得一说的是 typeof null的值是object而typeof undefined 的值是undefined
    而且一下也是需要注意的

    typeof Math.LN2 === 'number';
    typeof Infinity === 'number';
    typeof NaN === 'number'; // 尽管NaN是"Not-A-Number"的缩写,意思是"不是一个数字"
    typeof Number(1) === 'number'; // 不要这样使用!
    // 下面的容易令人迷惑,尽量不要用
    typeof new Boolean(true) === 'object';
    typeof new Number(1) ==== 'object';
    typeof new String("abc") === 'object';

    注意
    1,正则表达式字面量在某些浏览器中不符合标准

    typeof /s/ === 'function'; // Chrome 1-12 ... // 不符合 ECMAScript 5.1
    typeof /s/ === 'object'; // Firefox 5+ ...    // 符合 ECMAScript 5.1

    2,原型链中的任何属性都会产生值

    typeof flight.toString // function
    typeof flight.constructor// function

    我们有两种方法去掉这些不需要的属性,一是在程序中做检测,丢掉值为函数的属性,因为我们通常项目编码中需要的是数据而不是函数,第二是使用hasOwnProperty()方法,如果是对象拥有独有的属性,会返回true

    13,void 操作符
    void 运算符会对它的操作数表达式进行求值,然后忽略掉求值的结果,直接返回 undefined

    经常会有人用 void(0) 或者 void 0 来代替 undefined 变量来表示 undefined 值,因为他们担心自己拿到的 undefined 这个变量的值可能不是 undefined

    经常会有人用 void(0) 或者 void 0 来代替 undefined 变量来表示 undefined 值,因为他们担心自己拿到的 undefined 这个变量的值可能不是 undefined

    void function iife() {
        var bar = function () {};
        var baz = function () {};
        var foo = function () {
            bar();
            baz();
         };
        var biz = function () {};
    
        foo();
        biz();
    }();

    当用户点击一个以 javascript: 协议开头的 URI 时,浏览器会对冒号后面的代码进行求值,然后把求值的结果显示在页面上,这时页面基本上是一大片空白,这通常不是我们想要的。只有当这段代码的求值结果是 undefined 的时候,浏览器才不会去做这件傻事,所以我们经常会用 void 运算符来实现这个需求。像下面这样:

    <a href="javascript:void(0);">
      这个链接点击之后不会做任何事情,如果去掉 void(),
      点击之后整个页面会被替换成一个字符 0。
    </a>
    
    <a href="javascript:void(document.body.style.backgroundColor='green');">
      点击这个链接会让页面背景变成绿色。
    </a>

    注意,虽然这么做是可行的,但利用 javascript: 伪协议来执行 JavaScript 代码是不推荐的,推荐的做法是为链接元素绑定 click 事件。

    14,yield 操作符
    说道yield就不得不提到generator 函数,在ECMAScript 6 引入了generator函数,作用就是返回一个内部状态的遍历器,主要特征是函数内部使用了yield语句。
    当调用generator函数的时候,该函数并不执行,而是返回一个遍历器(可以理解成暂停执行)。以后,每次调用这个遍历器的next方法,就从函数体的头部或者上一次停下来的地方开始执行(可以理解成恢复执行),直到遇到下一个yield语句为止,并返回该yield语句的值。

    ECMAScript 6草案定义的generator函数,需要在function关键字后面,加一个星号。然后,函数内部使用yield语句,定义遍历器的每个成员。

    function* helloWorldGenerator() {
        yield 'hello';
        yield 'world';
    }
    var hw = helloWorldGenerator();
    hw.next() 
    // { value: 'hello', done: false }
    
    hw.next()
    // { value: 'world', done: false }
    
    hw.next()
    // { value: undefined, done: true }
    
    hw.next()
    // Error: Generator has already finished

    yield有点类似于return语句,都能返回一个值。区别在于每次遇到yield,函数返回紧跟在yield后面的那个表达式的值,然后暂停执行,下一次从该位置继续向后执行,而return语句不具备位置记忆的功能。

    上面代码定义了一个generator函数helloWorldGenerator,它的遍历器有两个成员“hello”和“world”。调用这个函数,就会得到遍历器。
    我会在后期《特殊性质与技巧》中使用一个篇幅来介绍yield,它是koa框架实现最核心的理念“减少回调金字塔”必不可少的工具。
    接下来,我们将进入第二天学习《javascript函数》。

  • 相关阅读:
    UVA 10462 Is There A Second Way Left?(次小生成树&Prim&Kruskal)题解
    POJ 1679 The Unique MST (次小生成树)题解
    POJ 2373 Dividing the Path (单调队列优化DP)题解
    BZOJ 2709 迷宫花园
    BZOJ 1270 雷涛的小猫
    BZOJ 2834 回家的路
    BZOJ 2506 calc
    BZOJ 3124 直径
    BZOJ 4416 阶乘字符串
    BZOJ 3930 选数
  • 原文地址:https://www.cnblogs.com/grimm/p/5446737.html
Copyright © 2011-2022 走看看