《JavaScript高级程序设计》Chapter03 JavaScript语言基础
阅读原文时间:2023年07月08日阅读:4

目录

var

  • var定义的变量会成为包含它的函数的局部变量,函数退出时即被销毁,即声明的范围为函数作用域

    • 可以通过省略var来定义一个全局变量,但这样做是不被推荐的;严格模式下,这样做会抛出ReferenceError异常
  • var的声明提升(hoist):使用var关键字声明的变量会自动提升到函数作用域的顶部【将所有变量声明拉到函数作用域顶部】

    // 以下二者是等价的,均不会报错,输出undefined
    function foo() {
        console.log(age); // undefined
        var age = 20;
    }
    
    function foo(){
        var age;
        console.log(age); // undefined
        age = 20;
    }
  • 使用var反复多次声明同一个变量是被允许的

    function foo(){
        var age = 12;
        var age = 15;
        var age = 18;
        var age = 21;
        console.log(age); // 21
    }

let

  • let与var作用类似,不同的是let声明的范围为块作用域

    • 块作用域是函数作用域的子集,因此适用于var的作用域限制同样适用于let

      if(true){
      var name = "Leo";
      console.log(name); // Leo
      }

      console.log(name); // Leo

      if(true){
      let name = "Leo";
      console.log(name); // Leo
      }
      console.log(name); // ReferenceError: name is not defined

  • 在同一个块作用域中,let不允许进行冗余声明是(SyntaxError)

    • 声明冗余报错不会因混用let和var受到影响:这两个关键字声明的不是不同类型的变量,只是指出变量在相关作用域如何存在

      var num = 1;
      console.log(num); // 1
      {
      let num = 2;
      console.log(num); // 2
      }

      let num = 1;
      console.log(num);
      {
      var num = 2; // SyntaxError: Identifier 'num' has already been declared
      console.log(num);
      }

  • 暂时性死区:let声明的变量不会在作用域中被提升(hoist),必须先声明后引用

    • let声明之前的执行瞬间称为“暂时性死区”,此阶段引用任何后面才声明的变量都会抛出ReferenceError
  • 全局声明:let在全局作用域中声明的变量不会成为window对象的属性,而var声明的变量会

  • for循环中的let声明:

    • 用var定义迭代变量会渗透到循环体外部,let定义迭代变量的作用域仅局限于for循环块内部

      for(var i=0; i<5; i++){
          // 循环逻辑
      }
      console.log(i); // 5
      --------------------------
      for(let i=0; i<5; i++){
          // 循环逻辑
      }
      console.log(i); // ReferenceError: i is not defined
    • 使用let声明迭代变量,JavaScript引擎在后台为每个迭代循环都声明一个新的迭代变量。

      for(var i=0;i<5;i++){
          setTimeout(()=>console.log(i),0); // 5 5 5 5 5
      }
      ---------------------------------------
      for(let i=0;i<5;i++){
          setTimeout(()=>console.log(i),0); // 0 1 2 3 4
      }

const

  • 与let行为基本相同

    • 不允许重复声明,块声明作用域

    • 区别:声明的同时必须初始化,切不可修改const声明的变量的值(TypeError)

      • 此限制只适用于其指向的变量的引用(const变量引用的为一个对象时,修改const对象内部属性是被允许的)
      • 不能用const声明迭代变量

Undefined

  • 使用var或let声明变量但未初始化时,变量会被赋予初始值undefined

  • 具有值undefined的变量与未定义的变量是有区别的

    • 赋值为undefined的变量是存在的,只是值为undefined;未定义的变量不存在,但对其typeof仍会返回undefined。二者是存在根本差别的

      let message;
      // let age; //确保未被声明过
      console.log(message); // undefined
      console.log(age); // ReferenceError: age is not defined
      console.log(typeof message); // undefined
      console.log(typeof age); // undefined
  • undefined是假值

Null

  • null表示一个空对象指针,typeof null会返回object

  • undefined由null派生而来,ECMA-262将他们定义为表面上相等

    • undefined永远不必显示设置;但当变量需要保存一个对象且此时没有对象可以保存,就应当以null对变量进行填充,保持null的空对象指针语义,从而与undefined区别开来
  • null是假值

