语法

语句结束

  • 语句以分号结尾,但是并非强制的,浏览器中负责执行JavaScript代码的引擎会自动在每个语句的结尾补上;

    • 让JavaScript引擎自动加分号在某些情况下会改变程序的语义,导致运行结果与期望不一致;所以最好手动加分号

      注释

  • 单行注释://

  • 多行注释: /* */

  • 代码块使用{}包含

    引入js

  • HTML中引入js代码块:<script>...</script>

  • HTML中引入js文件:<script src="..."></script>

    运行JS

  • 在html中引入js代码块或者js文件,加载html即会加载js

    • 对于一些需要联网执行的js代码,不能直接从本地通过**file://**来访问,因为浏览器安全限制,**file://链接不能执行联网操作** 需要架设服务器通过http://来访问
  • 直接运行js:安装node,通过node直接运行js文件

    • 这种方法对于需要浏览器支持的js代码无法运行,如alert,操作dom等运行不了

      数据类型

  • JavaScript是动态语言,动态语言即创建时不指定数据类型,编译运行时识别类型

  • 使用类型均可直接输出
    • 数组输出会有[]
    • 对象输出会有{} :

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
引用数据类型:对象(Object)、数组(Array)、函数(Function)。
ES6还新增了Map和Set类型


  • Number JavaScript不区分数值型,都用Number表示。可以存储如下数据
    • 整数 可以使用十六进制整数:0x...
    • 浮点数 浮点数可以使用科学计数法
    • NaN 表示无法计算的值,如**0/0=NaN** 跟数学有些区别
    • Infinity 表示无穷大的值,如**2/0=Infinity**
  • 字符串
    • 使用单引号或者双引号均可
  • 布尔值
  • null
  • undefined 未定义 即创建了变量没有赋值任何值,则采用默认值undefined
    • 和null没有什么区别,大多数情况下,我们都应该用null。undefined仅仅在判断函数参数是否传递的情况下有用。
  • 数组
  • 对象

    内置类

    Number类

  • isNaN()检擦参数是否是数字,返回 boolean 值

  • toFixed()方法 将当前数字对象四舍五入至指定小数位数,返回字符串

    Math

  • round(n) 将参数 n 四舍五入至离它最近的整数

  • sqrt(n) 求参数 n 的平方根
  • random() 获得一个 0 到 1 之间的随机数

    Date

  • 日期的比较:date对象可以直接使用大于小于进行比较

    1. var date=new Date(); //使用无参构造得到当前时间
    2. Date.parse(日期字符串) //得到毫秒值
    3. var date2=new Date(毫秒值) //得到指定时间
    4. date2.setFullYear(2010,0,14); //设置指定时间

    ```javascript var box = new Date();

box.toDateString(“”); // 以特定的格式显示星期几、月、日和年 box.toTimeString(); // 以特定的格式显示时、分、秒和时区 box.toUTCString(); // 以特定的格式显示完整的 UTC 日期。

  1. - 注意月份返回的是0-11.
  2. ```javascript
  3. var box = new Date();
  4. box.getTime(); // 获取日期的毫秒数,和 valueOf()返回一致
  5. box.setTime(100); // 以毫秒数设置日期,会改变整个日期
  6. box.getFullYear(); // 获取四位年份
  7. box.setFullYear(2012); // 设置四位年份,返回的是毫秒数
  8. box.getMonth(); // 获取月份,没指定月份,从 0 开始算起
  9. box.setMonth(11); // 设置月份
  10. box.getDate(); // 获取日期 ,即获取当前的天
  11. box.setDate(8); // 设置日期,返回毫秒数
  12. box.getDay(); // 返回星期几,0 表示星期日,6 表示星期六
  13. box.setDay(2); // 设置星期几
  14. box.getHours(); // 返回时
  15. box.setHours(12); // 设置时
  16. box.getMinutes(); // 返回分钟
  17. box.setMinutes(22); // 设置分钟
  18. box.getSeconds(); // 返回秒数
  19. box.setSeconds(44); // 设置秒数
  20. box.getMilliseconds(); // 返回毫秒数
  21. box.setMilliseconds(); // 设置毫秒数
  22. box.getTimezoneOffset()); // 返回本地时间和 UTC 时间相差的分钟数
//注意MM是获取月份,mm是获取分钟
//yyyy 年  hh小时   dd  天   ss秒   区分大小写

