第3章 语言基础

  • 3.1 语法

    • 区分大小写:ECMAScript中一切都区分大小写,无论是变量、函数名还是操作符。
    • 标识符:变量、函数、属性或者函数参数的名称。标识符第一个字符必须是字母、下划线或美元符号($),剩下的字符可以是字母、下划线、数字和美元符号,即第一个字符不能是数字。关键字、保留字、true、false、null不能用作标识符。
    • 严格模式:ECMAScript 5增加了严格模式(strict mode)的概念。严格模式是一种不同的JavaScript解析和执行模型,ECMAScript 3 的一些不规范写法会在该模式下被处理,对于不安全的活动将抛出错误。选择这种语法形式的目的是不破坏ECMAScript 3 的语法(个人理解是在不重写的情况下利用严格模式检测ECMAScript 3 编写的代码中的特别不符合规范的部分)。严格模式会影响JavaScript执行的很多方面,所有现代浏览器都支持严格模式。
    • 语句结尾的分号:分号可以明确告知解析器语句结束的位置,否则由解释器自己去确定语句在哪里结尾,降低了性能。同时加上分号有助于通过删除空格压缩代码,同时可以让开发者明确语句是否结束。
    • 代码块:代码块由{}标识,控制语句最好都加上大括号,可以让内容更清晰,在需要修改代码时也可以减少出错的可能性。

    3.2 关键字和保留字

    • 关键字有特殊用途,比如控制语句的开始和结束,或者执行等待的操作。关键字不能用作标识符或属性名。
    • 保留字是将来可能做关键字的,同样不能用作标识符或属性名。

    3.3 变量

    • ECMAScript中的变量可以用于保存任何类型的数据。每个变量只不过是一个用于保存任意值的命名占位符。变量通过varletconst进行声明。

    • var 关键字(所有版本的ECMAScript都可以使用)

      var mes;//不初始化的情况下,变量会保存一个特殊值undefined
      var mes = 'h1';//定义并初始化其值
      
      var mes = 'h1';
      mes = 100;// 可以改变数据类型
      
      var mes ='h1';
          fou = false;
          age = 10;
      
      //作用域
      function test(){
          var mes = 'h1';
      }
      test();
      console.log(mes);//出错!使用`var`操作符定义的变量会成为包含它的函数的局部变量
      
      //声明提升
      function foo(){
          console.log(age);
          var age = 26;
      }
      foo();// undefined
      //上述代码不会报错是因为其等价于:
      function foo(){
          var age;// 提升(hoist),就是把所有变量声明都拉到函数作用域顶部。
          console.log(age);
          age = 26;
      }
      foo();
      
      //var多次声明
      function foo(){
          var age = 12;
          var age = 23;
          var age = 18;
          console.log(age);
      }
      foo();//18 相当于只在顶部声明了一次,后面都是重新赋值
      
    • let 声明(ES 6 及以后的版本中使用)

      • let声明的范围是块作用域,var声明的范围是函数作用域。

      • 块作用域是函数作用域的子集,适用于var的作用域限制也适用于let。

      • let 和 var 都不允许冗余声明,注意var的声明提升的特性增加了其容错的空间。

      • let 块作用域的特性使得嵌套使用相同的标识符不会报错。

      • var 和 let 两个关键字声明的并不是不同类型的变量,它们只是指出变量的作用域

      • 暂存死区:通过 let 声明的变量直到它们的定义被执行时才初始化。在变量初始化前访问该变量会导致 ReferenceError。该变量处在一个自块顶部到初始化处理的“暂存死区”中。

        //age 不会被提升
        console.log(age);//ReferenceError:age没有定义
        let age = 26;
        
      • 全局声明:var 关键字在全局作用域中声明的变量会成为window对象的属性,而 let 关键字则不会。

      • 用 let 进行条件声明,声明的变量会被限制在条件语句块中,条件语句块外相当于没有声明。

      • for 循环中的 let 声明

        for(var i=0; i<5; i++){
        	//循环逻辑
        }
        console.log(i) ;//5 var声明的循环变量会渗透到循环体外部
        
        for(let i=0; i<5; i++){
        	//循环逻辑
        }
        console.log(i);// ReferenceError 不会渗透出来
        
        for(var i=0; i<5; i++){
            setTimeout(() => console.log(i),0)
        }
        // 5、5、5、5、5 退出循环后执行超时逻辑,此时循环变量i退出循环的值是5
        
        for(let i=0; i<5; i++){
            setTimeout(() => console.log(i),0)
        }
        // 0、1、2、3、4 JavaScript引擎会在后台为每个迭代循环声明一个新的迭代变量(个人理解每次循环都是在执行一个块语句,因此不同块内的let声明可以保存不同的值)
        
    • const 声明(ES 6 及以后的版本中使用)

      • const 声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时出错。

      • 同样不允许重复声明

      • 声明的作用域也是块

      • 如果 const 变量引用的是一个对象,修改对象内部的属性并不违反const的限制。

      • 不能用 const 来声明循环迭代变量,因为迭代变量要求自增。但是如果只是想要一个每次循环都不变的变量时可以用const 来声明。

        for (const value of [1,2,3,4,5]){
        	console.log(value);
        }
        //1,2,3,4,5
        

