• 六天玩转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函数》。

  • 相关阅读:
    vue脚手架配置插件image-webpack-loader 图片压缩
    umi-request 一个新的请求工具
    uniapp 中出现 wx.config is not a function
    项目跨域开启代理,前端不再需要找后端了!!!
    vue脚手架项目 以及react项目,webpack配置去除多余css样式
    uniapp 实现动态切换全局主题色
    uniapp 开发app 开启页面的下拉刷新无效
    C# ? 语法糖
    正则表达式
    nginx 自签名
  • 原文地址:https://www.cnblogs.com/grimm/p/5446737.html
Copyright © 2020-2023  润新知