Date.prototype.format = function (format) {
                        var o = {
                            "M+": this.getMonth() + 1, //month
                            "d+": this.getDate(), //day
                            "h+": this.getHours(), //hour
                            "m+": this.getMinutes(), //minute
                            "s+": this.getSeconds(), //second
                            "q+": Math.floor((this.getMonth() + 3) / 3), //quarter
                            "S": this.getMilliseconds() //millisecond
                        }
                        if (/(y+)/.test(format)) format = format.replace(RegExp.$1,
                            (this.getFullYear() + "").substr(4 - RegExp.$1.length));
                        for (var k in o) if (new RegExp("(" + k + ")").test(format))
                            format = format.replace(RegExp.$1,
                                RegExp.$1.length == 1 ? o[k] :
                                    ("00" + o[k]).substr(("" + o[k]).length));
                        return format;
                    }

Moment.js

moment().format('YYYY--MM-DD-hh/HH-mm-ss-dd');  //moment()即获取当前时间//获取年月日时分秒秒星期几   HH是二十四小时制,hh是十二小时制 //也可以指定时间得到moment对象   
moment("1995-12-25");
//或者传入个date对象
moment(new Date())
//获取年月日时分秒   区分大小写
延后:add(?,'单位')
提前  subtract(?,'单位')   用法同add
moment().add(7, 'days').add(1, 'months'); // 链式
moment().add({days:7,months:1}); // 对象字面量并使用单位简写

//注意!!!使用add()或subtract()加减日期的时候会改变自身的值,需要复制过后才行

方法1.使用moment()函数再包装一次对象
将代码const end = start.add(1, 'day')改成const end = moment(start).add(1, 'day')
方法2.使用clone()方法复制一个对象
将代码const end = start.add(1, 'day')改成const end = start.clone().add(1, 'day')
  • 单位即单位简写 | years | y | | —- | —- | | quarters | Q | | months | M | | weeks | w | | days | d | | hours | h | | minutes | m | | seconds | s | | milliseconds | ms |

运算符

  • 逻辑运算符:&& || !
  • ==和===区别:不要使用==比较,始终坚持使用===比较。

    • ==会将2值转为相同的类型再比较,这会导致一些预想不到的错误
    • ===直接比较,如果类型不一致则直接返回false
    • NaN这个特殊的Number与所有其他值都不相等,包括它自己
      • 唯一能判断NaN的方法是通过isNaN()函数 isNaN(NaN,NaN) -> true
    • 对于数学上相等的浮点数js不一定相等 1 / 3 === (1 - 2 / 3); // **false**
      • 相同的浮点表达式还是true1/3===1/3 true
      • 浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值
        • **Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true** abs得到绝对值

          变量

  • 定义变量可以通过var a定义,也可以直接定义a;即定义了a变量

  • JavaScript在设计之初,为了方便初学者学习,并不强制要求用var申明变量。这个设计错误带来了严重的后果:如果一个变量没有通过var申明就被使用,那么该变量就自动被申明为全局变量
    • 即方法外的变量,无论有无var都是全局
    • 方法内的变量,var定义的就是局部变量,无var则为全局
  • 实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性
  • 在JavaScript代码的第一行写上**'use strict';**可以启用strict模式

    • 在strict模式下运行的JavaScript代码,强制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。
    • 任何时候都应该采用strict模式

      解析赋值

  • 从ES6开始,JavaScript引入了解构赋值,可以同时对一组变量进行赋值

    • 如对数组赋值,传统做法是根据索引一个一个赋值。解析赋值则可以直接对多个变量同时赋值var [x, y, z] = ['hello', 'JavaScript', 'ES6']; 通过一个数组创建x,y,z三个变量
  • 解析构造的作用就是创建变量,所以可以直接访问构建的变量
  • 忽略某些元素:let [, , z] = ['hello', 'JavaScript', 'ES6'];
  • 嵌套解析:**let** [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];

  • 从一个对象中取出若干属性:var person={...} var {name, age} = person;:提取person对象的部分属性用于创建2个变量

    • 不存在的属性构造的变量为undefined
      var person = {
      name: '小明',
      age: 20,
      gender: 'male',
      passport: 'G-12345678',
      school: 'No.4 middle school',
      address: {
         city: 'Beijing',
         street: 'No.1 Road',
         zipcode: '100001'
      }
      };
      var {name, address: {city, zip}} = person;
      name; // '小明'
      city; // 'Beijing'
      zip; // undefined, 因为属性名是zipcode而不是zip
      // 注意: address不是变量,而是为了让city和zip获得嵌套的address对象的属性:
      address; // Uncaught ReferenceError: address is not defined
      
  • 如果要使用的变量名和属性名不一致,可以用下面的语法获取: ```javascript var person = { name: ‘小明’, age: 20, gender: ‘male’, passport: ‘G-12345678’, school: ‘No.4 middle school’ };

// 把passport属性赋值给变量id: let {name, passport:id} = person; name; // ‘小明’ id; // ‘G-12345678’ // 注意: passport不是变量,而是为了让变量id获得passport属性: passport; // Uncaught ReferenceError: passport is not defined


- 解构赋值还可以使用默认值,这样就避免了不存在的属性返回undefined的问题:
```javascript
var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678'
};