3.4 数据类型

ECMAScript有6种简单数据类型(原始类型):undefined、Null、Boolean、Number、String、Symbol(ECMAScript 6 新增)。1种复杂数据类型 Object,Object是一种无序名值对的集合。ECMAScript不能定义自己的数据类型,所有值都可以用上述7种数据类型之一表示。

3.4.1 typeof 操作符

let message = "test";
console.log(typeof message)//"String" 因为typeof是操作符,可以不需要参数,也就可以不加括号
console.log(typeof(message));//"String" 也可以使用参数
console.log(typeof 89);// 89
console.log(typeof null);//NUll 被认为是对一个空对象的引用

3.4.2 Undefined 类型

Undefined类型只有一个值,就是特殊值undefined。当使用 var 或 let 声明了变量但是没有初始化时,就相当于给变量赋予了 undefined 值。

let message;
console.log(message == undefined);// true message未初始化,将其默认值undefined 参与比较

console.log(message);// undefined
console.log(age);//报错!因为age未声明

console.log(typeof message);//undefined 
console.log(typeof age);// undefined 虽然未声明,但是可以用typeof操作符,同样返回undefined

3.4.3 Null类型

Null类型同样只有一个值,即特殊值null。逻辑上,null值表示一个空对象指针。

let car = null;
console.log(car);//"object" 在定义将来要保存对象值的变量时,建议使用null初始化

console.log(null == undefined);//true undefined由null值派生,因此ECMA-262定义它们表面上相等

let message = null;//赋值null便于与undefined区分,且可以保持空对象指针的含义
let age;

if(message){
    //这个块不会执行
}
if(!message){
    //这个块会执行
}
if(age){
    //这个块不会执行
}
if(!age){
    //这个块会执行
}

3.4.4 Boolean类型

Boolean类型有两个字面值:true和false。这两个布尔值不同于数值,true不等于1,false不等于0。在JavaScript里,布尔值区分大小写,因此不要在需要用true的时候用True。

ECMAScript所有其他类型的值都可以转化成布尔值,转化函数为Boolean()。

Boolean(String);//非空字符串为true,空字符串为false
Boolean(Number);//非0数值,无穷值为true,0、NaN为false
Boolean(Object);//任意对象都为true,null为false
Boolean(Undefined);// N/A(缺失值,不存在的值)被转化为true,undefined被转化为false

3.4.5 Number类型

Number类型使用IEEE 754(二进制浮点数算术标准)格式来表示整数和浮点数(双精度值)。

let intNum = 56;//十进制整数

