一、JS 基础


  1. JavaScript 是一种运行在客户端上 的脚本语言。
    2. JavaScript 不需要编译,运行过程中由JS解释器(JS引擎)逐行来进行解释并运行。
    3. JavaScript 执行逻辑:解释一行后立刻执行一行,若出错则停止执行,不会继续解释下一行。
    4. ECMAScript:规定了JS的编程语法和基础核心知识,即JS语法的工业标准。
    5. 单双引号的使用:在HTML中推荐使用双引号,而JavaScript中推荐使用单引号。

    二、JS 输入输出语句


  1. alert('msg'); 浏览器弹出警示框,归属浏览器。
    2. prompt('msg'); 浏览器弹出输入框,用户可输入信息,归属浏览器。
    【注意】利用 prompt(还有表单) 获取的值的类型为字符串型!
    3. console.log('msg'); 浏览器控制台打印输出信息,归属浏览器。

    三、JS 变量


  1. 更新变量:一个变量被重新赋值后,它原有的值会被覆盖,变量值以最后一次赋的值为准。
    2. 同时声明多个变量,只需写一个 var ,多个变量名之间使用英文逗号隔开。
    3. 变量只声明不赋值,则该变量的内容为 undefined 。
    4. 变量不声明、不赋值且直接使用(输出),JS引擎报错。
    5. 变量不声明(没加var)但直接赋值使用(输出),能正常使用,JS引擎不报错; 该变量变为全局变量
    6. 变量命名规范
  • 由字母、数字、下划线( _ )、美元符号( $ )组成。
  • 严格区分大小写。
  • 不能以数字开头。
  • 不能是关键字、保留字,例如 var、for、if、else、while 等。
  • 尽量遵守驼峰命名法:首字母小写,后面单词的首字母需要大写,例如 myFirstName。
  • 特别注意,尽管 name 不是关键字或保留字,但尽量不要用它作为变量名,因为某些浏览器的 name 有特殊含义。

    四、JS 数据类型


1. JS 数据是动态类型

  • JavaScript 是一种弱类型语言,即变量在程序运行过程中,其类型会根据等号右边的值来确定。
  • JavaScript 拥有动态类型,这意味着同一个变量可用作不同的类型(可变的)。

    2. 简单数据类型

    ① number:数字型,包含整型值和浮点值,默认值为 0。
    ② boolean:布尔值类型,包含 true 和 false ,默认值为 false。
    ③ string:字符串类型,用(单 / 双)引号括住的值,默认值为 ‘’(空字符串)。
    ④ null:空值,var a = null; 声明了变量a为空值。
    ⑤ undefined:未定义,var a; 声明了变量a但是没有赋值,此时 a = undefined。
    【特别注意】NaN,属于number型,它由数字型值与非数字型值运算后产生,意思是 Not a Number。
    【判断方法】isNaN(); //判断变量或值是否为【非数字】,是则返回 true,否则返回 false。

    3. 数字进制

    ① 八进制:数字前面加 0 ,例如 var num1 = 012; //换算为十进制是10
    ② 十六进制:数字前面加 0x ,例如 var num2 = 0x12; //换算为十进制是18

    4. 字符串转义字符【需要写在引号里】

    ① \n 换行符 newline
    ② \ 斜杠 \
    ③ \’ 单引号 ‘
    ④ \” 双引号 “
    ⑤ \t tab缩进
    ⑥ \b 空格 blank

    5. 字符串类型的相关方法

    ① 计算字符串长度:字符串变量.length(空格与标点符号都算一个字符)。
    ② 字符串拼接:使用加号 + 进行拼接,可拼接其他数据类型的变量或值,结果必定是字符串类型。
    1. console.log('LJM' + 'SB'); // 'LJMSB'
    2. console.log('LJM' + 22); // 'LJM22'
    3. console.log('LJM' + true); // 'LJMtrue'
    4. var age = 22;
    5. console.log('LJM' + age +'岁'); // 'LJM22岁'
    ③ 拓展:若使用其他算术运算符( - * / )会使字符串类型的数字被系统视作数字型,然后进行运算。

    6. boolean【布尔型】

    ① 只有两个值 true 和 false。
    ② 布尔型和数字型进行四则运算时,true 的值视为1,false 的值视为0。

    7. undefined【未定义】

    ① undefined 与字符串相加,结果为字符串,例如 undefined + 'pink' = undefinedpink
    ② undefined 与数字型 / 布尔型相加,结果为 NaN。

    8. null【空】

    ① null 与字符串相加,结果为字符串,例如 null + 'pink' = nullpink
    ② null 与数字型相加,结果为数字型,例如 null + 1 = 1
    ③ null 与布尔型相加,结果为数字型,例如 null + true = 1null + false = 0

    9. 检测变量的数据类型【typeof】

    【特别注意】
    var ljm = null;  
    console.log(typeof ljm);  //输出结果为 object
    

    10. 数据类型的转换

    1)转换为字符串型【常用第③种】
    toString() 使用方法:变量.toString()
    String() 使用方法:String(变量或值)
    ③ 利用加号 + 拼接空字符串 ‘’,实现字符串转换效果