Boolean

  • 转型函数:Boolean(param);

    数据类型

    转为true

    转为false

    Boolean

    true

    false

    String

    非空字符串

    空字符串

    Number

    非零数值

    0,NaN

    Object

    任意对象

    null

    Undefined

    \

    undefined

Number

  • 八进制字面量:以0o开头,后续数字均为0~7

    • 后续数字一旦有超过0~7的范围,数字序列就被当做十进制解释
    • 严格模式下,八进制字面量是无效的
  • 十六进制字面量:以0x开头,后续数字为0~9和 a~f, 分别代表0~15

    • a~f大小写均是被允许的
  • 浮点值

    • ECMAScript会想方设法将值转换为整数值:小数点后面没有数字或只有0时,会被转换为整数进行处理

    • 浮点值精度可达17位小数,但算数计算并不精准(IEEE 754数值造成的错误)

      console.log( 0.1+0.2 == 0.3 ); // false  0.1+0.2得到结果为0.30000000000000004,出现舍入错误
  • 数值的范围

    • Number.MIN_VALUE(5e-324) ~ Number.MAX_VALUE(1.797693134862315e+308)
    • 任何超出可表示范围的正负数会被表示为 ±Infinity
      • 可以用函数 isFinite(param) 判断一个数是否在可表示范围内
  • NaN (Not a Number):表示本来要返回数值的操作失败了

    • 任何涉及NaN的操作均返回NaN,NaN不等于包括NaN在内的任何值

    • 利用 isNaN(param) 函数可以判断一个数是否“不是数值”:此函数会先将不是数值的值尝试转换成数值,任何不能转换为数值的值都会使这个函数返回true

      console.log(isNaN(NaN)) // true
      console.log(isNaN(10)) // false
      console.log(isNaN("10")) // false  可转为数值10
      console.log(isNaN("blue")) // true  不可转为数值
      console.log(isNaN(true)) // false  可转为数值1
  • 数值转换函数

    • Number(param):param可为任意数据类型

      数据类型

      转换规则

      Boolean

      true转为1,false转为0

      Number

      直接返回

      Null

      0

      Undefined

      NaN

      String

      只包含数值字符,会被直接转换为数值(包括数字、正负号、小数点)
      空字符串会转换为0
      包含有效的十六进制格式(0x),会被由十六进制串转换为相对应的十进制数值

      Object

      调用对象的valueOf( )方法,再按照上述规则转换返回值;若转换结果为NaN,则调用toString( )方法,按照转换字符串的规则转换

    • parseInt(param1,param2):param1为字符串,param2为进制

      • 直接忽略最前面的空格,从第一个非空格字符串开始转换

      • 第一个非空格字符不是数值字符、正负号,会立即返回NaN(意味着空字符串会返回NaN

      • 转换直到字符串尾或最后一个非数值字符

        console.log(parseInt("1234hello")); // 1234 hello被忽略
        console.log(parseInt("12.34")) // 12 .不是有效的整数字符
        console.log(parseInt("")); // NaN
        console.log(parseInt("0xaf")); // 175 被解释为十六进制
        console.log(parseInt("af",16)); // 175 被解释为十六进制

    • parseFloat(param):与parseInt原理类似

      console.log(parseFloat("1234hello")); // 1234  hello被忽略
      console.log(parseFloat("12.34.56")); // 12.34
      console.log(parseFloat("0123.45")); // 123.45
      console.log(parseFloat("3.125e7")); // 31250000

String

  • 表示零或多个16位Unicode字符序列,通过字符串的length属性可以获取其长度

  • ECMAScript中的字符串时不可变的,一旦修改某个变量中的字符串值,必须先销毁原字符串再将新的值存入该变量中

  • 向字符串转换

    • 几乎所有值都有方法 toString( ),返回当前值的字符串等价物

      • Number,Boolean,Object,String均有toString( )方法,null与undefined没有toString( )方法

      • 对Number调用toString( )方法时,可以传一个底数参数,表示以什么底数输出数值的字符串表示

    • String(param)转型函数

      • param存在toString()方法则直接调用
      • 值为null则返回"null",值为undefined则返回"undefined"
  • 模板字面量:使用反引号,会保留字面量内的换行字符,以跨行定义字符串

  • 字符串插值:模板字面量常用以支持字符串插值,即在一个连续定义中插入一个或多个值

    • 字符串插值通过在 ${...} 中使用一个JavaScript表达式实现,插入的值会使用toString()强制转为字符串

      let value = 19;
      let str = `<div>
          <a href="#">
              <span>Leo is ${value} years old</span>
          </a>
      </div>`;
      console.log(str);
  • 标签函数:自定义插值行为,前缀到模板字面量来应用自定义行为

    • 第一个参数接收原始字符串被插值分割后形成的数组
    • 由于插值表达式数量可变,应使用剩余操作符 ... 将它们收集到一个数组中

    let value = 19;
    let exp = 'second';
    function tagFunc(strings, …exp){
    console.log(strings);
    console.log(exp);
    }
    tagFunc${value} to the ${exp} power is ${value*value};
    // [ '', ' to the ', ' power is ', '' ]
    // [ 19, 'second', 361 ]

Symbol

ECMAScript6新增数据类型,确保对象属性使用唯一标识符,不会发生属性冲突的危险

  • Symbol(string):string为对符号的描述参数,与此符号的定义与标识完全无关

  • 不能与new关键字一起作为构造函数使用,用以避免创建符号包装对象

    let sym1 = Symbol('foo');
    let sym2 = Symbol('foo');
    console.log(sym1 == sym2); // false
  • 全局符号注册表:运行时不同部分需要共享和重用符号实例时,以一个字符串作为键在全局符号注册表创建并重用符号

    • Symbol.for(string):使用某字符串调用时,检查全局运行时注册表,不存在相应符号则会创建一个新的符号实例,已存在则直接重用

    • 即使采用相同的符号描述,全局注册表中定义的符号与Symbol()定义的符号也不等同

    • 可以用Symbol.keyFor(param)查询全局注册表,param为符号实例,返回对应的描述字符串

      let sym1 = Symbol.for('foo'); // 创建一个新的符号实例
      let sym2 = Symbol.for('foo'); // 重用已有符号实例
      console.log(sym1 === sym2); // true
      
      let sym3 = Symbol('foo');
      console.log(sym1 === sym3); // false
      
      console.log(Symbol.keyFor(sym1)); // foo
      console.log(Symbol.keyFor(sym3)); // undefined
  • 符号添加为对象属性

    let s1 = Symbol('foo'),
        s2 = Symbol('bar'),
        s3 = Symbol('baz'),
        s4 = Symbol('qux');
    let obj = { [s1]:'foo val' }; // o[s1] = 'foo val';
    Object.defineProperty(obj, s2,{ value: 'bar val', enumerable: true});
    Object.defineProperties(obj, {
        [s3] : {value:'baz val', enumerable: true},
        [s4] : {value:'qux val', enumerable: true},
    });
    console.log(obj);
    //{
    //  [Symbol(foo)]: 'foo val',
    //  [Symbol(bar)]: 'bar val',
    //  [Symbol(baz)]: 'baz val',
    //  [Symbol(qux)]: 'qux val'
    //}
  • 获取对象的符号属性

    let s1 = Symbol('foo'),
        s2 = Symbol('bar');
    let obj = {
        [s1]: 'foo val',
        [s2]: 'bar val',
        baz: 'baz val',
        qux: 'qux val'
    };
    console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(foo), Symbol(bar) ]
    console.log(Object.getOwnPropertyNames(obj)); // [ 'baz', 'qux' ]
    console.log(Object.getOwnPropertyDescriptors(obj));
    //{
    //  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(obj)); // [ 'baz', 'qux', Symbol(foo), Symbol(bar) ]
  • To be continued ……