let octalNUm1 = 070;//八进制的56
let octalNum2 - 079;//无效八进制,当成79处理
let octalNum3 = 08;//无效八进制,当做8处理

//ECMAScript 2015或ES 6中的八进制通过前缀 0o 来表示,上述的八进制字面量在严格模式下无效
let octalNum = 0O75;

let hexNum1 = 0xA;//十六进制 10
let hexNum2 = 0x1f;//十六进制 31
  • 浮点值

    let floatNum1 = 1.1;
    let floatNum2 = 0.1;
    let floatNum3 = .6;
    
    let floatNum4 = 1.;//小数点后没有数字被当做1处理
    let floatNum5 = 10.0;//小数点后是0,被当做10处理。即被转换成整数
    
    let floatNum6 = 1.27e8;//科学计数法
    let floatNum7 = 1.98e-17 //浮点值的精度最高可达17位小数,因此最后的0.98e-17会被丢弃
    
    let floatNum8 = 0.0000006;//ECMAScript会将小数点后至少包含6个0的小数转化为科学记数法,因此该值会被转化为6e-7
    
    //0.1+0.2=0.300 000 000 000 000 04,而不是0.3,但0.25+0.05就可以得到0.3,原因待探索。
    
  • 值的范围

    • ECMAScript可以表示的最小数值保存在Number.MIN_VALUE中,这个值在多数浏览器中是5e-324,可以表示的最大数值保存在Number.MAX_VALUE中,。。。为1.797 693 134 862 315 7e+308
    • 如果超出了最大值最小值,则会被自动转换一个特殊的Infinity值。任何无法表示的负数用 -Infinity 表示,任何无法表示的正数用 Infinity 表示。
    • 如果计算返回无穷值,则该值不能再进一步用于任何计算。
    • 要确定一个值是否有限大可以用 isFinite()函数。
    • 使用 Number.NEGATIVE_INFINITY 和 Number.POSITIVE_INFINITY 也可以取 正、负 Infinity。
  • NAN

    • 有个特殊的数值叫 NaN,意思是 Not a Number,用于表示本来要返回数值的操作失败了(但并不抛出错误),进而中止代码执行。

    • 0/0; //NaN
      -0/+0; //NaN
      5/0; //Infinity
      5/-0; //-Infinity
      NaN/8; //NaN 任何涉及NaN的操作始终返回NaN
      NaN == NaN; //false NaN不等于包括NaN在内的任何值
      
      isNaN(NaN); //true
      isNaN(10); //false
      isNaN("10"); //false,可以转换成数值10
      isNaN("blue"); //true,不可以转换成数值
      isNaN(true); //false.可以转换成数值1
      
  • 数值转换

    有3个函数可以将非数值转换为数值:Number()、parseInt() 和 parseFloat() 。

    • Number(),转型函数,可用于任何数据类型。

      Number(true); //1
      Number(null); //0
      Number(undefined); //NaN
      Number("Hello"); //NaN
      Number(""); //0
      Number("00011"); //11
      Number("0xf"); //15
      
      //如果需要转换的是对象,则调用valueOf()方法,并按照转换规则转换返回的值,如果转换结果是NaN,则调用toString()方法,再按照转换字符串的规则转换。
      
    • parseInt(),主要用于将字符串转换为数值。字符串最前面的空格将被忽略,从第一个非空字符开始转换。如果第一个字符不是数字字符、加号或者减号,则立即返回NaN。

      parseInt(""); //NaN 这里与Number不一样
      parseInt("12hb"); // 12
      parseInt(123); // 123 数值会被先转换成字符串,再进行操作
      parseInt("0xA"); // 10,解释为十六进制整数
      parseInt(22.5); // 22
      
      parseInt("AF",16); // 175,第二个参数用于指定进制数
      parseInt("AF"); // NaN
      parseInt("10",2) // 2
      parseInt("10",8) // 8
      parseInt("10",10) // 10
      parseInt("10",16) // 16
      
    • ParseFloat(),同样用于将字符串转换为数值,类似parseInt(),但第一个出现的小数点是有效的,第二次就无效了。始终忽略字符串开头的”0“,因此八进制和十六进制数值始终返回0。它只解析十进制值,不能指定底数。

      parseFloat("123mkij"); // 123,按整数解析
      parseFloat("0xA"); // 0
      parseFloat("22.7"); // 22.7
      parseFloat("12.3.1"); // 12.3
      parseFloat("098.76"); // 98.76
      parseFloat("2.12e3"); // 2120
      