2)转换为数字型【常用①、②】
parseInt(string) 使用方法:parseInt(字符串类型的变量或值)

parseInt('3.94');      //结果为 3,去掉小数取整,且不会进位
parseInt('120px');     //结果为 120,去掉'px'
parseInt('rem120px');  //结果为 NaN,因为字符串开头为字母而不是数字,系统无法识别

parseFloat(string) 使用方法:parseFloat(字符串类型的变量或值)

parseFloat('3.94');      //结果为 3.94,保留小数部分
parseFloat('120px');     //结果为 120,去掉'px'
parseFloat('rem120px');  //结果为 NaN,因为字符串开头是字母而不是数字,系统无法识别

Number() 使用方法:Number(字符串类型的变量或值)
④ 利用算术运算符( - * / )进行隐式转换。

3)转换为布尔型:Boolean(变量或值)
① 代表为空的值或否定的值会转换为 false,例如 空字符串’’ 空值null 数字0 否定值NaN undefined
② 其余的值都会被转换为 true。

五、JS 算术运算符


1. 运算符

加减乘除(+ - * /);还有取余数(又称取模),符号为 %

2. 浮点数

  • 浮点数值的最高精度为17位小数,但在进行算术运算时会出现精确度的问题;
  • 因此有以下结论:不能直接拿浮点数来比较是否相等,因为精确度不确定。

    3. 自增自减

  • 前置自增自减【++变量、—变量】

  • 后置自增自减【变量++、变量—】

① 单独使用时,前置与后置效果一样,都是自增1或自减1。
② 其他情况下,例如输出、循环、判断、与其他变量运算时,前置与后置效果有所区别。

  • 前置:先变量自增自减,后表达式返回变量值 var age = 10; console.log(++age + 10); //输出21
  • 后置:先表达式返回变量值,后变量自增自减 var age = 10; console.log(age++ + 10); //输出20
    var a = 10;
    var b = a++ + ++a;
    /*
    计算表达式1:a++,后置自增,故表达式1的返回值为10,随后a自增1,a = 10 + 1 = 11;
    计算表达式2:++a,前置自增,故a先自增1,a = 11 + 1 = 12,随后表达式2返回值为12;
    计算 b = a++ + ++a = 表达式1 + 表达式2 = 10 + 12 = 22。
    */
    console.log(b);   // 输出22
    

    4. 比较运算符【 = 小结】

    ① = 作用:赋值;用法:把右边的值赋给左边
    ② == 作用:判断;用法:判断两边的值是否相等,但 会把字符串型的数据转换为数字型 再判断
    ③ === 作用:全等;用法:判断两边的值和数据类型是否完全一致, 不会转换数据类型

    5. 短路运算【逻辑中断】

    1)原理:当有多个子表达式(值)同时进行逻辑运算时,若前面子表达式的值可确定整个表达式的值,则不再计算后面的子表达式的值(提高运算效率)

2)逻辑与运算【&&】

  • 语法:表达式1 && 表达式2
  • 如果表达式1的值为真,则返回表达式2的值;console.log( 123 && 456 ); //输出456
  • 如果表达式1的值为假,则返回表达式1的值;console.log( 0 && 456 ); //输出0

3)逻辑或运算【 || 】

  • 语法:表达式1 || 表达式2
  • 如果表达式1的值为真,则返回表达式1的值;console.log( 123 || 456 ); //输出123
  • 如果表达式1的值为假,则返回表达式2的值;console.log( 0 || 456 ); //输出456

    6. 运算符优先级

    ① 小括号 ()
    ② 一元运算符 ++ — !
    ③ 算数运算符 先 * / % 后 + -
    ④ 关系运算符 > >= < <=
    ⑤ 相等运算符 == != === !==
    ⑥ 逻辑运算符 先 && 后 ||
    ⑦ 赋值运算符 =
    ⑧ 逗号运算符 ,

    六、JS 数组


1. 创建数组的方法

① 利用 new 关键字创建空数组:var arr = new Array();
② 利用数组字面量 [ ] 创建数组:

//创建空数组
var arr1 = [ ];
//创建带数组元素的数组,元素可以是任意类型的数据
var arr2 = [1, 2, 'LJM傻逼', true];