Object

ECMAScript中的Object是所有对象的基类,任何对象都有这些属性与方法。每个Object实例都有如下属性与方法:

  • constructor:创建当前对象的函数
  • hasOwnProperty(propertyName):判断当前对象是否含有给定属性(String)
  • propertyIsEnumerable(propertyName):判断当前属性是否可用for-in语句枚举
  • toString():返回对象的字符串表示
  • ……

一元操作符

  • 递增/递减:++/--

    • 前缀时,先改变变量的值再对表达式求值;后缀时,先对表达式求值再改变变量的值

    • 不限于整数,也可作用于以下类型的值

      数值类型

      规则

      String

      对有效数值形式则转为数值在改变,变量类型变为Number
      对非有效数值形式,变量值变为NaN,变量类型变为Number

      Boolean

      将false转为0,将true转为1,再进行递增递减操作,变量类型变为Number

      float

      直接+1或-1(注意:浮点数的加减可能不精确,出现舍入错误)

      Object

      先调用对象的valueOf()方法,取到的值可操作则直接应用上述规则
      如果为NaN,则调用toString()并再次应用其他规则
      变量类型从Object变为Number

  • 一元加减:+/-

    • 主要用于基本的算数操作,但也可以用于数据类型转换
    • 对于数值,+/- 仅做正负的改变;对于非数值,会执行与Number()函数相同的改变,再根据 +/- 改变正负