3.4.6 String类型

String(字符串)表示零或多个16位Unicode字符序列。字符串可以用双引号、单引号或反引号表示。

  • 字符字面量:用于表示非打印字符或其他用途的字符。如 \n 表示换行,\b 表示退格等,详见P38。如"\xnn"表示以十六进制编码nn表示的字符,例如 \x41 等于 “A”; “\unnnn” : 以十六进制编码 nnnn 表示的Unicode字符,例如 \u03a3 表示大写希腊字母 Sigma。

  • 字符串的特点:ECMAScript中的字符串是不可变的(immutable),一旦创建,值就不能变了,要修改某个变量的字符串,必须先销毁原始的字符串,然后再将新字符串保存到该变量。

  • 转换为字符串

    • toString(),适用于数值、布尔值、对象和字符串值,null和undefined没有toString()方法。

      let age = 11;
      age.toString(); //字符串"11"
      11.toString(); // 报错!11不是变量
      
      let f = true;
      f.toString(); // 字符串"true"
      true.toString(); //字符串“true",即关键字也可以调用toString()方法
      
      let num = 10;
      num.toString(); // "10"
      num.toString(2); // "1010",接收的参数表示将num转换成多少进制的数值,再转换成字符串
      num.toString(8); // “12”
      num.toString(10); // “10”
      num.toString(16); // “a”
      
    • String(),如果值有toString方法,则调用,否则null 返回“null”,undefined 返回“undefined”。可用于确认数据类型是null还是undefined。

  • 模板字面量

    模板字面量用反引号表示,可以保留换行字符、空格、缩进符,因此可以跨行定义字符串,在定义模板时非常有用。

    let pageHTML = `
    <div>
    	<a href = "#">
    		<span>Jake</span>
    	</a>
    </div>`;
    
  • 字符串插值

    技术上讲,模板字面量不是字符串,而是一种特殊的JavaScript句法表达式,只是求值后得到的是字符串。模板字面量在定义时立即求值并转换成字符串实例,任何插入的变量也会从其最近的作用域中取值。

    模板字面量最常用的一个特性就是支持字符串插值,也就是在一个连续定义中插入一个或多个值。字符串插值在JavaScript中通过${}实现,所有插入的值都会使用toString()强制转换为字符串。

    let value = 5;
    let exponent = 'second';
    let interpolatedString = 
        value + ' to the ' + exponent + ' power is ' + (value*value);
    let interoulatedTmolatedString =
        `${value} to the ${exponent} power is ${value*value}`;//效果同上
    
    `Hello,${`World`}!` //Hello,World!
    
    let foo = {toString:()=>'World' }; //toString后的括号里不能放参数
    console.log(`Hello,${foo}!`);//Hello,World! 在转换为字符串时调用了toString函数
    
    //在插值表达式中可以调用函数和方法
    function capitalize(word){
        return `${word[0].toUpperCase()}${word.slice(1)}`;//slice 表示从第几个字符开始,截取至末尾的字符串切片
    }
    console.log(`${capitalize('hellow')},${capitalize('world')}!`);Hello,World!
    
    //模板可以插入自己之前的值
    let value = '';
    function append(){
        value = `${value}abc`;
        console.log(value);
    }
    append(); // abc
    append(); // abcabc
    append(); //abcabcabc
    
  • 模板字面量标签函数

    模板字面量也支持标签函数(tag function),通过标签函数可以自定义插值行为。标签函数会接收被插值符号分隔后的模板和对每个表达式求值的结果。

    let a=6;
    let b=8;
    
    //...expressions,表示剩余参数,数量不定。下面的注释基于传入的参数是`${a}+${b} = ${a+b}`
    function zipTag(strings,...expressions){
        //这里的strings=['','+','=',''],即${}符号将表达式分隔后的原始字符串数组
        return strings[0] +
            expressions.map((e,i) => `${e}${strings[i + 1]}`).join('');
        //这里的expressions=['6+','8=','14']
        //传入(e,i),e表示值,i表示索引
    }
    
    let untagedResult = `${a}+${b} = ${a+b}`;
    let tagedResult = zipTag`${a}+${b} = ${a+b}`;
    
    console.log(untagedResult);//"6+8=14"
    console.log(tagedResult);//"6+8=14" 虽然结果一样,但可以通过标签函数获取原始字符串数组,以及每个${}求值结果的集合
    
  • 原始字符串

    使用模板字面量也可以直接获取原始的模板字面量内容,而不是被转换后的字符表示。为此,可以使用默认的String.raw标签函数:

    console.log(`\u00A9`);// ©
    console.log(String.raw`\u00A9`)// \u00A9
    
    function printRaw(strings){
        console.log('实际的字符:');
        for (const string of strings){
            console.log(string);
        }
        console.log('未被转义的字符:');
        for(const string of  strings.raw){//strings.raw报错!
            console.log(String);
        }
    }
    
    printRaw`\u00A9${'and'}\n`;这段代码报错,且and未能承担其正常功能。
    