2. 数组索引

  • 即数组下标,从 0 开始,用来访问数组元素的序号。
  • 若访问数组元素时超过了索引上限,则访问的值变为 undefined。

    3. 遍历数组

    for

  • 最简单的一种循环遍历方法,也是使用频率最高的一种,可优化

    var arr = [1, 2, 3, 4, 5, 6]
    for(var i = 0; i < arr.length; i++) {
      console.log(arr[i])
    }
    // 1 2 3 4 5 6
    
  • 优化:使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显

    var arr = [1, 2, 3, 4, 5, 6]
    var len = arr.length
    for(var i = 0; i < len; i++) {
      console.log(arr[i])
    }
    // 1 2 3 4 5 6
    

    for … in …

  • 这个循环用的人也很多,但是效率最低(输出的 key 是数组索引)

    var arr = ['我', '是', '谁', '我', '在', '哪']
    for(var key in arr) {
      console.log(key)
    }
    // 0 1 2 3 4 5
    

    for … of … (ES6)

  • 性能要好于 for … in … ,但仍然比不上普通的 for 循环(输出的 key 是元素值,但不能循环对象)

    var arr = ['我', '是', '谁', '我', '在', '哪']
    for(var key of arr) {
      console.log(key)
    }
    // 我 是 谁 我 在 哪
    

    forEach

  • 数组里的元素个数有几个,该方法里的回调就会执行几次

  • 第一个参数是数组里的元素,第二个参数为数组里元素的索引,第三个参数则是它自己
  • 数组自带的遍历方法,虽然使用频率略高,但是性能仍然比普通循环略低

    var arr = [1, 2, 3, 4, 5, 6]
    arr.forEach(function (item, idnex, array) {
      console.log(item)     // 1 2 3 4 5 6
      console.log(array)    // [1, 2, 3, 4, 5, 6] (输出6次)
    })
    

    map

  • 遍历每一个元素并返回到新数组里(可以返回处理后的元素)

  • 返回的新数组和旧数组的长度是一样的
  • 使用比较广泛,但其性能还不如 forEach

    var arr = [1, 2, 3, 4, 5, 6]
    var newArr = arr.map(function (item, idnex) {
      return item * item
    })
    console.log(newArr)    // [1, 4, 9, 16, 25, 36]
    

    filter

  • 遍历数组,过滤出符合条件的元素并返回一个新数组 ```javascript var arr = [ { id: 1, name: ‘买笔’, done: true }, { id: 2, name: ‘买笔记本’, done: true }, { id: 3, name: ‘练字’, done: false } ]

var newArr = arr.filter(function (item, index) { return item.done })

console.log(newArr) // [{ id: 1, name: ‘买笔’, done: true },{ id: 2, name: ‘买笔记本’, done: true }]

⑦ **some**

- 遍历数组,只要有一个以上的元素满足条件就返回 true,否则返回 false
```javascript
var arr = [
    { id: 1, name: '买笔', done: true },
    { id: 2, name: '买笔记本', done: true },
    { id: 3, name: '练字', done: false }
]

var bool = arr.every(function (item, index) {
    return item.done
})

console.log(bool)    // true

every

  • 遍历数组,每一个元素都满足条件则返回 true,否则返回 false ```javascript var arr = [ { id: 1, name: ‘买笔’, done: true }, { id: 2, name: ‘买笔记本’, done: true }, { id: 3, name: ‘练字’, done: false } ]

var bool = arr.every(function (item, index) { return item.done })

console.log(bool) // false

⑨ **find (ES6)**

- 遍历数组,返回符合条件的第一个元素,如果没有符合条件的元素则返回 undefined
```javascript
var arr = [1, 1, 2, 2, 3, 3, 4, 5, 6]
var num = arr.find(function (item, index) {
    return item === 3
})

console.log(num)   // 3

findIndex (ES6)

  • 遍历数组,返回符合条件的第一个元素的索引,如果没有符合条件的元素则返回 -1 ```javascript var arr = [1, 1, 2, 2, 3, 3, 4, 5, 6] var num = arr.findIndex(function (item) { return item === 3 })

console.log(num) // 4

<a name="uErDw"></a>
#### 4. 获取数组长度【数组名.length】
<a name="NSBBv"></a>
#### 5. 数组中添加元素
① 由于 length 属性是可读写的,所以可以通过修改它来实现数组扩容。<br />② 也可以直接追加数组元素,切记必须赋值。<br />③ 但是直接给数组名赋值的话,数组元素会被清空并覆盖。
<a name="zjHMv"></a>
#### 6. 数组冒泡排序算法
```javascript
var arr = [3,5,1,2,4];
// 从小到大排序
for (var i=0; i<arr.length; i++) {
  for (var j=0; j<arr.length-i; j++) {
    if (arr[j] > arr[j+1]) {
      var temp = arr[j];
      arr[j] = arr[j+1];
      arr[j+1] = arr[j];
    }
  }
}
// 从大到小排序
for (var i=0;  i<arr.length;  i++) {
  for (var j=0;  j<arr.length-i;  j++) {
    if (arr[j] < arr[j+1]) {
      var temp = arr[j];
      arr[j] = arr[j+1];
      arr[j+1] = arr[j];
    }
  }
}

