zoukankan      html  css  js  c++  java
  • JavaScript基础巩固系列——入门篇+数据类型

    全手打原创,转载请标明出处:https://www.cnblogs.com/dreamsqin/p/13686263.html, 多谢,=。=~(如果对你有帮助的话请帮我点个赞啦)

    重新学习JavaScript是因为当年转前端有点儿赶鸭子上架的意味,我一直在反思我的知识点总是很零散,不能在脑海中形成一个完整的体系,所以这次想通过再次学习将知识点都串联起来,结合日常开发的项目,达到温故而知新的效果。与此同时,总结一下我认为很重要但又被我遗漏的知识点~

    背景知识

    JavaScript既是一种轻量级的脚本语言,又是一种嵌入式语言。

    • 脚本语言(script language):不具备开发操作系统的能力,只用来编写控制大型应用程序(例如浏览器)的脚本。
    • 嵌入式语言(embedded):通过嵌入大型应用程序调用宿主环境提供的底层API(例如浏览器为JavaScript提供浏览器控制类、DOM类、Web类API;Node为JavaScript提供文件操作、网络通信等API;)实现本身核心语法不支持的复杂功能。

    JavaScript历史

    • 为什么诞生:Navigator 浏览器需要一种可以嵌入网页的脚本语言,用来控制浏览器行为,因为当时网速很慢而且上网费很贵,有些操作不宜在服务器端完成。
    • 需求:不需要太强的功能,语法较简单,容易学习和部署。

    JavaScript的编程风格是函数式编程和面向对象编程的一种混合体。

    • 语法来源
      • 基本语法:借鉴 C 语言和 Java 语言。
      • 数据结构:借鉴 Java 语言,包括将值分成原始值和对象两大类。
      • 函数的用法:借鉴 Scheme 语言和 Awk 语言,将函数当作第一等公民,并引入闭包。
      • 原型继承模型:借鉴 Self 语言(Smalltalk 的一种变种)。
      • 正则表达式:借鉴 Perl 语言。
      • 字符串和数组处理:借鉴 Python 语言。

    ECMAScript历史

    ECMAScript 和 JavaScript 的关系是,前者是后者的规范,后者是前者的一种实现。

    • 为什么诞生:微软开发了JScript并内置于IE3.0浏览器中,Netscape 面临丧失浏览器脚本语言的主导权局面,最终决定将 JavaScript 提交给国际标准化组织 ECMA(European Computer Manufacturers Association),希望 JavaScript 能够成为国际标准,以此抵抗微软。
    • 需求:规定浏览器脚本语言的标准。

    基本语法

    • 变量->变量提升:JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有变量的声明语句,都会被提升到代码的头部,对应的也有函数名提升(前提是使用function命令声明,如果采用赋值语句定义就会报错)。
    // 原代码(如果没有变量提升会报错:a is not defined)
    console.log(a);
    var a = 1;
    
    // 实际运行代码
    var a;
    console.log(a);
    a = 1;
    
    // 运行不会报错
    f();
    function f() {}
    
    • 标识符:用来识别各种值(变量、函数)的合法名称,只能以任意 Unicode 字母、美元符号$或下划线_开头,只能包含 Unicode 字母、美元符号、下划线及数字。
      • 中文是合法的标识符,可以用作变量名。
      • JavaScript保留字:arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。
    • 注释:JavaScript可以兼容HTML代码注释,所以<!---->也被视为合法的单行注释,但-->只有在行首才会被当作单行注释,否则为正常运算,例如:
    function countdown(n) {
      while (n --> 0) console.log(n);    // 被作为n-- > 0执行
    }
    countdown(3)
    // 2
    // 1
    // 0
    
    • 区块:使用大括号,将多个相关的语句组合在一起。
      • 区块对于var命令来说不构成单独的作用域,与不使用区块的情况没有任何区别。例如:
      {
        var a = 1;
      }
      a // 1,在区块外部变量依然有效
      
    • 条件语句
      • 终于明白条件判断大家为什么要这么写了if (2 = x),因为常量写在运算符的左边,一旦不小心将相等运算符写成赋值运算符,就会报错,因为常量不能被赋值。
      • 在没有标明区块(大括号)时,else代码块总是与离自己最近的那个if语句配对。
      • switch语句后面的表达式与case语句后面的表示式比较时,采用的是严格相等运算符(===),即不会发生类型转换。
    • 循环语句
      • break语句用于跳出代码块或循环。
      • continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环。
      • 标签:JavaScript 语言允许语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,例如:
      top:
        for (var i = 0; i < 3; i++){
          for (var j = 0; j < 3; j++){
            if (i === 1 && j === 1) break top;
            console.log('i=' + i + ', j=' + j);
          }
        }
      // i=0, j=0
      // i=0, j=1
      // i=0, j=2
      // i=1, j=0
      

    数据类型

    • 整数和浮点数:
      • JavaScript内部,所有数字都是以64位浮点数形式储存。
      1 === 1.0 // true
      
      • 由于浮点数不是精确的值,所以涉及小数的比较和运算要特别小心。
      0.1 + 0.2 === 0.3 // false
      0.3 / 0.1 // 2.9999999999999996
      (0.3 - 0.2) === (0.2 - 0.1) // false
      
    • 数值精度:精度最多只能到53个二进制位,绝对值小于2的53次方的整数,即负的2的53次方到 2的53次方,都可以精确表示。
    Math.pow(2, 53) // 9007199254740992(简单的法则就是JavaScript对15位的十进制数都可以精确处理)
    
    • 数值范围:JavaScript 提供Number对象的MAX_VALUEMIN_VALUE属性,返回可以表示的最大值和最小值,负向溢出时返回0,正向溢出时返回Infinity。
    Number.MAX_VALUE // 1.7976931348623157e+308
    Number.MIN_VALUE // 5e-324
    
    • 数值表示法:0b11(二进制)、0o377(八进制)、35(十进制)、0xFF(十六进制)、123e3(科学计数法)
      • 小数点前的数字多于21位、小数点后的零多于5个时JavaScript自动将数值转为科学计数法表示。
      • 默认情况下,JavaScript 内部会自动将八进制、十六进制、二进制转为十进制。
    • 特殊数值:
      • 几乎所有场合,正零负零都会被当作正常的0,是等价的,唯一有区别的场合是+0或-0当作分母,返回的值是不相等的。
      (1 / +0) === (1 / -0) // false(除以正零得到+Infinity,除以负零得到-Infinity)
      
      • NaN表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合、还有一些数学函数的运算结果,数据类型属于Number,不等于任何值(包括它本身),布尔运算时被当作false,和任何数运算得到的都是NaN。
      typeof NaN // 'number'
      NaN === NaN // false 
      Boolean(NaN) // false
      NaN + 32 // NaN
      
      • Infinity表示“无穷”,用来表示两种场景,一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,Infinity有正负之分,大于一切数值(除了NaN),四则运算符合无穷的数学计算规则,与null计算时null会转成0,与undefined计算返回的都是NaN。
      Infinity === -Infinity // false
      Infinity > 1000 // true
      Infinity > NaN // false
      0 * Infinity // NaN(特殊)
      Infinity - Infinity // NaN(特殊)
      Infinity / Infinity // NaN(特殊)
      0 / Infinity // 0
      Infinity / 0 // Infinity
      
    • 全局方法:
      • parseInt():用于将字符串转为整数,头部有空格会自动去除,转换时是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分,如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外)返回NaN,可以接受第二个参数(2到36之间,超出返回NaN,为0、undefined、null则忽略)表示被解析的值的进制并返回该值对应的十进制数。
      parseInt('15e2') // 15
      parseInt('.3') // NaN
      parseInt('+1') // 1
      parseInt('') // NaN
      parseInt('1000', 2) // 8
      parseInt('10', null) // 10
      
      • parseFloat():用于将一个字符串转为浮点数,需要与Number()函数区分。
      parseFloat('') // NaN
      Number('') // 0
      parseFloat('123.45#') // 123.45
      Number('123.45#') // NaN
      
      • isNaN():用来判断一个值是否为NaN,只对数值有效,如果传入其他值,会被先转成数值(所以重点关注里面传入的值是否可以被Number转为数值)。
      isNaN('Hello') // true
      // 相当于
      isNaN(Number('Hello')) // true
      //判断NaN更可靠的方法是,利用NaN是唯一一个不等于自身的这个特点
      function myIsNaN(value) {
        return value !== value;
      }
      
      • isFinite():返回一个布尔值,表示某个值是否为正常的数值,除了Infinity、-Infinity、NaN和undefined这几个值会返回false,isFinite对于其他的数值都会返回true。
    • 字符串:
      • 字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始),但无法改变字符串之中的单个字符。
      'hello'[1] // "e"
      
      var s = 'hello';
      delete s[0];
      s // "hello"
      s[1] = 'a';
      s // "hello"
      
      • 对于码点在U+10000到U+10FFFF之间的字符,JavaScript 总是认为它们是两个字符(length属性为2),所以JavaScript 返回的字符串长度可能是不正确的。
      • Base64转码:就是一种编码方法,可以将任意值转成 0~9、A~Z、a-z、+和/这64个字符组成的可打印字符,应用场景一个是将不可打印符号转成可打印字符(例如ASCII的0~31),或者以文本格式传递二进制数据,有两个原生方法:btoa()任意值转为 Base64 编码、atob()Base64 编码转为原来的值,但不适用于非ASCII的字符(需要先通过encodeURIComponent进行转码)。
      var string = 'Hello World!';
      btoa(string) // "SGVsbG8gV29ybGQh"
      atob('SGVsbG8gV29ybGQh') // "Hello World!"
      
      function b64Encode(str) {
        return btoa(encodeURIComponent(str));
      }
      function b64Decode(str) {
        return decodeURIComponent(atob(str));
      }
      b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
      b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"
      
    • 对象:
      • 对于不符合标识名条件的键名必须加上引号,例如第一个字符为数字,或者含有空格或运算符。
      var obj = {
       '1p': 'Hello World',
       'h w': 'Hello World',
       'p+q': 'Hello World'
      };
      
      • JavaScript 引擎如果遇到无法确定是对象还是代码块的情况,一律解释为代码块,如果要解释为对象,最好在大括号前加上圆括号。
      // eval语句(作用是对字符串求值)
      eval('{foo: 123}') // 123
      eval('({foo: 123})') // {foo: 123}
      
      • 数字键在方括号读取运算符中可以不加引号,但不能使用点运算符读取。
      var obj = {
        123: 'hello world'
      };
      
      obj.123 // 报错
      obj[123] // "hello world"
      
      • Object.keys():返回一个对象本身的所有属性。
      var obj = {
        key1: 1,
        key2: 2
      };
      
      Object.keys(obj);
      // ['key1', 'key2']
      
      • delete:用于删除对象的属性,删除成功后返回true,但删除一个不存在的属性,delete不报错,而且也返回true,只有某属性不可删除时(configurable:false)才返回false,只能删除对象本身的属性,无法删除继承的属性。
      • in:用于检查对象是否包含某个属性,如果包含就返回true,否则返回false,但无法识别哪些属性是对象自身的,哪些属性是继承的(可以用hasOwnProperty判断)。
      var obj = { p: 1 };
      'p' in obj // true
      'toString' in obj // true
      
      var obj = {};
      if ('toString' in obj) {
        console.log(obj.hasOwnProperty('toString')) // false
      }
      
      • for...in:用来遍历一个对象的全部属性,仅遍历可遍历(enumerable:true)属性跳过不可遍历属性,不仅遍历对象自身的属性,还遍历继承的属性(前提是继承的属性是可遍历的),如果想仅遍历自身属性可用hasOwnProperty在循环内部判断。
      var obj = {a: 1, b: 2, c: 3};
      for (var i in obj) {
        console.log('键名:', i);
        console.log('键值:', obj[i]);
      }
      // 键名: a
      // 键值: 1
      // 键名: b
      // 键值: 2
      // 键名: c
      // 键值: 3
      
      • with:操作同一个对象的多个属性时,提供一些书写的方便(在内部可以不使用点运算符就能直接读取属性),如果with区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量,不建议使用with语句,无法判断内部变量为对象属性还是全局变量
      var obj = {
        p1: 1,
        p2: 2,
      };
      with (obj) {
        p1 = 4;
        p2 = 5;
      }
      // 等同于
      obj.p1 = 4;
      obj.p2 = 5;
      
    • 函数:
      • name属性:返回函数的名字,常用的是获取参数函数的名字。
      var myFunc = function () {};
      function test(f) {
        console.log(f.name);
      }
      test(myFunc) // myFunc
      
      • length属性:返回函数预期传入的参数个数,即函数定义之中的参数个数。
      function f(a, b) {}
      f.length // 2
      
      • 函数本身作用域:函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。
      var a = 1;
      var x = function () {
        console.log(a);
      };
      
      function f() {
        var a = 2;
        x();
      }
      
      f() // 1
      
      • 参数传递方式:如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value),在函数体内修改参数值,不会影响到函数外部;如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference),在函数内部修改参数,将会影响到原始值,但如果修改参数的地址指向,则不会影响原始值。
      var obj = { p: 1 };
      function f(o) {
        o.p = 2;
      }
      f(obj);
      obj.p // 2
      
      function f2(o) {
        o = { p: 3 };
      }
      f2(obj);
      obj.p // 2
      
      • arguments对象:包含了函数运行时的所有参数,这个对象只有在函数体内部才可以使用,正常模式下arguments对象可以在运行时修改,但严格模式下不行,它很像数组,但它是一个对象,数组专有的方法(比如sliceforEach)无法使用,但可以将它转为真正的数组(slice方法或遍历填入新数组)。
      var f = function(a, b) {
        'use strict'; // 开启严格模式
        arguments[0] = 3;
        arguments[1] = 2;
        return a + b;
      }
      f(1, 1) // 2
      
      var args = Array.prototype.slice.call(arguments);
      // 或者
      var args = [];
      for (var i = 0; i < arguments.length; i++) {
        args.push(arguments[i]);
      }
      
      • 闭包:能够读取其他函数内部变量的函数,由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”,闭包的最大用处,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在,还有一个是封装对象的私有属性和私有方法。
        PS:外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。
      function createIncrementor(start) {
        return function () {
          return start++;
        };
      }
      var inc = createIncrementor(5);
      
      inc() // 5
      inc() // 6
      inc() // 7
      
      • 立即调用的函数表达式(IIFE-Immediately-Invoked Function Expression):通常情况下,只对匿名函数使用,它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
      (function(){ /* code */ }());
      // 或者
      (function(){ /* code */ })();
      
      • eval命令:接受一个字符串作为参数,并将这个字符串当作语句执行,参数如果不是字符串就会原样返回,eval没有自己的作用域,可能会因为修改当前作用域的变量造成安全问题(使用严格模式,eval内部声明的变量,不会影响到外部作用域,但依然可以读写当前作用域的变量),最常见的场合是解析 JSON 数据的字符串(应使用JSON.parse替代),凡是使用别名执行eval,eval内部一律是全局作用域。
          eval('var a = 1;'); // 生成变量a
          eval(123) // 123 // 非字符串原样返回
      
    • 数组:
      • JavaScript 语言规定,对象的键名一律为字符串,所以,数组的键名其实也是字符串,之所以可以用数值读取,是因为非字符串的键名会被转为字符串。
      var arr = ['a', 'b', 'c'];
      arr['0'] // 'a'
      arr[0] // 'a'
      
      • JavaScript 使用一个32位整数保存数组的元素个数,所以数组成员最多只有 4294967295 个(232 - 1)个,length属性的最大值就是 4294967295。
      • length属性可写,如果设置一个小于当前成员个数的值,该数组的成员数量会自动减少到length设置的值,可以通过将length设置为0来清空数组。
      var arr = [ 'a', 'b', 'c' ];
      arr.length = 2;
      arr // ["a", "b"]
      arr.length = 0;
      arr // []
      
      • 数组本质上是一种对象,所以可以为数组添加属性,但是这不影响length属性的值。
      var a = [];
      a['p'] = 'abc';
      a.length // 0
      
      • for...in:不仅会遍历数组所有的数字键,还会遍历非数字键,所以不推荐使用它对数组进行遍历,应该使用循环遍历或forEach方法替代。
      var a = [1, 2, 3];
      a.foo = true;
      for (var key in a) {
        console.log(a[key]);
      }
      // 1
      // 2
      // 3
      // true
      
      a.forEach(function (item) {
        console.log(item);
      });
      // 1
      // 2
      // 3
      
      • delete:删除数组元素后不影响length属性,删除的位置形成空位,读取时返回undefined,所以用length属性遍历时需要注意,是无法跳过空位的。
      var a = [, , ,];
      a[1] // undefined(空位与该位置值为undefined不一样,空位在forEach、for...in、Object.keys遍历时会跳过
      
      • 类数组对象(array-like object):很像数组的对象,如果一个对象的所有键名都是正整数或零,并且有length属性(非动态,不会随着成员的变化而变化),不具备数组的特有方法,常见的有arguments对象、大多数Dom元素集、字符串,slice方法可以将类数组对象转换为真正的数组,除此之外可以通过call()把数组方法嫁接到对象上。
      var obj = {
        0: 'a',
        1: 'b',
        2: 'c',
        length: 3
      };
      
      obj[3] = 'd';
      obj.length // 3
      
      // forEach 方法(本来arguments对象无法调用该方法,但这种方法比直接使用数组原生的forEach要慢,所以最好还是先将“类似数组的对象”转为真正的数组,然后再直接调用数组的forEach方法。)
      function logArgs() {
        Array.prototype.forEach.call(arguments, function (elem, i) {
          console.log(i + '. ' + elem);
        });
      }
      

    参考资料

    JavaScript 语言入门教程 :https://wangdoc.com/javascript/index.html

  • 相关阅读:
    sql语句添加查询字段
    SqlServer Case when then用法总结
    单例与多线程
    HttpSession详解
    范式
    SQL语句中的Having子句与where子句
    HTTP无状态
    字节流与字符流的区别
    选择排序
    ReentrantLock VS synchronized
  • 原文地址:https://www.cnblogs.com/dreamsqin/p/13686263.html
Copyright © 2011-2022 走看看