Skip to content

Latest commit

 

History

History
593 lines (440 loc) · 16.1 KB

3.JS--语言基础.md

File metadata and controls

593 lines (440 loc) · 16.1 KB

一、变量

  • var

    • 声明作用域

      function test() {
        var message = 'hi' // 局部变量
        console.log(message, 222); // 'hi' 222
      }
      // console.log(message); // 出错
      test();
      console.log(message); // 出错

      在函数内部用var定义的变量,在函数调用后随即被销毁。因此函数调用前后都会报错。

      function test() {
        message = 'hi' // 局部变量
        console.log(message, 222); // 'hi' 222
      }
      // console.log(message); // 出错
      test();
      console.log(message); // 'hi'

      去掉var操作符,在函数执行后,message就变成了全局变量,并且可以在外部访问。

    • var声明提升

      使用var可以将关键字声明的变量会自动提升到函数作用域顶部,所以第一个打印不会报错。

      function foo() {
        console.log(age, 111);
        var age = 26
        var age = 36
        console.log(age, 222);
      }
      foo()
      // undefined 111
      // 36 222
  • Let

    let和war最明显的区别:let声明的范围是块级作用域,而var声明的范围是函数作用域。

    • 暂时性死区

      let与var的另一个重要的区别:let不会在作用域中被提升。

      // name 会被提升
      console.log(name); // 'Matt'
      var name = 'Matt';
      
      // age 不会被提升
      console.log(age); // Reference:age没有定义
      let age = 26;

      在let声明之前的执行瞬间被称为暂时性死区(temporal dead zone),在此阶段引用后面才声明的变量都会抛出ReferenceError。

    • 全局声明

      与var关键字不同,使用let在全局作用域中声明的变量不会成为window对象的属性(var声明的变量则会)。

      var name = 'Matt';
      console.log(window.name); // 'Matt'
      
      let age = 26;
      console.log(window.age); // undefined
    • 条件声明

      不能使用let进行条件式声明。

    • for循环中的let声明

      for (var i = 0; i < 5; ++i) {
      	//  循环逻辑
      }
      console.log(i); // 5
      
      for (let j = 0; j < 5; ++j) {
      	// 循环逻辑
      }
      console.log(j); //ReferenceError:没有定义

      var 会把for循环中迭代的变量渗透到循环外部,let的出现解决了这个问题。

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

      使用let变量的时候,JS引擎会在后台为每个迭代循环声明一个新的迭代变量。var的话,迭代变量保存的是导致循环退出的值。因此最后输出的都是5。

  • const

    const的行为与let基本相同,唯一一个重要的区别是用它声明变量是必须同时初始化变量,且const声明的变量不能更改(更改会导致运行时错误)。

    • const给常量赋值

      const age = 26;
      age = 16; //TypeError:	
    • const也不允许重复声明

      const name = 'jack';
      const name = 'lucy'; //SyntaxError
    • const声明的作用域也是块

      const name = "matt"
      if (true) {
        const name = "jack"
        console.log(name); // 'jack'
      }
      console.log(name); //'matt'
    • const声明对象,这个对象里的属性也可以修改

      const person = {}
      person.name = 'matt'
      person.age = '26'
      person.name = 'jack'
      person.age = '16'
      console.log(person.age, person.name); // 'jack 16'
    • js引擎会为for循环中的let声明分别创建独立的变量实例,虽然const变量和let变量相似,但不能用const来声明迭代变量。

    • const在for-in和for-of循环使用

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

二、数据结构