3.4.7 symbol类型

Symbol(符号)是ECMAScript 6新增的数据类型。符号是原始值,符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。即用于创建唯一记号,进而用作非字符串形式的对象属性。

  • 符号的基本用法
let sym = Symbol();
typeof sym; // symbol
console.log(sym); // Symbol()

let a = Symbol('as');// 输入的字符串参数用于对该符号进行描述
let b = Symbol('as');
console.log(a == b); // false,字符串参数与符号定义或标识完全无关
console.log(a); // Symbol(as)

let myNumber = new Number();
console.log(typeof myNumber);//"object"
let mySymbol = new Symbol();//TypeError:Symbol is not a constructor

let mySymbol =  Symbol(); 
let myWrappedSymbol = Object(mySymbol);
console.log(myWrappedSymbol); // "object"
  • 使用全局符号注册表

    如果运行时的不同部分需要共享和重用符号实例,那么可以用字符串作为键,在全局符号注册表中创建并重用符号。为此,需要使用Symbol.for()方法。

    let mySymbol = Symbol.for('foo'); //全局注册表中的符号必须用字符串来创建,传进来的参数都会被转换成字符串
    console.log(typeof mySymbol); // symbol
    
    let mySymbol1 = Symbol.for('foo'); // 重用已有符号
    let mySymbol2 = Symbol('foo');
    console.log(mySymbol == mySymbol1);//true
    console.log(mySymbol1 == mySymbol2);//false
    
    let mySymbol4= Symbol.for();
    console.log(mySymbol4); // Symbol(undefined),因为键定义了未初始化,初始值设为undefined
    
    console.log(Symbol.keyFor(mySymbol4));// undefined,可用,因为定义了
    console.log(Symbol.keyFor(mySymbol2)); // undefined,不可用,因为未定义
    console.log(Symbol.keyFor(mySymbol1));// foo
    
    console.log(Symbol.keyFor(123)); // 123 is not a symbol
    
  • 使用符号作为属性

    凡是可以使用字符串或数值作为属性的地方,都可以使用符号。

    //定义符号属性
    let s1 = Symbol('foo'),
        s2 = Symbol('bar'),
        s3 = Symbol('baz'),
        s4 = Symbol('qux'),
        s5 = Symbol('mdn');
    let o = {
        [s1]:'foo val'
    };
    o[s2] = 'bar val';
    Object.defineProperty(o,s3,{value:'baz val'});
    Object.defineProperties(o,{
        [s4]:{value:'qaz'},
        [s5]:{value:'mdn'}
    });
    
    console.log(o);//{ [Symbol(foo)]: 'foo val', [Symbol(bar)]: 'bar val' },后两种方法失效,原因待探索。
    
    //获取属性名、属性描述
    let s1 = Symbol('foo'),
        s2 = Symbol('bar');
    
    let o = {
        [s1]:'foo val',
        [s2]:'bar val',
        baz:'baz val',
        qux:'qux val'
    };
    
    console.log(Object.getOwnPropertySymbols(o));//[ Symbol(foo), Symbol(bar) ],获取符号属性数组
    console.log(Object.getOwnPropertyNames(o));//[ 'baz', 'qux' ],获取常规属性数组
    console.log(Object.getOwnPropertyDescriptors(o));
    /*{
      baz: {
        value: 'baz val',
        writable: true,
        enumerable: true,
        configurable: true
      },
      qux: {
        value: 'qux val',
        writable: true,
        enumerable: true,
        configurable: true
      },
      [Symbol(foo)]: {
        value: 'foo val',
        writable: true,
        enumerable: true,
        configurable: true
      },
      [Symbol(bar)]: {
        value: 'bar val',
        writable: true,
        enumerable: true,
        configurable: true
      }
    }*///获取属性描述器对象,即属性描述
    
    console.log(Reflect.ownKeys(o));//[ 'baz', 'qux', Symbol(foo), Symbol(bar) ],获取所有属性的键
    
    //最好先将符号属性保存在一个变量里,否则获取需要遍历
    let o={
        [Symbol('foo')]:'foo val'
    };
    
    let fooSymbol = Object.getOwnPropertySymbols(o)
        .find((symbol) => symbol.toString().match(/foo/));
    
    console.log(fooSymbol);//Symbol(foo)
    
  • 常用内置符号

    ES 6 引入了一批常用内置符号(well-known symbol),用于暴露语言内部行为,开发者可以直接访问、重写或模拟这些行为。这些内置符号都以Symbol 工厂函数字符串属性的形式存在。

    这些内置函数都是全局函数 Symbol 的普通字符串属性,指向一个的实例。所有内置符号属性都是不可写、不可枚举、不可配置的。

    这部分内容参考MDN,书上的内容暂时搁置