七、JS 函数


1. 声明函数【关键字:function】

① 关键字声明函数

function 函数名(){
  // 函数体
}
// 函数调用
函数名();

② 表达式声明函数(匿名函数,即没有函数名)

var fun = function(){
  // 函数体
}
// 函数调用
fun();  // fun是变量名,不是函数名,所以fun可存储返回值

2. 函数形参与实参个数不匹配

① 函数的形参个数与实参个数可以不匹配,但结果难以预计。
② 若实参个数多于形参个数,不影响函数执行,多余的实参无效。
③ 若实参个数少于形参个数,缺少的形参的值默认为undefined,会影响函数最终结果。

3. 函数返回值【关键字:return】

① return只能返回一个值,且返回的是最后一个值,需要返回多个值的话用数组即可。

  • return num1, num2, num3; // 返回的是 num3

② return除了能返回数值,还有终止函数作用。
③ 若一个函数没有return,则默认返回undefined。

4. 总结【break、continue、return】

① break:结束当前的循环体(仅一层),或结束switch语句。
② continue:跳出本次循环(仅一层),继续执行下次循环。
③ return:在函数内,不仅可以退出循环(多层),还能返回一个值,并结束函数的运行。

5. arguments 对象【函数内置对象】

① arguments 对象中存储了传递的所有实参
② arguments 对象是一个伪数组,具有length属性和数组索引,但不能用真数组的一些方法。

6. 立即执行函数

【说明】不需要调用,一旦所属 js 文件被引用后立马就能自己执行的函数(立即执行函数之间需要使用 ; 隔开)
【作用】

  • 能够独立创建一个作用域,在立即函数内部的所有变量都是局部变量;
  • 不同的立即执行函数内的同名变量互不影响,系统也不会报错,避免了不同 js 文件之间的函数冲突;
  • 函数执行完后,系统就能立即清除里面的所有局部变量,节省内存。

【写法1】(function(形参){ 函数体... })(实参);

(function(a,b) {
  console.log(a + b);   // 输出 3
})(1,2);

【写法2】(function(形参){ 函数体... }(实参));

(function(c,d) {
  console.log(c + d);   // 输出 7
}(3,4));

八、JS 作用域


1. 作用域

① 全局作用域:整个标签,或者是外部的一个单独的 js 文件
② 局部作用域:即函数内部的作用域。
【注意】:ES6开始,JavaScript才新增了块级作用域( if、for、while、switch的{ }内部 )

2. 变量的作用域

① 全局变量:在任何作用域下都能使用的变量;在全局作用域下 var 声明的变量就是全局变量。
② 局部变量:只能在局部作用域下(函数内部)才能使用的变量;函数的形参属于局部变量。
【注意】:在函数内部,没有 var 声明然后直接赋值的变量,属于全局变量!

3. 两种变量的执行效率

① 全局变量只有在浏览器关闭的时候才会被销毁,比较占内存资源。
② 局部变量在程序执行完毕后就会被立即销毁,比较节约内存资源。

4. 作用域链

根据内部函数可访问外部函数的变量的机制,JS用链式查找方式(就近原则)来决定内部函数访问的是外部的哪一个变量。

var num = 123;      // 全局变量num
// 外部函数 f1
function f1()  {
  var num = 456;    // 局部变量num
  // 内部函数 f2
  function f2 ()  {
    console.log(num);  // 输出结果:456(若删去局部变量num,则输出123)
  }
  f2();
}
f1();

九、JS 预解析


1. JS 解释器运行步骤

① 预解析:把 js 代码里所有的 var 和 function 提升到当前作用域的最前面。
② 代码执行:按照代码书写的顺序从上往下执行。

2. 预解析

① 预解析分为:变量预解析 和 函数预解析
② 变量预解析:又称变量提升,就是把所有的变量声明提升到当前作用域的最前面,但不提升赋值操作。

// 原代码
console.log(num);
var num = 123;

// 预解析后
var num;
console.log(num);   // 输出结果 undefined
num = 123;

③ 函数预解析:又称函数提升,就是把所有的函数声明提升到当前作用域的最前面,但不调用函数。
【注意】必须弄清楚两种函数声明的预解析差异!

  • 关键字声明函数的预解析:把整个函数声明提到当前作用域的最前。
  • 表达式声明函数的预解析:只是把变量声明提到当前作用域的最前。

【综合案例1】