es有七种简单数据类型(原始类型):Undefined、Null、Boolean、Number、String、Symbol和BigInt。

  • typeof操作符

    • typeof null返回的是'object'

      console.log(typeof null); // 'object'
      console.log(typeof (null)); // 'object'
    • typeof可以用来区分函数和其他对象

      function fn() {}
      console.log(typeof fn); // 'function'
      console.log(typeof new Date()); // 'object'
      console.log(typeof new RegExp()); // 'object'
      console.log(typeof {}); // 'object'
  • Undefined类型

    未初始化的变量会被自动赋予undefined的值,未声明的变量typeof时也是undefined。因此,建议声明变量的时候同时进行初始化

    let name;
    console.log(name); // 未初始化,值为undefined
    console.log(typeof name); // 初始化了的变量,typeof类型为undefined
    console.log(typeof age); // 未声明的变量,typeof类型为undefined
    console.log(age); //报错
  • Null类型

    逻辑上讲,null值表示一个空对象指针,因此typeof null会返回'object'。

    undefined的值是由null值派生而来的,因此他们表面上相等

    console.log(undefined == null); // true
    console.log(undefined === null); // false
  • Boolean类型

    有两个字面值:true和false

    不同类型和Boolean的转换。

    // Boolean
    console.log(Boolean(true)); // true
    console.log(Boolean(false)); // false
    
    // String
    console.log(Boolean('12')); // 非空字符串 true
    console.log(Boolean('')); // false
    
    // Number
    console.log(Boolean(12)); // 非零数值包括无穷大 true
    console.log(Boolean(-Infinity)); // true
    console.log(Boolean(Infinity)); // true
    console.log(Boolean(NaN)); // false
    console.log(Boolean(0)); // false
    
    // Object
    console.log(Boolean({})); // true
    console.log(Boolean({
    	a: 1
    })); // true
    console.log(Boolean(null)); // false
    
    // Undefined
    console.log(Boolean(undefined)); // false
  • Number类型

    有进制,也有+0和-0;

    • 浮点值

      必须要有小数点,且小数点后必须至少有一个数字。

      浮点值的精确度最高可达17位小数。永远不要测试某个特定的浮点值

      console.log(0.1 + 0.2); // 0.30000000000000004
    • 值的范围

      最小的数值可以保存在Number.MIN_VALUE,最大的数值可以保存在Number.MAX_VALUE;任何无法表示的负数以-Infinity表示,任何无法表示的正数以Infinity表示。

      使用Number.NEGATIVE_INFINITY和Number.POSITIVE_INFINITY也可以获取正、负Infinity

    • NaN

      意思是“不是数值”,用于表示本来要返回数值的操作失败了(而不是抛出错误)。

      0,+0,-0相除会返回NaN

      console.log(0 / 0); // NaN
      console.log(-0 / +0); // NaN

      如果分子是非0值,分母是有符号的0或者无符号的0,会返回Infinity和-Infinity。

      console.log(5 / 0); // Infinity
      console.log(6 / -0); // -Infinity

      任何涉及NaN的操作始终返回NaN

      console.log(NaN + 10); // NaN
      console.log(NaN + NaN); // NaN
      console.log(NaN / 10); // NaN
      console.log(NaN / 0); // NaN

      NaN不等于包括NaN在内的任何值

      console.log(NaN == NaN); // false
      console.log(NaN == 0); // false
      console.log(NaN == 1); // false

      isNaN函数,判断这个参数是否“不是数值”,可以转换为数值的返回false,否则true。

      console.log(isNaN(NaN)); // true
      console.log(isNaN(10)); // false
      console.log(isNaN('10')); // false
      console.log(isNaN('BLUE')); // true
      console.log(isNaN(true)); // false
      console.log(isNaN(false)); // false
    • 数值转换

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

      // Boolean => 0/1
      console.log(Number(true)); // 1
      console.log(Number(false)); // 0
      
      // Number => 直接返回
      console.log(Number(12)); // 12
      
      // Null => 0
      console.log(Number(null)); // 0
      
      // Undefined => NaN
      console.log(Number(undefined)); // NaN
      
      // 数值型String
      console.log(Number('123')); // 123
      console.log(Number('011')); // 11
      console.log(Number('e11')); // NaN
      console.log(Number('abc11')); // NaN
      
      // 浮点值格式的String
      console.log(Number('1.1')); // 1.1
      console.log(Number('0.1')); // 0.1
      
      // 十六进制的String
      console.log(Number('0xf')); // 15
      
      // 空字符串
      console.log(Number('')); // 0
      
      // 其他字符串 => NaN
      console.log(Number('abc')); //NaN

      考虑到Number转字符串有点反常规,通常在需要得到整数时,可以优先使用parseInt()函数。

      console.log(parseInt(' 123blue ')); // 123
      console.log(parseInt(' 123blue  44')); // 123
      console.log(parseInt('')); // NaN
      console.log(parseInt('0xA')); // 10
      console.log(parseInt('22.5')); // 22
      console.log(parseInt('70')); // 70
      console.log(parseInt('0xf')); // 15

      parseInt可以传两个参数,第二个参数可以用于指定底数(进制数)。

      console.log(parseInt('10', 2)); // 2
      console.log(parseInt('10', 8)); // 8
      console.log(parseInt('10', 10)); // 10
      console.log(parseInt('10', 16)); // 16

      parseFloat也是解析到字符串末尾,或者解析到一个无效浮点数值字符为止。

      console.log(parseFloat(' 1234blue ')); // 1234
      console.log(parseFloat('0xA')); // 0 十六进制始终返回0
      console.log(parseFloat('22.5')); // 22.5 只解析十进制不能指定指数
      console.log(parseFloat('22.34.5')); // 22.34
      console.log(parseFloat('0908.5')); // 908.5 会忽略字符串开头的0
      console.log(parseFloat('3.125e7')); // 31250000
      console.log(parseFloat('3.0')); // 3 如果小数点后没有小数点或者只有零返回整数
      console.log(parseFloat('3.00')); // 3 
  • String类型

    • 转换为字符串

      几乎所有的值都有toString()方法;null和undefined值没有toString()方法;

      const age = 11
      console.log(age.toString()); // '11'
      const found = true
      console.log(found.toString()); // 'true'

      toString()接收进制基数。

      const age = 10
      console.log(age.toString()); // '10'
      console.log(age.toString(2)); // '1010'
      console.log(age.toString(8)); // '12'
      console.log(age.toString(10)); // '10'
      console.log(age.toString(16)); // 'a'

      String()会把始终返回表示相应类型值的字符串

       let value1 = 10
       let value2 = true
       let value3 = null
       let value4 = undefined
       console.log(String(value1)); // '10'
       console.log(String(value2)); // 'true'
       console.log(String(value3)); // 'null'
       console.log(String(value4)); // 'undefined'
    • 模板字面量

      可以跨行定义字符串

      let str = `123
      234`

      可以插值

      let age = 25
      let str = `age:${age}`
  • Symbol类型

    符号是原始值,且符号实例是唯一、不可变的 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。符号就是用来创建唯一记号,进而用做非字符串形式的对象属性。

    • 基本用法

      let sym = Symbol()
      console.log(typeof sym); // symbol

      传入的字符参数与符号定义或标识完全无关。

      let sym = Symbol()
      let sym1 = Symbol()
      console.log(sym == sym1); // false
      
      let symFoo = Symbol('foo')
      let symFoo1 = Symbol('foo')
      console.log(symFoo == symFoo1); // false

      Symbol()函数不能与new关键字一起作为构造函数使用。这样做是为了避免创建符号包装对象。

    • 使用全局符号注册表

      Symbol.for()对每个字符串键都执行幂等操作。第一次使用某个字符串调用时,他会检查全局运行时注册表,发现不存在对应对应的符号,于是就会生成一个新的符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串相应的符号,然后就会返回该符号实例。

      let oneSym = Symbol.for('foo')
      let oneSymSame = Symbol.for('foo')
      console.log(oneSym === oneSymSame); // true

      在全局注册表中定义的符号跟使用Symbol()定义的符号并不等同。

      let oneSym = Symbol('foo')
      let oneSymSame = Symbol.for('foo')
      console.log(oneSym == oneSymSame); // false

      创建全局符号

      let s = Symbol.for('foo');
      console.log(Symbol.keyFor(s)); // 'foo'

      创建普通符号

      let s = Symbol('bar');
      console.log(Symbol.keyFor(s)); // undefined

      Symbol.keyFor传空会报错。

    • 使用符号作为属性

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

      let s1 = Symbol('foo'),
          s2 = Symbol('bar'),
          s3 = Symbol('baz'),
          s4 = Symbol('qux');
      let o = {
      	[s1]: 'foo val'
      }
      // 或 o[s1] = 'foo val'
      console.log(o); // {Symbol(foo): 'foo val'}
      
      Object.defineProperty(o, s2, {
      	value: 'bar val'
      })
      console.log(o); // {Symbol(foo): 'foo val', Symbol(bar): 'bar val'}
      Object.defineProperties(o, {
        [s3]: {
        	value: 'baz val'
        },
        [s4]: {
        	value: 'qux val'
        }
      })
      console.log(
      o); //{Symbol(foo): 'foo val', Symbol(bar): 'bar val', Symbol(baz): 'baz val', Symbol(qux): 'qux val'}

      Object.getOwnPropertyNames()返回对象实例的常规属性数组;

      Object.getOwnPropertySymbols()返回对象实例的符号属性数组;

      Object.getOwnPropertyDescriptors返回同时包含常规和符号属性描述的对象;

      Reflect.ownKeys()会返回两种类型的键。

      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: {…}, qux: {…}, Symbol(foo): {…}, Symbol(bar): {…}}
      console.log(Reflect.ownKeys(o)); // ['baz', 'qux', Symbol(foo), Symbol(bar)]
  • Object类型

    ECMAScript中的对象其实就是一组数据和功能的集合。对象通过new操作符后跟对象的类型的名称来创建。

    let o = new Object()

    每个Object实例都有如下属性和方法:

    • constructor:用于创建当前对象的函数。这个属性的值就是Object()函数。
    • hasOwnProperty(propertyName):用于判断当前对象是否存在给定的属性。也能检查符号(Symbol)。
    • isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。
    • propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用for-in语句枚举。
    • toLocaleString():返回对象的字符串表示。
    • toString():返回对象的字符串表示。
    • valueOf():返回对应字符串、数值、布尔值表示。通常与toString()的返回值相同。