3.4.8 Object 对象

ECMAScript中的对象其实是一组数据和功能的集合。对象通过 new 操作符后跟对象类型名称来创建。开发者可以通过 Object 类型的实例来创建自己的对象,再给对象添加实例和方法。

let o = new Object();

//每个对象都有如下属性和方法
constructor;//用于创建当前对象的函数,String()、Number()、Object()等
hasOwnProperty(propertyName)://用于判断当前对象实例上是否存在给定的属性,如o.hasOwnProperty('name')
isPropertyOf(object)://用于判断当前对象是否为另一个对象的原型
propertyIsEnumerable(propertyName);//判断给定的属性是否可以使用for-in语句枚举,属性名必须为字符串
toLocaleString();//返回对象的字符串表示
toString();//返回对象的字符串表示
valueOf();//返回对象对应的字符串、数值或布尔值表示

3.5 操作符

ECMAScript 中的操作符是独特的,因为它们可以用于各种值,包括字符串、数值、布尔值、甚至是对象。都会尽量转换成数值或字符串进行操作。

3.5.1 一元操作符

只操作一个值的操作符叫一元操作符。

  • 递增/递减操作符:i++在语句执行之后再进行 +1 操作,且此时 i 的值被改变。 ++i 先执行 i = i + 1,再执行当前语句。
  • 一元加和减:在非数值前加上 + 或 - 会将其转换为数值。非数字型的字符串会被转换成 NaN。

3.5.2 位操作符