// 原代码
var num = 10;
fun();
function fun( ) {     // 关键字声明函数
  console.log(num);
  var num = 20;
}

// 预解析后
var num;
function fun() {
  var num;
  console.log(num);   // 输出的是局部变量num
  num = 20;
}
num = 10;
fun();               // 输出结果 undefined

【综合案例2】

// 原代码
say();
var say = function( ) {      // 表达式声明函数
  console.log('函数表达式');
}
say();
function say( ) {            // 关键字声明函数
  console.log('函数声明');
}
say();

// 预解析后
var say;                     // 表达式声明函数的预解析,say的值为undefined
function say() {             // 关键字声明函数的预解析
  console.log('函数声明');
}
say();        // 输出:函数声明
say = function() {           // 给say赋值新的函数
  console.log('函数表达式');
}
say();        // 输出:函数表达式
say();        // 输出:函数表达式

【综合案例3】

// 原代码
f1( );
console.log(c);
console.log(b);
console.log(a);
function f1( ) {
  var a=b=c=9;
  console.log(a);
  console.log(b);
  console.log(c);
}

// 预解析后
function f1( ) {
  var a;
  a=b=c=9;
  // 相当于 var a=9; b=9; c=9;  此时变量b和变量c被视为全局变量,而变量a是局部变量
  // 集体声明 var a=9, b=9, c=9; 这种才符合规范,且a b c都是局部变量
  console.log(a);
  console.log(b);
  console.log(c);
}
f1( );
console.log(c);
console.log(b);
console.log(a);
// 输出结果 9 9 9 9 9 报错(a is not defined)

十、JS 对象


1. 概念

  • 定义:对象是一组无序的相关属性和方法的集合
  • 类型:有三种,自定义对象、内置对象、浏览器对象

    2. 创建与调用方法

    ① 利用对象字面量 { } 创建对象
    【例1】创建一个空对象 var obj = { };
    【例2】创建一个有属性和方法的对象【用冒号 : 赋值、相互之间用逗号 , 隔开】

    var obj = {
    uname: '笨比LJM' ,
    age: 22 ,
    sex: '男' ,
    sayHi: function(){
      console.log('hi~');
    }
    }
    

    ② 利用 new Object 创建对象
    【例1】创建一个空对象 var obj = new Object( );
    【例2】创建一个有属性和方法的对象【用等号 = 赋值、相互之间用分号 ; 隔开】

    var obj = new Object();  
    obj.uname = '笨比LJM';
    obj.age = 22 ;
    obj.sex = '男' ;
    obj.sayHi = function(){
    console.log('hi~');
    }
    

    ③ 利用构造函数创建对象【这一过程称之为 对象的实例化】
    【格式】

    function 构造函数名(形参) {
    this.属性名 = 值;
    this.方法名 = function(){}
    }
    var 对象名 = new 构造函数名(实参);
    

    【注意】

  • 构造函数名字首字母要大写

  • 构造函数不需要写 return 也可以返回结果
  • 每次调用构造函数都必须使用关键字 new

④ 创建 / 调用对象的属性
【例】obj.unameobj['uname']
【注意】创建新属性时没有赋值的话,默认值为 undefined
【区别】

  • 对象名.属性名 这里的 属性名 是静态数据,不能由变量来代替;
  • 对象名['属性名'] 这里应该把 ‘属性名’ 看作一个整体,其本质是字符串,可由变量来代替,常用这种写法来动态创建新属性;
  • 数组表示法在存取属性值时会进行表达式运行;而点表示法是直接存取属性值,理论上执行效率会比数组表示法高。

⑤ 调用对象的方法【切记要加上小括号( ),不加则返回方法的定义】
【例】obj.sayHi()obj['sayHi']()

3. [变量 & 属性] [函数 & 方法] 的区别

① 变量:可存储数据,单独声明,需要 var,需要赋值,使用格式为 变量名。
② 属性:可存储数据,依靠对象,不需 var,需要赋值,使用格式为 对象名.属性名。
③ 函数:可实现某种功能,单独声明,调用格式为 函数名( )。
④ 方法:可实现某种功能,依靠对象,调用格式为 对象名.方法名( )。

4. 遍历对象【for - in 语句】

【格式】for (变量 in 对象) { 循环体 }

for (var key in obj) {     // key 是一个自定义的变量
  console.log(key);        // 输出变量 key,得到的是所有属性名 or 方法名
  console.log(obj[key]);   // 输出 obj[key],得到的是所有属性值 or 方法的定义
}

5. 内置对象

① JavaScript语言自带的一些对象,为开发者提供一些常用的功能(属性和方法)。
② 常见的内置对象:Math、Date、Array、String。
③ MDN文档查询【https://developer.mozilla.org/zh-CN/

  • 查阅该方法的功能
  • 查看里面参数的意义和类型
  • 查看返回值的意义和类型

