JavaScript基础(4)
正文:
- JavaScript函数
- JavaScript作用域
- JavaScript预解析
- JavaScript对象
- JavaScript内置对象
- JavaScript简单数据类型与复杂数据类型
JavaScript函数
- 函数的概念
- 函数的使用
- 函数的参数
- 函数的返回值
- arguments的使用
- 函数案例
- 函数的两种声明方式
函数的概念
在JS里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用虽然 for循环语句也能实现一些简单的重复操作,但是比较具有局限性,此时我们就可以使用JS 中的函数
函数的使用
函数在使用时分为两步:声明函数和调用函数
1)声明函数
// 声明函数 function 函数名() { // 函数体代码 }
function 是声明函数的关键字,必须小写
由于函数一般是为了实现某个功能才定义的,所有我们通常将函数名命名为动词,比如:getSum
2)调用函数
// 调用函数 函数名(); //通过函数名来执行函数体代码
- 调用时千万不要忘记添加小括号
注意:声明函数本身并不会执行代码,只有调用函数时才会执行函数体代码
3)函数的封装
- 函数的封装是把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口
- 简单理解:封装类似于将电脑配件整合组装到机箱中(类似快递打包)
案例:利用函数计算1-100之间的累加和
function getSum() { var sum = 0; for (var i = 1; i <= 100; i++) { sum += 1; } console.log(sum); } getSum();
函数的参数
形参和实参
在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参同样也需要传递相应的参数,这些参数被称为实参,而在调用该函数时
参数的作用:在函数内部某些值不能固定,我们可以通过参数在调用函数时传递不同的值进去
案例:利用函数求任意两个数的和
function getSum(num1, num2) { console.log(num1 + num2); } getSum(1,2);
案例:利用函数求任意两个数之间的和
function getSum(start, end) { var sum = 0; for (var i = start; i <= end; i++) { sum += i; } console.log(sum); } getSum(1, 100); getSum(1, 10);
函数形参和实参个数不匹配问题
小结:
- 函数可以带参数也可以不带参数
- 声明参数的时候,函数名括号里面的是形参,形参默认值为undefined
- 调用函数的时候,函数名括号里面的是实参
- 多个参数中间用逗号隔开
- 形参的个数可以和实参的个数不匹配,但结果不可预计,我们要尽量匹配
函数的返回值
return语句
有的时候,我们会希望函数将值返回给调用者,此时通过使用return语句就可以实现。
语法格式:
function 函数名() { return 需要返回的结果; }
- 我们函数只是实现某种功能,最终的结果需要返回给函数的调用者 函数名() 通过return实现的
- 只要函数遇到return 就把后面的结果 返回给函数的调用者 函数名() = return后面的结果
案例:利用函数求任意两个数的最大值
function getMax(num1, num2) { if (num1 > num2) { return num1; } else { return num2; } } console.log(getMax(1, 3));
方法二
function getMax(num1, num2) { return num1 > num2 ? num1 : num2; } console.log(getMax(1, 3));
案例:利用函数求任意一个数组中的最大值
function getArrMax(arr) { var max = arr[0]; for (var i = 1; i <= arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } return max; } var re = getArrMax([5, 2, 99, 101, 67, 77]); console.log(re);
函数没有return返回undefined
函数都是有返回值的
- 如果有return则返回return后面的值
- 如果没有return则返回undefined
break、continue、return的区别
- break:结束当前的循环体(如for、while)
- continue:跳出本次循环,继续执行下次循环(如for、while)
- return:不仅可以退出循环,还能够返回return语句中的值,同时还可以结束当前的函数体内的代码
arguments的使用
当我们不确定有多少个参数传递的时候,可以用arguments来获取。在JavaScript中,arguments实际上它是当前函数的一个内置对象。所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有实参。只有函数才有arguments对象,而且是每个函数都内置好了这个arguments。
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
- 具有length属性
- 按索引方式存储数据
- 不具有数组的push,pop等方法
function fn() { for (var i = 0; i < arguments.length; i++) { console.log(arguments[i]); } } fn(1, 2, 3); fn(1, 2, 3, 4, 5);
案例:利用函数求任意个数的最大值
function getMax() { // arguments = [1, 2, 3] var max = arguments[0]; for (var i = 1; i < arguments.length; i++) { if (arguments[i] > max) { max = arguments[i]; } } return max; } console.log(getMax(1, 2, 3)); console.log(getMax(1, 2, 3, 4, 5)); console.log(getMax(11, 2, 34, 555, 6, 100));
函数案例:
案例:利用函数封装方式,翻转任意一个数组
function reverse(arr) { var newArr = []; for (var i = arr.length - 1; i >= 0; i--) { newArr[newArr.length] = arr[i]; } return newArr; } var arr1 = reverse([1, 2, 3, 4, 5, 6]); console.log(arr1)
案例:冒泡排序
function sort(arr) { for (var i = 0; i < arr.length -1; i++) { for (var j = 0; j < arr.length - i - 1; j++) { if (arr[j] > arr[j+1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; } var arr1 = sort([1, 4, 2, 9, 8]); console.log(arr1);
案例:判断闰年
要求:输入一个年份,判断是否是年(闺年:能被4整除并且不能被100整数,或者能被400整除。
function isRunYear(year) { var flag = false; if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) { flag = true; } return flag; } console.log(isRunYear(2000)); console.log(isRunYear(1999));
案例:函数可以调用另一个函数
因为每个函数都是独立的代码块,用于完成特殊任务,因此经常会用到函数相互调用的情况。
function fn1() { console.log(111); fn2(); console.log('fn1'); } function fn2() { console.log(222); console.log('fn2'); } fn1();
案例:用户输入年份,输出当前年份2月份的天数
如果是闰年,则2月份是29天,如果是平年,则2月份是28天
function backDay() { var year = prompt('请您输入年份:'); if (isRunYear(year)) { alert('当前年份是闰年2月份有29天'); } else { alert('当前年份是平年2月份有28天'); } } function isRunYear(year) { var flag = false; if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) { flag = true; } return flag; } backDay();
函数的两种声明方式
- 利用函数关键字自定义函数(命名函数)
- 函数表达式(匿名函数)
// 1. 利用函数关键字自定义函数(命名函数); function fn() { } fn(); // 2. 函数表达式(匿名函数) // var 变量名 = function() {}; var fun = function (aru) { console.log('我是函数表达式'); console.log(aru); } fun('九少'); // (1) fun是变量名 不是函数名 // (2) 函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值 而 函数表达式里面存的是函数 // (3) 函数表达式也可以进行传递参数
JavaScript作用域
- 作用域
- 变量的作用域
- 作用域链
作用域
通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突
// 1. JavaScript作用域:就是代码名字(变量)在某个范围内起作用和效果,目的是为了提高程序的可靠性,更重要的是减少命名冲突 // 2. js的作用域(es6)之前:全局作用域 局部作用域 // 3. 全局作用域:整个script标签 或者是一个单独的js文件 var num = 20; console.log(num); // 4. 局部作用域(函数作用域) 在函数内部就是局部作用域 这个代码的名字只在函数内部起效果和作用 function fn() { // 局部作用域 var num = 10; console.log(num); } fn();
变量的作用域
在JavaScript中,根据作用域的不同,变量分为两种:
- 全局变量
- 局部变量
1)全局变量
在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。
- 全局变量在代码的任何位置都可以使用
- 在全局变量作用域下var声明的变量是全局变量
- 特殊情况下,在函数内不使用var声明的变量也是全局变量(不建议使用)
2)局部变量
在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
- 局部变量只能在该函数内部使用
- 在函数内部var声明的变量是局部变量
- 函数的形参实际上就是局部变量
// 1. 全局变量:在全局作用域下的变量 在全局下都可以使用 // 注意:如果在函数内部 没有声明直接赋值的变量也属于全局变量 var num = 10; console.log(num); function fn() { console.log(num); } fn(); // 2. 局部变量 在局部作用域下的变量 后者在函数内部的变量就是 局部变量 // 注意:函数的形参也可以看作是局部变量 function fun() { var num1 = 10; // num1就是局部变量 只能在函数内部使用 num2 = 20; // 全局变量 } fun(); // console.log(num1); console.log(num2);
3)从执行效率来看全局变量和局部变量
- 全局变量只有浏览器关闭的时候才会销毁,比较占内存资源
- 局部变量 当我们程序执行完毕就会销毁
4)JS没有块级作用域
现阶段JS没有 块级作用域,JS是在es6 的时候新增的块级作用域。
if (3 < 5) { var num = 10; } console.log(num); // 仍然可以在 块{} 之外使用num
作用域链
- 只要是代码,就至少有一个作用域
- 写在函数内部的局部作用域
- 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
- 根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链
// 作用域链:内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值,这种结构我们称之为作用域链。就近原则 var num = 10; function fn() { // 外部函数 var num = 20; function fun() { // 内部函数 console.log(num); } fun(); } fn();
案例:
function f1() { var num = 123; function f2() { console.log(num); } f2(); } var num = 456; f1(); // 输出结果为123
案例:
var a = 1; function fn1() { var a = 2; var b = '22'; fn2(); function fn2() { var a = 3; fn3(); function fn3() { var a = 4; console.log(a); console.log(b); } } } fn1(); // 输出结果4 22
JavaScript预解析
- 预解析
- 变量预解析和函数预解析
- 预解析案例
预解析
JavaScript代码是由浏览器中的JavaScript解析器来执行的。JavaScript解析器在运行JavaScript代码的时候分为两步:预解析和代码执行。
1)JS引擎运行JS分为两步:预解析 、代码执行
- 预解析JS引擎会把JS里面所有的var 还有function 提升到当前作用域的最前面
- 代码执行 按照代码书写的顺序从上往下执行
2)预解析分为 变量预解析(变量提升)和 函数预解析(函数提升)
- 变量提升 就是把所有的变量声明提升到当前作用域的最前面 不提升赋值操作
- 函数提升 就是把所有的函数声明提升到当前作用域的最前面 不调用函数
i. 变量提升
// 情况1 console.log(num); // undefined var num = 10; // 相当于执行了以下代码 var num; console.log(num); num = 10;
// 情况3 fun(); var fun = function () { console.log(22); } // 相当于执行了以下代码 var fun; fun(); fun = function () { console.log(22); }
注意:函数表达式 调用必须写在函数表达式的下面
ii. 函数提升
// 情况2 fn(); function fn() { console.log(11); } // 相当于执行了 function fn() { console.log(11); } fn();
预解析案例
案例1
输出结果:undefined
var num = 10; fun(); function fun() { console.log(num); var num = 20; } // 相当于执行了下面的操作 var num; function fun() { var num; console.log(num); num = 20; } num = 10; fun();
案例2
输出结果:
undefined
20
var num = 10; function fn() { console.log(num); var num = 20; console.log(num); } fn(); // 相当于以下代码 var num; function fn() { var num; console.log(num); num = 20; console.log(num); } num = 10; fn();
案例3
输出结果:
undefined
9
var a = 18; f1(); function f1() { var b = 9; console.log(a); console.log(b); var a = '123'; } // 相当于以下代码 var a; function f1() { var b; var a; b = 9; console.log(a); console.log(b); a = '123'; } a = 18; f1();
案例4
输出结果:
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直接赋值,没有var声明,当全局变量看 // 集体声明 var a = 9, b = 9 , c = 9; console.log(a); console.log(b); console.log(c); } f1(); console.log(c); console.log(b); console.log(a);
JavaScript对象
- 对象
- 创建对象的三种方式
- new关键字
- 遍历对象属性
对象
现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一张网页、一个与远程服务器的连接也可以是“对象”。
在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组函数等。
对象是由属性和方法组成的。
- 属性:事物的特征,在对象中用属性来表示(常用名词)
- 方法:事物的行为,在对象中用方法来表示(常用动词)
1)为什么需要对象
保存一个值,可以使用变量,保存多个值(一组值)时,可以使用数组。如果要保存一个人的完整信息呢?
例如:将‘张三丰’的个人信息保存在数组中的方式为:
var arr = ['张三丰', '男', 128, 154];
JS中的对象表达结构更清晰,更强大。张三丰的个人信息在对象中的表达结构如下:
张三丰.姓名 = '张三丰'; 张三丰.性别 = '男'; 张三丰.年龄 = 128; 张三丰.身高 = 154;
person.name = '张三丰'; person.sex = '男'; person.age = 128; person.height = 154;
创建对象的三种方式
在JavaScript中,现阶段我们可以采用三种方式创建对象(object);
- 利用字面量创建对象
- 利用new Object创建对象
- 利用构造函数创建对象
1)利用对象字面量创建对象
对象字面量:就是花括号{}里面包含了表达这个具体事务(对象)的属性和方法
{}里面采取键值对的形式表示
- 键:相当于属性名
- 值:相当于属性值,可以是任意类型的值(数字类型、字符串类型、布尔类型、函数类型等)
对象的调用
- 对象里面的属性调用:对象.属性名,这个点,就理解为“的”
- 对象里面属性的另一种调用方式:对象['属性名'],注意方括号里面的属性必须加引号,我们后面会用
- 对象里面的方法调用:对象.方法名(),注意这个方法名字一定加括号
var obj = { uname: '张三丰', age: 18, sex: '男', sayHi: function() { console.log('hi~'); } } // (1) 里面的属性或者方法我们采取键值对的形式 键 属性名 : 值 属性值 // (2) 多个属性或者方法中间用逗号隔开 // (3) 方法冒号后面跟的是一个匿名函数 // 2. 使用对象 // (1) 调用对象的属性 我们采取 对象名.属性名 console.log(obj.uname); // (2) 调用属性还有一种方法 对象名['属性名'] console.log(obj['age']); // (3) 调用对象的方法 sayHi 对象名.方法名 obj.sayHi();
变量、属性、函数、方法总结
变量和属性的相同点:它们都是用来存储数据的
不同点:
- 变量单独声明并赋值,使用的时候直接写变量名 单独存在
- 属性 在对象里面的不需要声明的,使用的时候必须是 对象.属性
函数和方法的相同点:都是实现某种功能 做某件事
不同点:
- 函数是单独声明并且调用的 函数名() 单独存在的
- 方法在对象里面调用的时候 对象.方法()
2)利用new Object创建对象
var obj = new Object(); // 创建了一个空的对象 obj.uname = '张三丰'; obj.age = 18; obj.sex = '男'; obj.sayHi = function () { console.log('hi~'); } // (1) 我们是利用 等号 = 赋值 的方法 添加对象的属性和方法 // (2) 每个属性和方法之间 分号隔开 console.log(obj.uname); console.log(obj['sex']); obj.sayHi();
3)利用构造函数创建对象
构造函数:是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
语法格式:
function 构造函数名() { this.属性 = 值; this.方法 = function() {} }
new 构造函数名()
function Star(uname, age, sex) { this.name = uname; this.age = age; this.sex = sex; this.sing = function(sang) { console.log(sang); } } var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象 // console.log(typeof ldh); console.log(ldh.name); console.log(ldh['sex']); ldh.sing('冰雨'); var zxy = new Star('张学友', 19, '男'); console.log(zxy.name); console.log(zxy.age); zxy.sing('李香兰');
- 构造函数名 字首字母要大写
- 构造函数不需要return 就可以返回结果
- 调用构造函数 必须使用new
- 只要new Star() 调用函数就创建一个对象
- 属性和方法前面必须添加 this
4)构造函数和对象
构造函数:如Stars(),抽象了对象的公共部分,封装到了函数里面,它泛指某一大类(class)。
创建对象:如 new Stars(),特指某一个,通过new关键字创建对象的过程我们称之为对象实例化
// 构造函数和对象 // 1. 构造函数 明星 泛指的某一大类 它类似于java语言里面的 类(class) function Star(uname, age, sex) { this.name = uname; this.age = age; this.sex = sex; this.sing = function(sang) { console.log(sang); } } // 2. 对象 特指 是一个具体的事物 刘德华 == {name: '刘德华', age: 18, sex: '男', sing: ƒ} var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象 console.log(ldh); // 3. 我们利用构造函数创建对象的过程我们也称为对象的实例化
new关键字
new在执行时会做四件事情:
- 在内存中创建一个新的空对象
- 让this指向这个新的对象
- 执行构造函数里面的代码,给这个新对象添加属性和方法
- 返回这个新对象(所以构造函数里面不需要return)
遍历对象属性
for...in语句用于对数组或者对象的属性进行循环操作。
// 遍历对象 var obj = { name: 'pink老师', age: 18, sex: '男', fn: function() {} } for (var k in obj) { console.log(k); // k 变量输出 得到的是 属性名 console.log(obj[k]); // obj[k] 得到的是 属性值 }
小结:
对象可以让代码结构更加清晰
- 对象复杂数据类型Object
- 本质:对象就是一组无序的相关属性的方法的集合
- 构造函数泛指某一大类,比如苹果,不管是红苹果还是绿苹果,都统称为苹果
- 对象实例特指一个事物,比如苹果
- for ... in语句用于对对象的属性进行循环操作
JavaScript内置对象
- 内置对象
- 查文档
- Math对象
- 日期对象
- 数组对象
- 字符串对象
内置对象
JavaScript中的对象分为3种:自定义对象、内置对象、浏览器对象
- 前面两种对象是JS基础内容,属于ECMAScript;第三个浏览器对象属于我们JS独有的,后面JS API讲解。
- 内置对象就是指JS语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能(属性和方法)
- 内置对象最大的优点就是帮助我们快速开发
- JavaScript提供了多个内置对象:Math、 Date、Array、String等
查文档
MDN
Mozilla开发者网络(MDN)提供了有关开放网络技术(Open Web)的信息,包括HTML、CSS和万维网及HTML5应用的API。
MDN: https://developer.mozilla.org/zh-CN
如何学习对象中的方法
- 查阅该方法的功能
- 查看里面参数的意义和类型
- 查看返回值的意义和类型
- 通过demo进行测试
Math对象
Math 对象不是构造函数,它具有数学常数和函数的属性和方法。跟数学相关的运算(求绝对值,取整、最大值等)可以使用Math中的成员。
// Math数学对象,不是一个构造函数,所以我们不需要new来调用,而是直接使用里面的属性和方法即可 console.log(Math.PI); // 3.141592653589793 console.log(Math.max(1, 99, 3)); // 99 console.log(Math.max(-1, -10)); // -1 console.log(Math.max(1, 99, '九少')); // NaN console.log(Math.max()); // -Infinity
案例:封装自己的数学对象
利用对象封装自己的数学对象里面有PI最大值和最小值
var myMath = { PI: 3.1415926, max: function () { var max = arguments[0]; for (var i = 1; i < arguments.length; i++) { if (arguments[i] > max) { max = arguments[i]; } } return max; }, min: function () { var min = arguments[0]; for (var i = 1; i < arguments.length; i++) { if (arguments[i] < min) { min = arguments[i]; } } return min; } } console.log(myMath.PI); console.log(myMath.max(1, 5, 9)); console.log(myMath.min(1, 5, 9));
Math的几个常见方法
// 1. 绝对值方法 console.log(Math.abs(-1)); // 1 console.log(Math.abs('-1')); // 隐式转换 会把字符串型 -1 转换为数字型 console.log(Math.abs('九少')); // NaN // 2. Math.floor() 地板 向下取整 往小了取值 console.log(Math.floor(1.1)); // 1 console.log(Math.floor(1.9)); // 1 // 3. Math.ceil() ceil 天花板 向上取整 往大了取值 console.log(Math.ceil(1.1)); // 2 console.log(Math.ceil(1.9)); // 2 // 4. Math.round() 四舍五入 console.log(Math.round(1.1)); // 1 console.log(Math.round(1.5)); // 2 console.log(Math.round(-1.1)); // -1 console.log(Math.round(-1.5)); // 这个结果是 -1 ,其他数字都是四舍五入,但是.5特殊,它往大了取
// 1. Math对象随机数方法 random()返回一个随机的小数 0 <= x < 1 // 2,这个方法里面不跟参数 console.log(Math.random()); // 返回0~1之间的一个数 // 3. 得到两个数之间的随机整数并且包含这2个整数 // Math.floor(Math.random() * (max - min + 1)) + min; function getRandom(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } console.log(getRandom(1, 10)); // 随机点名 var arr = ['张三', '张三丰', '李四', '李思思']; console.log(arr[getRandom(0, 3)]); console.log(arr[getRandom(0, arr.length - 1)]);
案例:猜数字游戏
程序随机生成一个1~10之间的数字,并让用户输入一个数字
- 如果大于该数字,就提示,数字大了,继续猜
- 如果小于该数字,就提示数字小了,继续猜
- 如果等于该数字,就提示猜对了,结束程序
案例分析:
- 随机生成一个1~10的整数,我们需要用到Math.random()方法
- 需要一直猜到正确为止,所有需要一直循环,使用while循环
- 核心算法:使用if else if多分支语句来判断大于、小于、等于
function getRandom(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } var random = getRandom(1, 10); while (true) { var num = prompt('你来猜? 输入1~10之间的一个数字'); if (num > random) { alert('你猜大了'); } else if (num < random) { alert('你猜小了'); } else { alert('恭喜你猜对了!'); break; // 退出整个循环结束程序 } }
日期对象
- Date对象和Math对象不一样,他是一个构造函数,所以我们需要实例化后才能使用
- Date实例用来处理日期和时间
Date()方法的使用
1)获取当前时间必须实例化
var now = new Date(); console.log(now);
2)Date()构造函数的参数
如果括号里面有时间,就返回参数里面的时间。例日期格式字符串为“2019-5-1’,可以写成newDate('2019-5-1')或者 new Date('2019/5/1')
日期格式化
我们想要2019-8-88:8:8格式的日期,要怎么办?
需要获取日期指定的部分,所以我们要手动的得到这种格式
var date = new Date(); console.log(date.getFullYear()); // 返回当前日期的年 console.log(date.getMonth() + 1); // 月份 返回的月份小1个月 记得月份+1 console.log(date.getDate()); // 返回的是 几号 console.log(date.getDay()); // 周一返回的是1 周六返回的是6 但是周日返回的是0 // 输出一个 xxxx年 x月 x日 星期x var year = date.getFullYear(); var month = date.getMonth() + 1; var dates = date.getDate(); var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; var day = date.getDay(); console.log('今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day]);
// 格式化日期 时分秒 var date = new Date(); console.log(date.getHours()); console.log(date.getMinutes()); console.log(date.getSeconds()); // 要封装一个函数返回当前的时分秒 格式 08:08:08 function getTime() { var time = new Date(); var h = time.getHours(); h = h < 10 ? '0' + h : h; var m = time.getMinutes(); m = m < 10 ? '0' + m : m; var s = time.getSeconds(); s = s < 10 ? '0' + s : s; return h + ':' + m + ':' + s; } console.log(getTime());
获取日期的总的毫秒形式
Date对象是基于1970年1月1日(世界标准时间)起的毫秒数。我们经常利用总的毫秒数来计算时间,因为它更精确。
// 1.通过valueOf() getTime() var date = new Date(); console.log(date.valueOf()); // 就是我们现在时间距离1970.1.1总的毫秒数 console.log(date.getTime()); // 2.简单的写法(常用写法) var date1 = +new Date(); // +new Date() 返回的就是总的毫秒数 console.log(date1); // 3. H5 新增的 获得总的毫秒数 console.log(Date.now());
案例:倒计时
- 核心算法:输入的时间减去现在的时间就是剩余的时间,即倒计时,但是不能拿着时分秒相减,比如05分减去25分,结果会是负数的。
- 用时间戳来做。用户输入时间总的毫秒数减去现在时间的总的毫秒数,得到的就是剩余时间的毫秒数。
- 把剩余时间总的毫秒数转换为天、时、分、秒(时间戳转换为时分秒)
function countDown(time) { var nowTime = +new Date(); // 返回的是当前时间总的毫秒数 var inputTime = +new Date(time); // 返回的是用户输入时间总的毫秒数 var times = (inputTime - nowTime) / 1000; // time是剩余时间总的秒数 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('2023-8-10 18:00:00'));
数组对象
1)数组对象的创建
创建数组对象的两种方式
- 字面量方式
- new Array()
// 1. 利用数组字面量 var arr = [1, 2, 3]; console.log(arr[0]); // 2. 利用new Array() // var arr1 = new Array(); // 创建了一个空的数组 // var arr1 = new Array(2); // 这个2 表示数组的长度为2 里面有2个空的数组元素 var arr1 = new Array(2, 3); // 等价于[2, 3] 这样写表示 里面有2个数组元素 是 2 和 3 console.log(arr1);
2)检测是否为数组
// 翻转数组 function reverse(arr) { if (arr instanceof Array){ // instanceof 运算符 它可以用来检测是否为数组 var newArr = []; for (var i = arr.length - 1; i >= 0; i--) { newArr[newArr.length] = arr[i]; } return newArr; } else { return 'error 这个参数要求必须是数组格式 [1, 2, 3]' } } console.log(reverse([1, 2, 3])); console.log(reverse(1, 2, 3)); // Array.isArray(参数); var arr = []; console.log(Array.isArray(arr));
3)添加删除数组元素的方法
// 1. push() 在数组末尾 添加一个或者多个数组元素 push推 // (1) push()参数直接写 数组元素九可以了 // (2) push完毕,返回的结果是新数组的长度 // (3) 原数组也会发生变化 var arr = [1, 2, 3]; arr.push(4, '九少'); console.log(arr); console.log(arr.push(4, '九少')); // 5+2=7 // 2. unshift 在数组的开头 添加一个或多个数组元素 console.log(arr.unshift('red', 'purple')); console.log(arr); // 3. pop() 它可以删除数组的最后一个元素 console.log(arr.pop()); console.log(arr); // 4. shift() 它可以删除数组的第一个元素 console.log(arr.shift()); console.log(arr);
案例:筛选数组
有一个包含工资的数组[1500,1200,2000,2100,1800],要求把数组中工资超过2000的删除,剩余的放到新数组里面。
var arr = [1500, 1200, 2000, 2100, 1800]; var newArr = []; for (var i = 0; i < arr.length; i++) { if (arr[i] < 2000) { newArr.push(arr[i]); } } console.log(newArr);
案例:数组排序
// 1. 翻转数组 var arr = ['pink', 'red', 'blue']; arr.reverse(); console.log(arr); // 2. 数组排序(冒泡排序) var arr1 = [3, 4, 7, 1]; // [1, 13, 4, 7, 77]无法排序 arr1.sort(); console.log(arr1); var arr2 = [1, 13, 4, 7, 77]; arr2.sort(function (a, b) { // return a - b; 升序的顺序排序 return b - a; // 降序的顺序排序 }) console.log(arr2);
4)数组索引的方法
// 返回数组元素索引号方法 indexOf(数组元素) 作用就是返回该数组元素的索引号 // 它只返回第一个满足条件的索引号 // 它如果在该数组里面找不到元素,则返回的是-1 // var arr = ['red', 'green', 'blue', 'pink', 'blue']; var arr1 = ['red', 'green', 'pink']; console.log(arr1.indexOf('blue')); var arr2 = ['red', 'green', 'blue', 'pink', 'blue']; console.log(arr2.lastIndexOf('blue'));
案例:数组去重
有一个数组['c', 'a', 'z', 'a', 'x', 'a', 'x', 'c', 'b'],要求去除数组中重复的元素。
案例分析:
- 目标:把旧数组里面不重复的元素选取出来放到新数组中,重复的元素只保留一个,放到新数组中去重。
- 核心算法:我们遍历日数组,然后拿着旧数组元素去查询新数组,如果该元素在新数组里面没有出现过,我们就添加否则不添加。
- 我们怎么知道该元素没有存在?利用新数组indexOf(数组元素)如果返回时-1 就说明新数组里面没有改元素
function unique(arr) { var newArr = []; for (var i = 0; i < arr.length; i++) { if (newArr.indexOf(arr[i]) === -1) { newArr.push(arr[i]); } } return newArr; } var demo = unique(['c', 'a', 'z', 'a', 'x', 'a', 'x', 'c', 'b']); console.log(demo);
案例:数组转字符串
// 1. toString() 将数组转化为字符串 var arr = [1, 2, 3]; console.log(arr.toString()); // 1, 2, 3 // 2. join(分隔符) var arr1 = ['green', 'blue', 'pink']; console.log(arr1.join()); console.log(arr1.join('-'));
字符串对象
1) 基本包装类型
为了方便操作基本数据类型,JavaScript还提供了三个特殊的引用类型:String、Number和Boolean。
基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。
// 基本包装类型 var str = 'andy'; console.log(str.length); // 对象才有属性和方法 即复杂数据类型才有属性和方法 // 简单数据类型为什么有length属性呢? // 基本包装类型:就是把简单数据类型包装成为了 复杂数据类型 // (1) 把简单数据类型包装为复杂数据类型 var temp = new String('andy'); // (2) 把临时变量的值给 str str = temp; // (3)销毁这个临时变量 temp = null;
2)字符串的不可变
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
var str = 'abc'; str = 'hello'; // 当重新给str赋值的时候,常量'abc'不会被修改,依然在内存中 // 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变 // 由于字符串的不可变,在大量拼接字符串的时候会有效率问题 var str1 = ''; for (var i = 0; i < 100000; i++) { str += i; } console.log(str); // 这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间
3)根据字符返回位置
字符串所有的方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串。
// 根据字符返回位置 str.indexOf('要查找的字符', [起始的位置]) var str = '只要肝不死那就继续肝'; console.log(str.indexOf('肝')); console.log(str.indexOf('肝', 3)); // 索引是从3的位置开始往后查找
案例:返回字符的位置
查找字符串"abcoefoxyozzopp"中所有o出现的位置以及次数。
- 核心算法:先查找第一个o出现的位置
- 然后只要indexOf返回的结果不是-1就继续往后查找
- 因为indexOf只能查找到第一个,所以后面的查找,利用第二个参数,当前索引加1,从而继续查找
var str = "abcoefoxyozzopp"; var index = str.indexOf('o'); var num = 0; while (index !== -1) { console.log(index); num++; index = str.indexOf('o', index + 1); // 从index+1开始往后找 } console.log('o出现的次数是:' + num);
4)根据位置返回字符
// 1. charAt(index) 根据位置返回字符串 var str = 'andy'; console.log(str.charAt(3)); // 遍历所有的字符 for (var i = 0; i < str.length; i++) { console.log(str.charAt(i)); } // 2. charCodeAt(index) 返回相应索引号的字符ASCII值 目的:判断用户按下了那个键 console.log(str.charCodeAt(0)); // 97 // 3. str[index] H5新增的 console.log(str[0]); // a
案例:返回字符位置
判断一个字符串abcoefoxyozzopp'中出现次数最多的字符,并统计其次数
- 核心算法: 利用 charAt()遍历这个字符串
- 把每个字符都存储给对象, 如果对象没有该属性,就为1,如果存在了就 +1
- 遍历对象,得到最大值和该字符
var str = 'abcoefoxyozzopp'; var o = {}; for (var i = 0; i < str.length; i++) { var chars = str.charAt(i); // chars 是字符串的每一个字符 if (o[chars]) { // o[chars] 得到是属性值 o[chars]++; } else { o[chars] = 1; } } console.log(o); // 遍历对象 var max = 0; var ch = ''; for (var k in o) { if (o[k] > max) { max = o[k]; ch = k; } } console.log('最多的字符是' + ch);
5)字符串操作方法
// 1. concat('字符串1', '字符串2'....) var str = 'andy'; console.log(str.concat('red')); // 2. substr('截取的起始位置', '截取第几个字符'); var str1 = '改革春风吹满地'; console.log(str1.substr(2, 2)); // 第一个2是索引号的2 第二个2是取几个字符
// 3.替换字符 replace('被替换的字符', '替换为的字符') 它只会替换第一个字符 var str = 'andy'; console.log(str.replace('a', 'b')); // 有一个字符串’abcoefoxyozzopp‘要求把里面所有的 o 替换为 * var str1 = 'abcoefoxyozzopp'; while (str1.indexOf('o') !== -1) { str1 = str1.replace('o', '*'); } console.log(str1) // 4. 字符转换为数组 split('分隔符') var str2 = 'red,pink,blue'; console.log(str2.split(',')); var str3 = 'red&pink&blue'; console.log(str3.split('&'));
JavaScript简单数据类型与复杂数据类型
- 简单类型与复杂类型
- 堆和栈
- 简单类型的内存分配
- 复杂类型的内存分配
- 简单类型传参
- 复杂类型传参
简单类型与复杂类型
简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型
- 值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型
- string , number, boolean, undefined, null
- 引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型
- 通过new 关键字创建的对象(系统对象、自定义对象),如Object、Array、Date等
堆和栈
堆栈空间分配区别:
1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
简单数据类型存放到栈里面
2、堆(操作系统):存储复杂类型(对象)一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
复杂数据类型存放到堆里面
注意:JavaScript中没有堆栈的概念,通过堆栈的方式,可以让大家更容易理解代码的一些执行方式,便于将来学习其他语言;
简单类型的内存分配
- 值类型(简单数据类型):string,number,boolean,undefined,null
- 值类型变量的数据直接存放在变量(栈空间)中
复杂类型的内存分配
- 引用类型(复杂数据类型):通过new 关键字创建的对象(系统对象、自定义对象)如ObiectArray、Date等
- 引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中
简单数据类型传参
函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。
function fn(a) { a++; console.log(a); } var x = 10; fn(x); console.log(x);
复杂类型传参
函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。
// 复杂数据类型传参 function Person(name) { this.name = name; } function f1(x) { console.log(x.name) // 2. 这个输出什么? 刘德华 x.name = '张学友'; console.log(x.name); // 3. 这个输出什么? 张学友 } var p = new Person("刘德华"); console.log(p.name); // 1. 这个输出什么?刘德华 f1(p); console.log(p.name); // 4. 这个输出什么?张学友
学习来自:https://www.bilibili.com/video/BV1ux411d75J?p=190&spm_id_from=pageDriver&vd_source=8bc93faaf8c822f06e3c15f0c4d2e6d3