ECMAScript 中的所有数值都以 IEEE 754 64位格式储存,但位操作并不直接应用到64位表示,而是先把值转换成32位整数,再进行操作,之后再把数值转换为64位。

有符号整数使用32位的前31位表示整数值。第32位表示数值的符号,0表示正,1表示负。这一位称为符号位(sign bit)。符号位的值决定了数值其余部分的格式,正值以真正的二进制格式存储,负值以一种称为二补数(或补码)的二进制编码存储(先由其相反数取反码,再加1)。

特殊值 NaN 和 Infinity 在位操作中会被当做0处理。

  • 按位非:用波浪符(~)表示,返回数值的反码,可知 ~x + 1 = -x ,即~x = -x -1
  • 按位与:用和号(&)表示,有两个操作数,将两个数每一位对齐,两个位都是1时返回1,任何一位是0时返回0。
  • 按位或:用管道符(|)表示,有两个操作数,将两个数每一位对齐,至少一位是1时返回1,两位都是0时返回0。
  • 按位异或:用脱字符(^)表示,有两个操作数,将两个数每一位对齐,只在一位是1时返回1,两位都是1或0,则返回0。
  • 左移:用两个小于号(<<)表示,会按照指定位数将数值的所有位向左移动。数值右端会以0填充这些空位。同时保留符号位。
  • 有符号右移:由两个大于号(>>)表示,会将数值的31位都向右移,同时保留符号位。是左移的逆运算。
  • 无符号右移:用3个大于号表示(>>>),会将数值的所有32位都向右移,不保留符号位,符号位自动补成 0 ,因此正数无影响,负数右移后可能会变成很大的正数。

3.5.3 布尔操作符

  • 逻辑非:用 !表示,可应用给 ECMAScript 中任何类型的值,且始终返回布尔值。逻辑非操作符可以把任意值转换为布尔值。如 !!"blue"返回 true,即将字符串 "blue"转换成了布尔值true

  • 逻辑与:用 && 表示,应用到两个值。如果前一个值是 false,则无论后一个值是否定义,整个表达式都会返回 false ,这称为逻辑与操作符短路的特性。

  • 逻辑或:用 || 表示,应用到两个值。如果前一个值是 true,则第二个操作数不会再被求值了,无论其是否定义。利用这个特性可以避免给变量赋值nullundefined

    let myObject = preferredObject || backupObject;
    

    backupObject变量包含备用的值,如果preferredObjectnull,则backupObject就会赋值给myObject

3.5.4 乘性操作符

  • 乘法操作符:用*表示。Infinity*0 返回NaN。乘法操作和除法操作都可以用极限的思维理解,对于不能确定无穷小阶数的式子都返回NaN

  • 除法操作符:用/表示。

  • 取模操作符:用%表示。

    Infinity % 67; // NaN
    45 % 0; // NaN
    Infinity % Infinity; // NaN
    8 % Infinity ;// 8
    0 % 0 ; // 0
    

3.5.5 指数操作符

ECMAScript 7 新增了指数操作符,用**表示,同样也可以用**=进行指数赋值。

3.5.6 加性操作符

  • 加法操作符:用+表示,用于求两个数的和。-0 + (+0);//+0,数字加字符串会转换成字符串连接,但会优先计算括号内的部分。

  • 减法操作符:用-表示。

    Infinity - Infinity; //NaN
    -Infinity - (-Infinity); //NaN
    Infinity - (-Infinity); //Infinity
    -Infinity - Infinity; //-Infinity
    +0 - (-0);//-0,此处存疑!
    -0 - (-0);//+0
    

3.5.7 关系操作符