位操作符

  • 按位 与&|~ 异或^
  • 左移<<:按指定位数将数字的所有位左移(右侧补0,左侧符号位保留)
  • 右移
    • 有符号右移>>:将所有的32位右移指定位,符号位保留(空位以符号位补充)
    • 无符号右移>>>:将所有的32位右移指定位,空位以0补充(对于正数无差别,对于负数有很大差异)

布尔操作符

  • 逻辑非!:现将操作数统一转为Boolean,再取反

    • 可以用于将任意值转为Boolean型,与Boolean()转型函数等效:!!operand
  • 逻辑与&&:短路操作符,当第一个操作数为false时就不会对第二个操作数求值

    • 可用于任何类型操作数,不一定会返回Boolean值

      • 第一个操作数为Object,则返回第二个操作数;第二个操作数为Object,仅当第一个操作数求值为true才返回该Object
      • 两个操作数均为Object,则返回第二个操作数
      • 有一个操作数为NaN/undefined/null,直接返回NaN/undefined/null
  • 逻辑或||:短路操作符,当第一个操作数为true时就不会对第二个操作数求值

    • 可用于任何类型操作数,不一定会返回Boolean值

      • 第一个操作数为Object,则返回第一个操作数;第一个操作数求值为false,则返回第二个操作数

      • 两个操作数均为Object,则返回第一个操作数

      • 两个操作数均为NaN/undefined/null,则返回NaN/undefined/null

    • 可以避免给变量赋值null或undefined

      let obj = preferredObj || backupObj;
      • preferredObj为首选值,其不为null则会被赋给obj;backupObj为备用值,preferredObj为null时,其就会被赋给obj

乘性操作符

  • */ 取模%

    • 注意对0,Infinity,NaN的处理
    • 运算时,会对非数值的操作数进行类型转换

指数操作符

  • Math.pow(base,power):用于计算base的power次幂

    • ECMAScript 7新增指数操作符 ** ,与Math.pow函数等效

    • 指数操作符同样支持复合赋值操作:**=

      console.log(Math.pow(11,2)); // 121
      console.log(11**2); // 121
      console.log(121**0.5); // 11

加性操作符

  • +:计算数值时,需考虑Infinity,NaN,±0的情况

    • 对两个字符串操作时,将第二个字符串拼到第一个后面;只有一个为字符串时,将另一个转为字符串后再拼在一起

    • 任一操作数为Object时,会调用对象的valueOf()方法,由返回值参与加法运算;没有valueOf则先调用toString,得到的字符串再转为数值参与计算

      let obj1 = {
          valueOf : function (){
              return -1;
          },
      }, obj2 = {
          valueOf : function (){
              return "-2";
          }
      }, obj3 = {
          valueOf : function (){
              return -2;
          }
      };
      console.log(obj1 + obj2); // -1-2
      console.log(obj1 + obj3); // -3
  • -:计算数值时,需考虑Infinity,NaN,±0的情况

    • 对字符串、布尔值、null、undefined,会调用Number()转为数值再计算
    • 其他情况规则与加法相同