// 如果person对象没有single属性,默认赋值为true:
var {name, single=true} = person;
name; // '小明'
single; // true
  • 如果变量已经被声明了,再次赋值的时候,正确的写法也会报语法错误。这是因为JavaScript引擎把{开头的语句当作了块处理,于是=不再合法。解决方法是用小括号括起来 ```javascript // 声明变量: var x, y; // 解构赋值: {x, y} = { name: ‘小明’, x: 100, y: 200}; // 语法错误: Uncaught SyntaxError: Unexpected token =

解决方法: ({x, y} = { name: ‘小明’, x: 100, y: 200});


- 更多运用
```javascript
使用场景
解构赋值在很多时候可以大大简化代码。例如,交换两个变量x和y的值,可以这么写,不再需要临时变量:

var x=1, y=2;
[x, y] = [y, x]
快速获取当前页面的域名和路径:

var {hostname:domain, pathname:path} = location;
如果一个函数接收一个对象作为参数,那么,可以使用解构直接把对象的属性绑定到变量中。例如,下面的函数可以快速创建一个Date对象:

function buildDate({year, month, day, hour=0, minute=0, second=0}) {
    return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
}
它的方便之处在于传入的对象只需要year、month和day这三个属性:

buildDate({ year: 2017, month: 1, day: 1 });
// Sun Jan 01 2017 00:00:00 GMT+0800 (CST)
也可以传入hour、minute和second属性:

buildDate({ year: 2017, month: 1, day: 1, hour: 20, minute: 15 });
// Sun Jan 01 2017 20:15:00 GMT+0800 (CST)

字符串

  • JavaScript的字符串就是用’’或””括起来的字符表示。
    • 如果’本身也是一个字符,那就可以用””括起来,比如"I'm OK"包含的字符是I,’,m,空格,O,K这6个字符。
    • 如果字符串内部既包含’又包含”,可以用转义字符\来标识,比如:'I\'m \"OK\"!' 表示I'm "OK"!
    • ASCII字符可以以\x##形式的十六进制表示,例如:'\x41'等同于 ‘A’
    • 还可以用\u####表示一个Unicode字符
  • 字符串是不可变的,但是字符串变量可以变,详见字符串索引
  • 字符串也有一个**length**属性

    多行字符串

    • 使用转义字符\n表示换行
    • ES6标准新增了一种多行字符串的表示方法,用反引号****表示
      var sss='Hello\nWorld';   传统多行字符串
      var ssss=`Hello
      World`;      es6新特性多行字符串
      输出得到:
      Hello
      World
      

      模板字符串

  • 一般连接字符串与变量使用+,但是如果变量很多时就不方便。

    • ES6增加了模板字符串特性 字符串中${变量名}会自动替换为变量值
      var name = '小明';
      var age = 20;
      var message = '你好, ' + name + ', 你今年' + age + '岁了!';
      var message2 = '你好, ${name}, 你今年${age}岁了!';
      

      字符串索引

  • js中可以使用数组索引的方式来获取字符串中某个位置的字符 从0开始

    • var s="abcd"; s[2] -> c
  • 字符串是不可变的,但是字符串变量可以变 即可以改变字符串变量的值,但是无法改变字符串的值

    • 如通过索引修改字符串的某个字符,不会报错但是也没有任何效果
    • JavaScript的修改字符串的方法本质也是返回一个新的字符串

      常用方法

  • toUpperCase()将字符串变为大写并返回

  • toLowerCase()把一个字符串全部变为小写并返回
  • indexOf(字符串) 搜索指定字符串首次出现的位置,不存在返回-1
  • lastIndexOf()返回最后一次出现的索引
  • subString(索引) 前闭后开
    • subString(a,b)返回a-b之间的字符串
    • subString(a) 返回索引a后面的所有字符串
  • split()以参数分隔字符串,返回字符串数组
  • trim()去除前后空格
  • replace()替换部分内容,只替换第一个找到的
  • Es6扩展
    • includes() 返回布尔值,表示是否找到了参数字符串。
    • startsWith() 返回布尔值,表示参数字符串是否在原字符串的头部。
    • endsWith() 返回布尔值,表示参数字符串是否在原字符串的尾部。
  • RegExp.test()判断字符串是否符合正则表达式,返回布尔值

    var str = "hello world";
    var patt1 = new RegExp("ll");
    var result = patt1.test(str);  //true
    

    数组

  • js中的数组可以存储任意类型

    • 创建数组有2种方法:var a=[1,null,"s"]或者=new Array(1,null,"s")
  • length属性得到数组长度。对数组的length属性赋值可以修改数组的大小。
    • 修改为更大的length时,增加的数组元素值为undefined
    • 修改为更小length时,会砍掉超出大小的数组元素
  • 对数组索引赋值时,如果索引值超出大小也会导致数组大小变化

    • var a={1,2,3}此时length为3 a[5]=0 数组大小变为6

      多维数组

  • 数组内某个元素也是数组,那就构成了多维数组

    var arr = [[1, 2, 3], [400, 500, 600], '-'];
    获取500  arr[1][1]
    

常用方法

  • slice(a)/slice(a,b) 等同于字符串subString()的作用
    • slice()参数为空的时候,会返回数组所有元素。可以利用这点复制数组
  • push(?,...)数组末尾添加若干元素,并返回数组长度
    • (如果push超出数组长度就会返回新的长度)
  • shift()删除第一个元素,会修改原数组
  • pop() 删除数组末尾最后一个元素(是数组大小减小1,不仅仅是删除一个元素值),并返回被删除的元素。
    • 如果数组大小为0,则返回undefined
  • unshift(?,...) 数组头部添加若干元素,返回数组大小,超出返回新大小
  • sort()正序排序数组,返回排序后的数组
  • reverse()反转数组并返回数组,会改变原数组,而非创建新数组
  • join()连接所有数组元素得到字符串并返回
    • join(字符) 用指定字符进行连接 不想要元素间有分隔就join('')
    • join()无参时采用字符,连接
  • concat(另外一个数组) 连接2个数组并返回连接后的数组
    • concat()方法可以接收任意个元素和Array**var** arr = ['A', 'B', 'C']; arr.concat(1, 2, [3, 4]); _// ['A', 'B', 'C', 1, 2, 3, 4]_
  • splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:

    var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
    // 从索引2开始删除3个元素,然后再添加两个元素:
    arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
    arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
    // 只删除,不添加:
    arr.splice(2, 2); // ['Google', 'Facebook']
    arr; // ['Microsoft', 'Apple', 'Oracle']
    // 只添加,不删除:
    arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
    arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
    

    对象

  • JavaScript的对象是一组由键-值组成的无序集合 对象键值对值必须写在{}中

    • 通过对象名.属性名可以得到属性值 还可以对象名['属性名']访问
      • 对象的属性其实都是字符串,对应的值是多种多样
      • 如果访问不存在属性,返回undefined
    • 最后一个键值对无需加, 对于大部分的浏览器来说,加了也没事,有些浏览器会报错
    • 属性名如果包含特殊字符,则属性名必须用''包裹起来

  • 增加一个属性:对象名.属性名=?;即访问一个属性并赋值,如果这个属性不存在则增加属性并赋值
  • 删除一个属性:delete ?.?; 删除一个不存在的属性不会报错
  • 查看某一个属性是否存在:'属性名' **in** 对象; 返回布尔值
    • 如果一个对象自身不存在某属性,但是继承得到了该属性,也会返回true
  • 对象.hasOwnProperty("属性") 查看一个对象自身是否存在某属性,

    var person = {
      name: 'Bob',
      age: 20,
      tags: ['js', 'web', 'mobile'],
      city: 'Beijing',
      hasCar: true,
      zipcode: null
     'middle-school': 'MiddleSchool'   //含有特殊字符
    };
    

    Map

  • Map是ES6新增的类型,可以看成是List<Map>Array[Map,Map,...]类型,总之就是可以存很多个键值对。但是不同于对象,对象的键(属性)只能是字符串,Map一个键值对的2个值可以是任意值

    • Map更适合存储仅仅是多个同类型同意义的键值对,对象则不能多个同名的属性。
    • Map具有极高的查找速度

  • new Map() 创建一个Map
    • **new** Map([[键, 值], [键, 值],...]);
  • set(key,value) 添加一个键值对
    • 对已存在值的键值对进行set会覆盖旧值
  • has(value) 查询是否存在一个key名为value的键
  • delete(key) 删除一个键值对
  • get(key) 获取键对应的值,不存在则返回undefined

    Set

  • Set可以看成等同于java中的Set:存储一系列不重复的值


  • 创建Set:new Set() new Set([value1,value2,...]) 即传入一个数组
    • 创建Set时如果存入了相同的值会被自动过滤 new Set([1,1,2]) 得到[1,2]
  • 添加add(value) 添加已存在的不会有效果
  • 删除delete(value)
  • 元素是否存在boolean has(value)
  • Set转数组:Array.from(Set)

    iterable与Map,Set的坑

  • 遍历数组可以采用下标访问。对于Set和Map则不可以。自然Set和Map同样不可以用for-in/for进行遍历 (一些遍历方法如each()貌似也不能对其进行遍历)

  • Map和Set可以再嵌套Map,Set,但是实际运用中经常会报错,时好时坏。所以最好不要嵌套
  • Map,Set创建就具有静态的特性

AXRT~WK368W1J4WOYM0`}DJ.jpg

for-of

  • 遍历Array可以采用下标循环,遍历Map和Set就无法使用下标。为了统一集合类型,ES6标准引入了新的iterable类型,Array、Map和Set都属于iterable类型。他们本质都是通过数组保存元素
    • 具有iterable类型的集合可以通过新的for … of循环来遍历。 ```javascript var m = new Map([[1, ‘x’], [2, ‘y’], [3, ‘z’]]); for (var x of m) { // 遍历Map console.log(x[0] + ‘=’ + x[1]); } 得到的x就是m的每一个键值对(Map就是存有一个二维数组,for-of返回一整行),即map循环会得到很多个长度为2的数组
      则x[0]就是键 x[1]就是键值

Array和Set得到的就是每一个元素


- **for ... in循环由于历史遗留问题,它遍历的实际上是对象的属性名称。一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。**
   - 当我们手动给Array对象添加了额外的属性后,for ... in循环将带来意想不到的意外效果
      - **for-in则只遍历数组本身的元素**
   - **而且for-in不能用于Map,Set。其他方法获取不到还会返回undefined,用for-in遍历Map,Set连undefined都不会得到**

<br />
```javascript
var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x in a) {
    console.log(x); // '0', '1', '2', 'name'          name也被输出了出来   
}

forEach()

  • 只能用于iterable对象。
  • 参数名虽然不是固定的,但是最好就按照下面的来
  • 数组forEach(function (element, index, array) {...});
    • element: 指向当前元素的值
    • index: 指向当前索引
    • array: 表示指向一个Array对象 即会把iterable的数组或者子数组提取出来
  • Map:forEach(function (value, key, set) {...});
    • 第一个参数返回键值,第二个参数返回索引,第三个参数返回map本身
  • Set:forEach(function (element, sameElement, set) {... });

    • set因为没有索引,所以前两个参数都是返回元素,第三个返回set本身

      for-in

  • js的语句控制跟java没什么差别,除了for-in,所以单独讲下for-in

  • for-in可以用于数组,对象,字符串。
  • for-in本质上还是for,就是for的变种
    • 用于对象时得到所有属性名
      • 除了对象自身所有的属性,继承到的属性也会获取的,可以使用hasOwnProperty()进行过滤
    • 用于数组和字符串,得到所有数组元素或者字符串中字符的索引
      • 得到的索引是字符串型的索引不是number
        var o = {
        name: 'Jack',
        age: 20,
        city: 'Beijing'
        };
        for (var key in o) {
        if (o.hasOwnProperty(key)) {
        console.log(key); // 'name', 'age', 'city'
        }
        }