关系操作符执行比较两个值的操作,包括小于、大于、小于等于、大于等于,这几个操作都返回布尔值。

  • 如果有任一操作数是布尔值,则将其转换为数值再执行比较。

  • 如果操作数都是字符串,则逐个比较字符串中对应字符的编码(如a的编码是97,字符2的编码是50)

  • ,以第一个字符比较结果作为返回结果。

  • 如果有任一操作数是数值,则将另一个操作数转换为数值,再进行比较。

  • 如果有任一操作数是对象,则依次调用其valueOf()、toString()方法,直到能够比较为止。

  • "23" < "3";//true
    "23" < 3;//false
    "a" < 3;//false
    NaN < 3;//false
    NaN >= 3;//false 任何关系操作符在涉及比较NaN时都会返回false
    

3.7.8 相等操作符

  • 等于和不等于:( == 和 != )

    null == undefined ; // true
    NaN == NaN;//false 相等操作符在涉及比较NaN时都会返回false
    NaN != NaN; // true
    true == 2; //false
    undefined == 0; //false
    null == 0; //false null 和 undefined 不能转换成其他类型的值再比较
    
  • 全等和不全等:(=== 和 !== ),在不转换的前提下进行比较,推荐使用全等和不全等操作符,有助于保持数据类型的完整性。

    "55" == 55; //true
    "55" === 55; // false
    
    null == undefined; // true
    null === undefined; // false 不是相同的数据类型
    

3.7.9 条件操作符

let max = (num1>num2) ? num1 : num2;

3.7.10 赋值操作符

仅是简写语法,使用它们并不会提升性能。

*=
/=
%=
<<=
>>=
>>>=

3.7.11 逗号操作符

let num1 =1,num2 =2,num3=3;
let num = (3,8,9,1); // num的值为1,即最后一个值,这种写法并不常见

3.6 语句

  • if 语句

  • do-while 语句,循环体内代码在退出前至少执行一次

  • while 语句

  • for 语句,无法通过while循环实现的逻辑,同样也无法用for循环实现,两个循环可相互转换

    for(;;){//无穷循环
        dosomething();
    }
    
    let count =10;
    let i =0;
    for(;i<count;){ //for循环转换成了while循环
        console.log(i);
        i++;
    }
    
  • for-in 语句,用于枚举对象中的非符号属性,属性是无序的,因此枚举属性的返回结果的顺序可能因浏览器的不同而有所差异。如果要迭代的变量时null 或undefined,则不执行循环体。

    for(const propName in window){//const 不是必需的,但推荐使用,以保证局部变量不会被修改
        document.write(propName);
    }
    
  • for-of 语句,用于遍历可迭代对象的元素,循环会按照可迭代对象的next()方法产生值的顺序迭代元素。

    for(const e of [2,34,5,2]){
        document.write(e)
    }
    
  • 标签语句,用于给语句加标签,典型应用场景是嵌套循环

    start: for(let i =0; i< count; i++){
        console.log(i);
    }
    
  • break和continue语句:break立即结束当前循环,break后的循环体语句不再执行。continue立刻结束最当前外层循环,并继续执行下一轮的迭代循环。

  • with语句:主要场景是针对一个对象反复操作,将作用域设置为该对象能提供便利,通常不推荐在产品代码中使用,因为影响性能且难于调整其中的代码。

  • switch语句,switch语句可以用于所有数据类型,条件的值也可以是变量或表达式。

    switch ("hello world"){
        case "hello" + "world":
            console.log("Greeting was found");
            break;
        case "goodbye":
            console,log("Closing was found");
            break;
        default:
            console.log("Unexpected message was found")
    }
    
    let num = 76;
    switch (true){
        case num < 0://将条件值与给的值进行比较
            console.log("Less than 0");
            break;
        case  num >=0 && num <= 10:
            console.log("Betwwn 0 and 10");
            break;
        default:
            console.log("More than 10");
    }
    

3.7 函数

  • ECMAScript中的函数不需要指定是否返回值。

  • return语句可以不带返回值。此时,函数会立刻停止执行并返回 undefined。这种操作通常是为了提前终止函数执行,而不是为了返回值。

  • 函数不能以 eval 或 arguments作为名称及参数名。

posted @ 2021-06-16 23:03  unuliha  阅读(88)  评论(0)    收藏  举报