Math 对象 【数学】

  • Math 对象不需要 new 来调用,直接使用其属性和方法即可。

【例1】console.log(Math.PI); // 调用Math对象的圆周率属性并输出
【例2】console.log(Math.max(1,99,8)); // 调用Math对象的求最大值方法,输出99

  • 常用方法
    • Math.abs(); // 取绝对值,会把字符串型的参数转化为数字型,参数无意义则返回 NaN
    • Math.floor(); // 向下取整,最终返回的是整数
    • Math.ceil(); // 向上取整,最终返回的是整数
    • Math.round(); // (正数)四舍五入、(负数)五入六舍,最终返回的是整数
    • Math.random(); // 返回一个随机小数,范围 [0,1) ,不带参数

【拓展】获取两个整数之间的随机整数,包含两个整数在内

function getRandomInt(min, max){
  return Math.floor( Math.random() * (max-min+1) ) + min;
}
// 可运用这个方法实现随机点名:array[getRandomInt(0, array.length-1)]

Date 对象 【日期 / 时间】

  • Date对象必须要用 new 来创建(构造函数)。
  • 创建方法:var date = new Date();
    • 如果不带参数,则返回系统的当前时间
    • 带参数,字符串型,例如 ‘1998-12-12 20:33:41’【推荐写法】
    • 带参数,数字型,例如 1998,12,12【不推荐,月份存在误差(0-11)】
  • 常用方法
    • dateObj.getFullYear(); // 获取当前年份
    • dateObj.getMonth(); // 获取当前月份(0-11)
    • dateObj.getDate(); // 获取当前日期
    • dateObj.getDay(); // 获取当前星期(周日0,周一到周六1-6)
    • dateObj.getHours(); // 获取当前小时(24小时制)
    • dateObj.getMinutes(); // 获取当前分钟
    • dateObj.getSeconds(); // 获取当前秒钟

【拓展】封装一个函数,返回当前的年月日星期几,格式:2020年10月24日 星期六

function getDatesNow(){
  var dateObj = new Date( );
  var year = dateObj.getFullYear();
  var month = dateObj.getMonth() + 1;
  var date = dateObj.getDate();
  var day = dateObj.getDay();
  var dayArray = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
  return '今天是' + year + '年' + month + '月' + date + '日 ' + dayArray[day];
}

【拓展】封装一个函数,返回当前的时分秒,格式:08:08:08

function getTimeNow(){
  var time = new Date();    // 创建一个Date对象
  var h = time.getHours();
  h = h < 10 ? '0' + h : h;    // 若时数小于10,则在前面补0
  var m = time.getMinutes();
  m = m < 10 ? '0' + m : m;    // 若分钟小于10,则在前面补0
  var s = time.getSeconds();
  s = s < 10 ? '0' + s : s;    // 若秒钟小于10,则在前面补0
  return h + ':' + m + ':' + s;
}
  • 获取日期的总毫秒数

【说明】Date对象是从1970年1月1日( 世界标准时间 )开始计算的毫秒数,利用毫秒数计算时间最精确
【用法】这个毫秒数又称“时间戳”,因为时间一直在流逝,所以这个毫秒数必定不会重复。
【写法一】

  • 调用Date对象的方法:dateObj.valueOf();dateObj.getTime();

【写法二】

  • 常用的简单写法:var dateMS = +new Date();
  • 无参数,则返回当前时间的总毫秒数;
  • 带参数,则返回参数所表示时间的总毫秒数。

【写法三】

  • HTML5新增的写法:console.log(Date.now());

【拓展】封装一个倒计时函数,由用户输入倒计时的日期。

function countDown(time){
  var nowTime = +new Date();  // 返回当前时间的总毫秒数
  var inputTime = +new Date(time);  // 返回用户输入的日期的总毫秒数
  var times = (inputTime - nowTime) / 1000;  // times是时间差,并转换为秒数
  if (times <= 0){
    return '倒计时结束!';
  }
  else  {
    var d = parseInt(times / 60 / 60 / 24);  // 天数
    d = d < 10 ? '0' + d : d;
    var h = parseInt(times / 60 / 60 % 24);  // 时数(剩余不足一天的时数)
    h = h < 10 ? '0' + h : h;
    var m = parseInt(times / 60 % 60);  // 分钟数(剩余不足一小时的分钟数)
    m = m < 10 ? '0' + m : m;
    var s = parseInt(times % 60);  // 秒钟数(剩余不足一分钟的秒钟数)
    s = s < 10 ? '0' + s : s;
    return d + '天' + h + '时' + m + '分' + s + '秒';
  }
}
console.log(countDown('2020-10-24 20:33:41'));