关系操作符

  • 包括:> < >= <=

    • 操作数都是数值,执行数值比较
    • 操作数都是字符串,则逐个比较字符串中对应字符的编码
    • 有一个操作数是数值,则将另一个操作数转为数值,再做数值比较
    • 任一操作数为Object,则调用其valueOf方法,对结果进行比较;若没有valueOf方法,则调用toString方法,对结果进行比较
    • 对布尔值,将其转为数值再比较
    • 涉及NaN的所有比较都会返回false

相等操作符

  • 等于==和不等于!=:对比较的两操作数先进行强制类型转换,再比较是否相等

    • 任一操作数为Boolean,将其转为数值再比较是否相等
    • String与Number比较,尝试将String转为Number,再比较是否相等
    • Object和非Object比较,调用对象的valueOf方法获取其原始值,再比较是否相等
    • null == undefined,且它们不能转换为其他类型的值进行比较
    • 只要涉及NaN,立即返回false(NaN == NaN也返回false)
    • 两个Object比较,仅当两操作数指向同一对象时返回true
  • 全等===和不全等!==:比较时不作任何强制类型转换

    • null === undefined 返回false,因为他们是不同类型的值

条件操作符

  • variable = boolean_expression ? true_value : false_value ;

逗号操作符

  • 用于在一条语句中执行多个操作

    let num1 = 1, num2 = 2, num3 = 3;
  • 赋值时用逗号操作符分隔值,最终会返回表达式中最后一个值

    let num = (5,4,3,2,1,0); // num = 0

for-in

  • 用于枚举对象中的非符号键属性

    • 若迭代的变量是null或undefined,则不执行循环体

      for (const propName in window){
          console.log(propName);
      }

for-of

  • 用于遍历可迭代对象的元素

    • 会按照可迭代对象的next( )方法产生值的顺序迭代元素

    • 尝试迭代的变量不支持迭代时,语句会抛出错误

      let arr = [1,2,3,4];
      for (const num of arr){
          console.log(num);
      }

break,continue,label

  • break语句用于立即退出当前循环,强制执行当前循环后的下一条语句

  • continue语句也立即退出当前循环,但会再次从循环顶部开始执行

  • label语句用于给语句加标签,可以通过break和continue语句引用(常用于嵌套循环):强制执行或强制退出标签所在循环

    let num = 0;
    outermost: for (let i=0; i<10; i++){
        for (let j=0; j<10; j++){
            if (i===5 && j===5){
                break outermost;
            }
            num ++;
        }
    }
    console.log(num); // 55
    -------------------------------------
    let num = 0;
    outermost: for (let i=0; i<10; i++){
        for (let j=0; j<10; j++){
            if (i===5 && j===5){
                continue outermost;
            }
            num ++;
        }
    }
    console.log(num); // 95

with

  • 用于将代码作用域设置为特定的对象,主要用于针对同一对象的反复操作

    • 严格模式下,不允许with语句的使用

      let obj = {
          value : 114514,
          str : 'fuck',
          name : 'obj'
      };
      
      with (obj) {
          console.log(value);
          console.log(str);
          console.log(name);
      }
      //114514
      //fuck
      //obj 

switch

  • 在每个case条件分支后,最好都加上break语句,否则代码会在匹配完当前条件后继续匹配下一条件

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

    let str = 'hello world';
    switch (str) {
        case 'hello'+' world':
            console.log('Greeting was found!'); //
            break;
        case 'goodbye':
            console.log('Closing was found');
            break;
        default:
            console.log('Unexpected message was found');
    }
  • 在比较每个条件分支的值时使用'===',不会强制转换数据类型

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章