Array 对象 【数组】

  • Array 对象可以使用 new 来创建(构造函数)
    • var arr1 = new Array(); // 创建了一个空数组,没有数组元素,数组长度为0
    • var arr2 = new Array(2); // 创建了一个空数组,有2个空数组元素,数组长度为2
    • var arr3 = new Array(2,3); // 创建了一个非空数组,有2个数组元素[2,3],数组长度为2
  • Array 对象还可以使用数组字面量 [ ] 来创建(详情回看第六点)
  • 检测是否为数组对象
    • 运算符:变量名 instanceof Array; // 返回值 true 或 false
    • 方法:Array.isArray( 参数 或 变量名 ); // 返回值 true 或 false;优先使用这种方法,IE9以上
  • 添加删除数组元素的方法
    • arrObj.push(参数); // 在数组末尾添加一个或多个元素,返回新的数组长度
    • arrObj.pop(); // 删除数组最后一个元素,使数组长度减1,无参数,返回所删除的元素值
    • arrObj.unshift(参数); // 在数组开头添加一个或多个元素,返回新的数组长度
    • arrObj.shift(); // 删除数组第一个元素,使数组长度减1,无参数,返回所删除的元素值
  • 数组排序的方法

    • arrObj.reverse(); // 翻转数组,返回新的数组(会改变原数组
    • arrObj.sort(); // 排序
      • 无参数,将元素转换为字符串,再按各个字符的Unicode位点排序,返回新的数组(会改变原数组
      • 带参数,实现传统排序的功能,示例如下
        arrObj.sort(function(a,b) {
        return a-b;  // 升序排列
        return b-a;  // 降序排列
        });
        
  • 数组索引的方法

    • arrObj.indexOf(元素); // 查找给定元素的第一个索引,存在则返回索引号,不存在则返回-1
    • arrObj.lastIndexOf(元素); // 查找给定元素的最后一个索引,存在则返回索引号,不存在则返回-1

【拓展:数组去重算法】
【原理】遍历旧数组,拿着旧数组的元素去查询新数组,若不重复则把该元素 push 进新数组里。
【核心】利用 indexOf( ) 方法来判断元素是否重复

function unique(array)  {
  var newArr = [];
  for (var i = 0; i < array.length; i++)  {
    if (newArr.indexOf(array[i]) === -1)  {
      newArr.push(array[i]);
    }
  }
  return newArr;
}
  • 数组转换为字符串的方法
    • arrObj.toString(); // 无参数,用英文逗号分隔每一项,返回一个字符串
    • arrObj.join('分隔符'); // 用自定义的分隔符(无参数则默认逗号)分隔每一项,返回一个字符串
  • 其他数组方法
    • arrObj1.concat( arrObj2[, arrObj3, ...] ); // 连接两个或多个数组,返回一个新数组,不影响原数组
    • arrObj.slice( [begin[, end]] );
      • 提取数组元素,begin(包含) 与 end(不包含) 皆为数组索引,返回一个新数组,不影响原数组
      • 若 begin / end 为负数,则表示从原数组中的倒数第几个元素开始/结束提取。
      • 若 begin 大于原数组的长度,则会返回空数组。
      • 若省略 begin 和 end,则从索引0开始提取所有元素(即复制原数组)。
      • 若省略 end,则会一直提取到原数组末尾。若 end 大于原数组的长度,也是会一直提取到原数组末尾。
    • arrObj.splice( start[, deleteCount[, item1[, item2 ...]]] );
      • 可删除或替换现有元素,也可原地添加新的元素,返回被删除元素组成的数组(没有删除元素,则返回空数组),会改变原数组
      • start:指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容。
      • deleteCount:整数,表示要移除的数组元素的个数。若是0或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
      • item:要添加进数组的元素,从 start 位置开始。如果不指定,则 splice( ) 将只删除数组元素。

String 对象 【字符串】

  • 基本包装类型:就是把基本数据类型包装成为复杂数据类型,使其拥有了属性和方法。适用基本包装类型的有:string、number、boolean。

    // 平时常见的代码
    var str = 'LJMsb';
    console.log(str.length);  
    // 按道理基本数据类型没有属性和方法,对象才有,但这两行代码能正常执行
    // 这是因为JavaScript解释器会把基本数据类型包装成为复杂数据类型,过程如下:
    var temp = new String('LJMsb');  // 1. 生成临时变量,并使其变为复杂数据类型
    str = temp;                      // 2. 再赋值给我们声明的字符变量
    temp = null;                     // 3. 最后销毁临时变量
    
  • 字符串的不可变

    指的是值的不可变,虽然看上去可以改变某个字符串变量的值,实际上是地址变了,在内存中开辟了一个新的内存空间来放置新的字符串值,旧的还占着原本的内存。 故实际开发中要尽可能的减少字符串的修改或拼接行为,以免影响代码运行效率。

  • 根据字符返回位置( 索引号 )

    • strObj.indexOf('要查找的字符'[, 开始查找的位置]); // 返回指定内容在原字符串中的位置,若找不到则返回-1。开始查找的位置,可选,填的是索引号
    • strObj.lastIndexOf('要查找的字符'); // 从后往前找,只找第一个匹配的,返回指定内容在原字符串中的位置,若找不到则返回-1

【拓展:查找字符串内某个字符的所有位置及其出现的次数】

var str = 'abcceocpuowojbk';
var times = 1;  // 计算次数
var indexFind = str.indexOf('o');  // 查找第一个目标字符的位置
while (indexFind !== -1)  {
  console.log( '第' + times + '次:' + indexFind );
  index = str.indexOf('o', index+1);  // 从下一个字符开始继续查询
  times++;
}
  • 根据位置( 索引号 )返回字符
    • strObj.charAt(index); // 返回指定位置的字符,index为索引号
    • strObj.charCodeAt(index); // 返回指定位置的字符的ASCII码,index为索引号
    • strObj[ index ]; // HTML5新增,返回指定位置的字符,index为索引号;IE8+支持

【拓展:判断一个字符串中出现次数最多的字符,并统计其次数】
【原理】把每个字符都存储为一个对象的属性。若对象没有该属性,则新建一个并赋值1;若属性存在则属性值+1。最后遍历对象即可获取出现最多的字符及其出现次数。
【核心算法】strObj.charAt( index ); 、for-in语句(用于遍历对象)

var str = 'abcceocpuowojbko';
var charObj = { };  // 创建一个空对象
for (var i=0; i<str.length; i++)  {
  var chars = str.charAt(i);  // 获取索引号为 i 的字符
  if ( charObj[chars] )  {
    charObj[chars]++;   // 该字符对应的属性存在,则属性值+1
  } else {
    charObj[chars] = 1;  // 该字符对应的属性不存在,则新建一个并赋值为1
  }
}
console.log(charObj);  // 输出结果 { a:1, b:2; c:3, e:1, o:4, p:1, u:1, w:1,  j:1, k:1 }
// 遍历对象
var max = 0;
var charFind = '';
for ( var key in charObj )  {
  if ( charObj[key] > max )  {
    max = charObj[key];  // 得到的是属性值
    charFind = key;          // 得到的是属性名
  }
}
console.log('最多的字符是' + charFind);  // 输出结果 最多的字符是o
console.log('出现次数是' + max + '次');   // 输出结果 出现次数是4次
  • 字符串常用操作方法
    • strObj1.concat(strObj2[, strObj3 ...]); // 拼接字符串,但实际开发中常用 + 拼接
    • strObj.substr(startIndex, length); // 提取字符串,从索引号 startIndex 开始,提取长度为 length
    • strObj.replace('被替换的字符' , '替换成什么字符');
      • 替换字符,只能替换符合要求的第一个字符,无法替换则返回-1
      • 可利用循环语句 while(str.replace(...) !== -1) 来替换一个字符串内所有符合要求的字符
    • strObj.split('分隔符'); // 把字符串转换为数组(与 arrObj.join(); 相反),可把有规律的分隔符的字符串转换为数组
    • strObj.toUpperCase(); // 转换大写
    • strObj.toLowerCase(); // 转换小写

      十一、基本数据类型与复杂数据类型


基本数据类型:string、number、boolean、undefined、null【null是对象,空对象】
① 又称 简单数据类型 或 值类型,因为在变量中存储的是【值本身】。
② 当我们想创建一个对象但不知道给什么属性或方法时,可以先赋值 null。
③ 由操作系统自动分配、释放、存放函数的参数值、局部变量的值等,操作方式类似于【栈】。
④ 变量的值就直接存放于【栈】内。

复杂数据类型:Object、Array、Date 等
① 又称 引用类型,因为在变量中存储的只是用于引用属性或方法的【地址】。
② 通过 new 关键字创建的对象(系统对象、自定义对象)都属于复杂数据类型。
③ 一般由程序员分配释放,若程序员不释放,则由垃圾回收机制来回收内存空间,操作方式类似于【堆】。
④ 变量存放于【栈】内的数据是地址,通过这个地址才可以访问【堆】内的数据(值)。

【注意】由以上描述可知,复杂数据类型与C语言的指针相似,故以后遇到把复杂数据类型作为函数的实参进行传递时,传递给形参的是地址,那么在函数内进行的数据修改是直接作用于【堆】内的数据,所以说这个修改的效果是永久的。而把基本数据类型作为函数的实参进行传递时,是在【栈】内复制一份数据后再传给形参,那么在函数内进行的数据修改仅作用于形参,对实参无效。