知识点补充
arrar.map
给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
var numbers = [1, 4, 9];
var doubles = numbers.map(function(num) {
return num * 2;
});
// doubles数组的值为: [2, 8, 18]
// numbers数组未被修改: [1, 4, 9]
变量
/前情提要/
/* shift+option+A */
// command+/
alert('弹出框') | console.log('输出') |cosole.error('错误') | console.warn('警告') | prompt('提示信息', '默认值')|document.write('在页面中输出')
*变量命名:字母|_|$|数字(不能开头)
*变量声明未赋值 undefined
数字类型
// 蓝色
*整型/浮点型
*八进制:010---8^1x1+8^0x0=8
*十六进制:0xab---16^1x10+16^0x11=171
*最值:Number.MAX_VALUE | Number.MIN_VALUE(无限接近于0,比它再小就是0)
*无穷:Infinity|- Infinity
*NaN:isNaN(1) -- false
/*里面如果是 纯数字 ,返回的结果就是false*/
- isNaN('123');//false
- isNaN('123px');//true
- NaN==NaN;// false -NaN不跟任何数相等
var age=prompt('how old are you');
// prompt拿到的结果是字符串,这时用isNaN判断就不用再去进行隐式转换了
isNaN(age);
字符串
// 黑色
- 其他类型的数据和 字符类型 相加 输出值都是 字符类型
- prompt 和 input 接收到的数据 都是字符串类型
字符串恒定性
字符串恒定性 : 字符串不可以被修改
* 字符串所有的api都不会修改字符串自身,而是得到一个新字符串
* 开发中一般声明一个变量去接收字符串方法的返回值
误区解读: 不要把字符串恒定性 与 变量赋值搞混淆
字符串恒定性 : 主要是针对字符串的api,这些api不会修改原字符串而是产生新的字符串
变量赋值 : 修改变量中存储的数据,跟恒定性没关系
let str = '我爱你';
//这行代码并不会修改str本身,而是产生一个修改后的新字符串
let res = str.replace('爱','宣');
console.log( res );//我宣你
console.log( str );//我爱你
//字符串是不可以修改的
str[3] = '宣';
console.log( str );//'我爱你'
str = 'abc';
console.log( str );;;//abc 这个是变量的重新赋值,跟字符串恒定性没关系
转义符
| 转义符 | 解释说明 |
|---|---|
| \n | 换行符,n 是 newline 的意思 |
| \ \ | 斜杠 \ |
| ' | ' 单引号 |
| " | ”双引号 |
| \t | tab 缩进 |
| \b | 空格 ,b 是 blank 的意思 |
其他类型
*boolean
//暗蓝色
true参与运算当1来看
false参与运算当0来看
*undefined
//浅灰色
undefined和 数字 相加 最后结果是NaN
undefined和 字符 相加 最后结果是拼接起来的字符串
*null
//浅灰色
null和 数字 相加 最后结果是还是那个数字
null和 字符 相加 最后结果是拼接起来的字符串
'' == null;// true,两个不同类型判断时,会将两方转换为数字,''为0,null为0,所以相等
判断类型
var num=10;
console.log(typeof num); //number
var nl=null;
console.log(typeof nl);//object 空对象
// typeof获取到的结果是以字符串的形式展示的
var a=10;
console.log(typeof (typeof a));//string
console.log(typeof 10+10)//number10
数据类型转换
转换为字符串
- 1.隐式转换(+)- 常用
var str=num + ''
- 2.toString()
// 不能转换undefined和null
var num=10;
// 这里的toString前面必须跟的是变量名,不能直接跟变量值,(10.toString❌)
var str=num.toString();
- 3.String() - 强制转换(不常用)
// 什么都可以转换
var str=String(num);
转换为数字类型
- 1.隐式转换( - * / % )
// % 比自己的大的数 余自己
10%11 = 10;
⚠️console.log(+num);
consolo.log(-(-num));
- 2.parseInt(string)
//前面是数字,就能转换非数字之前的数字
parseInt('120px34');//120
//第一个不是数字,就不能转换
parseInt('rem120px');//NaN
parseInt('true');//NaN
parseInt(true);//NaN
- 3.parseFloat(string)
//除了保留小数,其他都是parseInt一样
- 4.Number() - 强制转换 (不常用)
Number('true');//NaN
Number(true);//1
Number('120px');//NaN
转化为布尔型
Boolean();
//代表 空、否定的值 会被转换为false
- ''|0|NaN|null|undefined|false
- 注意:' '里面有空格,则为true
运算符
递增和递减
//前置递增:++num
- 先自加,后返回值
//后置递增:num++
- 先返回原值,后自加
/*递减同理*/
比较运算符
== 如果两方不一样,会将双方都转换为数字型
== 只判断值是否相等(存在隐式转换)
=== 值与数据类型都必须相等
console.log(18 == '18');//true
console.log(18 === '18'); //false
逻辑运算符
短路运算(逻辑中断)
// 短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值;
1.逻辑与:表达式1 && 表达式2
- 如果第一个表达式的值为真,则返回表达式2
- 如果第一个表达式的值为假,则返回表达式1
console.log( 123 && 456 );// 456
console.log( undefined && null );//undefined
2.逻辑或:表达式1 || 表达式2
- 如果第一个表达式的值为真,则返回表达式1
- 如果第一个表达式的值为假,则返回表达式2
console.log( 123 || 456 );// 123
console.log( 'undefined' || '' );//''
运算符优先级
- 小括号 ()
- 一元运算符 ++ -- !
- 算数运算符 先* / % 后 + -
- 关系运算符 > >= < <=
- 相等运算符 == != === !==
- 逻辑运算符 先 && 后 ||
- 赋值运算符 =
- 逗号运算符 ,
流程控制
if else if 语句(多分支语句)
// 适合于检查多重条件。
if (条件表达式1) {
语句1;
} else if (条件表达式2) {
语句2;
} else if (条件表达式3) {
语句3;
....
} else {
// 上述条件都不成立执行此处代码
}
三元表达式
表达式1 ? 表达式2 : 表达式3;
switch分支流程控制
switch( 表达式 ){
case value1:
// 表达式 等于 value1 时要执行的代码
break;
case value2:
// 表达式 等于 value2 时要执行的代码
break;
default:
// 表达式 不等于任何一个 value 时要执行的代码
}
**注意:
- switch 表达式的值会与结构中的 case 的值做 (===)比较
- 执行case 里面的语句时,如果没有break,则继续执行下一个case里面的语句。
//成绩判断例子:
var grade=prompt('输入你的成绩');
switch( parseInt(grade/10) ){
case 10:
case 9:
alert('A');
break;
case 8:
alert('B');
break;
case 7:
alert('C');
break;
case 6:
alert('D');
break;
default:
alert('不及格');
}
//method2: case的值与switch的值进行true,false比较
var grade=prompt('输入你的成绩');
switch( true ){
case grade>=90:
alert('A');
break;
case grade>=80:
alert('B');
break;
case grade>=70:
alert('C');
break;
case grade>=60:
alert('D');
break;
default:
alert('不及格');
}
循环
for
for(){} 两个括号里面的变量为同一个块级作用域
for(初始化变量; 条件表达式; 操作表达式 ){
//循环体
}
双重for循环
// 打印五行五列星星
var star = '';
for (var j = 1; j <= 5; j++) {
for (var i = 1; i <= 5; i++) {
star += '☆'
}
// 每次满 5个星星 就 加一次换行
star += '\n'
}
console.log(star);
#核心逻辑:
1.内层循环负责一行打印五个星星
2.外层循环负责打印五行
while循环
while (条件表达式) 里面的条件表达式,必须是bool,如果不是,会自动隐式转换成 bool值
while(){} 两个括号里面的变量 为不同的块级作用域
// Boolean() 转false -> 0,NaN,'',null,undefined,false
// 转true -> 其他都转为true
while (条件表达式) {
// 循环体代码
}
do-while循环
do {
// 循环体代码 - 条件表达式为 true 时重复执行循环体代码
} while(条件表达式);
//至少会执行一次
continue、break
- continue 关键字用于立即跳出本次循环,继续下一次循环(本次循环体中 continue 之后的代码就会少执行一次)。
- break 关键字用于立即跳出 当前 的整个循环(循环结束)。
数组
创建数组
- 1.利用 new 创建数组
var 数组名 = new Array() ;
var arr = new Array(); // 创建一个新的空数组
- 2.利用数组字面量创建数组
//1. 使用数组字面量方式创建空的数组
var 数组名 = [];
//2. 使用数组字面量方式创建带初始值的数组
var 数组名 = ['小白','小黑','大黄','瑞奇'];
- 注意:如果访问时数组没有和索引值对应的元素,则得到的值是undefined
数组长度
#arr.length
- 数组的length属性可以被修改
- 如果设置的length属性值大于数组的元素个数,则会在数组末尾出现空白元素;
- 如果设置的length属性值小于数组的元素个数,则会把超过该值的数组元素删除
//在数组的末尾插入新元素
数组[ 数组.length ] = 新数据;
遍历数组
let arr=['London','CD','German','India'];
// 元素 of 数组
for(let city of arr){
let str+=`${city}#`
}
console.log(str);// London#CD#German#India
函数
函数可以有返回值,也可以没有返回值
return
- 在使用 return 语句时,函数会停止执行,并返回指定的值
- 如果函数没有 return ,返回的值是 undefined
#break ,continue ,return 的区别
- break :结束当前的循环体(如 for、while)
- continue :跳出本次循环,继续执行下次循环(如 for、while)
- return :不仅可以退出循环,还能够返回 return 语句中的值,同时还可以结束当前的函数体内的代码
arguments
是当前函数的一个内置对象,存储了传递的所有实参
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
- 具有 length 属性
- 按索引方式储存数据
- 不具有数组的 push , pop 等方法
注意:在函数内部使用该对象,用此对象获取函数调用时传的实参。
函数的两种声明方式
-
自定义函数方式(命名函数)
/*利用函数关键字 function 自定义函数方式*/ // 声明定义方式 function fn() {...} // 调用 fn(); - 因为有名字,所以也被称为命名函数 - 调用函数的代码既可以放到声明函数的前面,也可以放在声明函数的后面 -
函数表达式方式(匿名函数)
// 这是函数表达式写法,匿名函数后面跟分号结束 var fn = function(){...}; // 调用的方式,函数调用必须写到函数体下面fn(); - 因为函数没有名字,所以也被称为匿名函数 - 这个fn 里面存储的是一个函数 - 函数表达式方式原理跟声明变量方式是一致的 - 函数调用的代码必须写到函数体后面
作用域
作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
JavaScript(es6前)中的作用域有两种:全局作用域局部作用域(函数作用域)
1.全局作用域
- 作用于所有代码执行的环境(整个 script 标签内部)或者一个独立的 js 文件。
2.局部作用域
- 作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系,所以也称为函数作用域。
变量的作用域
1.全局变量
- 在全局作用域下声明的变量
- 注意:在函数内部,没有声明直接赋值的变量 也属于全局变量
2.局部变量
- 在局部作用域下声明的变量
- 注意:函数的形参也可以看作局部变量
function fun(aru){
var num1=10;//num1是局部变量,只能在函数内部使用
num2=20;//全局变量
}
fun();
console.log(num1);// num1 is not defined
console.log(num2);// 20
consolo.log(aru);//aru is not defined
3.从执行效率来看全局变量和局部变量
- 全局变量 只有在浏览器关闭时才会销毁,比较占内存资源
- 局部变量 当函数程序执行完毕就会销毁,比较节约内存资源
块级作用域(ES6新增)
- 块作用域由 { } 包括。
- 在其他编程语言中(如 java、c等),在 if 语句、循环语句中创建的变量,仅仅只能在本 if 语句、本循环语句中使用
- Js中没有块级作用域(在ES6之前)
if(true){
var num = 123;
console.log(123); //123
}
console.log(123); //123
作用域链
内部函数可以访问外部函数变量,用 链式一层一层向上查找 决定哪些数据能被内部函数访问,就称作作用域链。
采取就近原则的方式来查找变量最终的值。
// 案例1:
function f1() {
var num = 123;
function f2() {
console.log( num );
}
f2();
}
var num = 456;
f1();//123
// 案例2:
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); //a的值 4
console.log(b); //b的值 '22'
}
}
}
fn1();
预解析
JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析 和 代码执行。
-
预解析:在当前作用域下, JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中进行提前声明或者定义。
-
代码执行: 从上到下执行JS语句。
预解析会把变量和函数的声明在代码执行之前执行完成。
变量预解析(变量提升)
- 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。
- **变量提升只提升声明,不提升赋值**
console.log(num); // undefined
var num = 10;
//相当于
var num;
console.log(num); //所以是undefined
num = 10;
函数预解析(函数提升)
- 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。
1.命名函数
fn();//'打印'
function fn() {
console.log('打印');
}
//相当于
function fn() {
console.log('打印');
}
fn();
//所以是可以正常调用的
2.匿名函数
fun();//报错 fn is not a function
var fun=function(){
console.log(22);
}
//相当于
var fun;
fun();//此时只有fun变量,没有fun函数,所以会报错
fun=function(){
console.log(22);
}
//这也解释了,为什么匿名函数的调用必须写在下面
案例
/案例1/
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
//var a=9;b=9;c=9;bc当全局变量看
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
//相当于
function f1() {
var a;
a=9;
b=9;
c=9;
console.log(a);//9
console.log(b);//9
console.log(c);//9
}
f1();
b=9;
c=9;
console.log(c);//9
console.log(b);//9
console.log(a);// a is not defined
/案例2/
function demo() {
function demoSon() {
num = 3;
//在函数内部未声明直接赋值的变量,会一层一层的往外找 外部是否有这个变量的声明,如果没有找到,则该变量可以直接在外层使用;直到在某层找到了有关于该变量的声明,此时,他就能作用的范围就只到该层了,不能到更上一层作用了。
//且该变量要在该函数调用之后才能使用
}
var num = 2;
demoSon();
console.log(num);
}
demo();
console.log(num);
//相当于
function demo() {
function demoSon() {
num = 3;
}
var num;
num = 2;
demoSon();
num = 3;
console.log(num);//3
}
demo();
console.log(num);//num is not defined
对象
1.什么是对象?
- 在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
- 对象是由属性和方法组成的。
2.为什么需要对象?
- 用数组保存数据的缺点是:数据只能通过索引值访问,开发者需要清楚所有的数据的排行才能准确地获取数据,而当数据量庞大时,不可能做到记忆所有数据的索引值。
- 对象中为每项数据设置了属性名称,可以访问数据更语义化,数据结构清晰,表意明显,方便开发者使用。
- JS中的对象表达结构更清晰,更强大。
创建对象的三种方式
1.使用对象字面量创建对象{}
var star = {
name : 'pink',
age : 18,
sex : '男',
sayHi : function(){
alert('大家好啊~');
}
};
1.访问对象的属性:
- console.log(star.name) // 调用名字属性
- console.log(star['name']) // 调用名字属性
2.调用对象的方法
- star.sayHi(); // 调用 sayHi 方法,注意,一定不要忘记带后面的括号
/变量、属性、函数、方法总结/
/*属性是对象的一部分,而变量不是对象的一部分,变量是单独存储数据的容器*/
- 变量:单独声明赋值,单独存在
- 属性:对象里面的变量称为属性,不需要声明,用来描述该对象的特征
/*方法是对象的一部分,函数不是对象的一部分,函数是单独封装操作的容器*/
- 函数:单独存在的,通过“函数名()”的方式就可以调用
- 方法:对象里面的函数称为方法,方法不需要声明,使用“对象.方法名()”的方式就可以调用,方法用来描述该对象的行为和功能。
2.利用 new Object 创建对象
- 创建空对象
var andy = new Obect();
/*通过内置构造函数Object创建对象,此时andy变量已经保存了创建出来的空对象*/
- 给空对象添加属性和方法
andy.name = 'pink';
andy.age = 18;
andy.sex = '男';
andy.sayHi = function(){
alert('大家好啊~');
}
3.利用构造函数创建对象
/构造函数/
#为什么需要使用构造函数?
- 因为前面两种方式一次只能创建一种。
#1.构造函数的定义
- 是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值。
- 它总与 new 运算符一起使用。
- 我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
#2.构造函数的封装格式
function 构造函数名(形参1,形参2,形参3) {
this.属性名1 = 参数1;
this.属性名2 = 参数2;
this.属性名3 = 参数3;
this.方法名 = 函数体;
}
#3.构造函数的调用格式
var obj = new 构造函数名(实参1,实参2,实参3)
/*以上代码中,obj即接收到构造函数创建出来的对象。*/
#注意事项
- 构造函数约定**首字母大写**。
- 函数内的属性和方法前面需要添加 **this** ,表示当前对象的属性和方法。
- 构造函数中**不需要 return 返回结果**。
- 当我们创建对象的时候,**必须用 new 来调用构造函数**。
# 4.其他
- 构造函数,如 Stars(),抽象了对象的公共部分,封装到了函数里面,它泛指某一大类(class)
- 创建对象,如 new Stars(),特指某一个,通过 new 关键字创建对象的过程我们也称为对象实例化
构造函数和对象的区别
1.构造函数 泛指的是某一大类(明星) 类似于java里面的class
2.对象 特指某一个具体的事物(刘德华)
3.通过构造函数(new关键字)创建对象的过程就称为对象的实例化
new关键字
#执行过程:
1.new 构造函数可以在内存中创建一个空的对象
2.this 就会指向刚才创建的空对象
3.执行构造函数里面的代码,给这个空对象添加属性和方法
4.返回这个对象(所以构造函数里面不需要return)
#作用:
1. 在构造函数代码开始执行之前,创建一个空对象;
2. 修改this的指向,把this指向创建出来的空对象;
3. 执行函数的代码
4. 在函数完成之后,返回this---即创建出来的对象
this
#用构造函数创建对象时的this的指向问题
function Person(name,age){
this.name=name;
this.age=age;
this.sayname=function(){
alert(this.name);
};
}
// 构造函数其实就是用new操作符调用的一个函数。用new时构造函数内部的this会指向新的实例,如:
var p1=new Person('tom',10);// 此过程中this指向p1
alert(p1.name);// tom
p1.sayname();// tom
// 直接调用(不适用new)
var p2=Person('tom',10);
alert(p2.name);//(报错)name is undefined
p2.sayname(); //(报错)sayname is undefined
alert(name)//tom
sayname();//tom
// **此时的name和sayname已经不属于p2,而是属于全局window,等同于:
alert(window.name);
window.sayname();
# 很明显不用new,而采用直接调用的方式this不在代表新的实例p2,而是window,this把name和sayname全放到了全局window中。
遍历对象
for (变量 in 对象名字) {
// 在此执行代码
}
/*语法中的变量是自定义的,它需要符合命名规范,通常我们会将这个变量写为 k 或者 key。*/
for (var k in obj) {
console.log(k); // 这里的 k 是属性名
console.log(obj[k]); // 这里的 obj[k] 是属性值
}
内置对象
前情提要
JavaScript 中的对象分为3种:自定义对象 、内置对象、 浏览器对象
- 前面两种对象是JS 基础 内容,属于 ECMAScript;
- 第三个浏览器对象属于 JS 独有的
- 内置对象提供了一些常用的或是**最基本而必要的功能**(属性和方法),如Math、 Date 、Array、String等
- 方便快速开发
MDN: https://developer.mozilla.org/zh-CN/
Math对象
Math 对象不是构造函数(不需要new),它具有数学常数和函数的属性和方法。
| 属性、方法名 | 功能 |
|---|---|
| Math.PI | 圆周率 |
| Math.floor() | 向下取整 |
| Math.ceil() | 向上取整 |
| Math.round() | 四舍五入版 就近取整 注意 -3.5 结果是 -3 |
| Math.abs() | 绝对值 |
| Math.max()/Math.min() | 求最大和最小值 |
| Math.random() | 获取范围在[0,1)内的随机值 |
**注意:上面的方法使用时必须带括号
/Math.max()/
console.log(Math.max(1,99,'pink'));//NaN
console.log(Math.max());//-Infinity
// 封装一个数学对象
var myMath={
PI: 3.141592653,
max: function(){
var max=arguments[0];
for(var i=0;i<arguments.length;i++){
if(arguments[i]>max)
max=arguments[i];
}
return max;
}
}
console.log(myMath.PI)
/Math.abs()/
// 存在隐式转换 会把只有数字的字符串转换为数字型
console.log(Math.abs('-1'));// 1
console.log(Math.abs('-120px'));// NaN
console.log(Math.abs('pink'));// NaN
/3个取整方法/
// (1) Math.floor() 地板 向下取整 往最小了取值 console.log(Math.floor(1.1)); // 1
console.log(Math.floor(1.9)); // 1
// (2) Math.ceil() ceil 天花板 向上取整 往最大了取值
console.log(Math.ceil(1.1)); // 2
console.log(Math.ceil(1.9)); // 2
// (3) Math.round() 四舍五入 其他数字都是四舍五入,但是 .5 特殊 它往大了取
console.log(Math.round(1.1)); // 1
console.log(Math.round(1.5)); // 2
console.log(Math.round(1.9)); // 2
console.log(Math.round(-1.1)); // -1
console.log(Math.round(-1.5)); // 这个结果是 -1
/Math.random()/
// 获取指定范围内的随机整数,并且包含这2个整数:
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 随机点名
var arr = ['张三', '张三丰', '张三疯子', '李四', '李思思', 'pink老师'];
console.log(arr[getRandom(0, arr.length - 1)]);
// 猜数字
var random=getRandom(1,10);
while(true){
var num=promt('你来猜? 输入1~10之间的一个数字');
if(num>random)
alert('你猜大了');
else if (num < random)
alert('你猜小了');
else
alert('你好帅哦,猜对了');
break; // 退出整个循环结束程序
}
日期对象
Date是一个构造函数(需要new),所以使用时需要实例化后才能使用其中具体方法和属性。
- 使用Date实例化日期对象
// 1.如果创建实例时没有传入参数,得到的日期对象是当前时间对应的日期对象
var now = new Date();
// 2.获取指定时间的日期对象
// 参数常用的写法 数字型 (2019, 10, 01) 或者是 字符串型 ('2019-10-1 8:8:8')或者(2019/10/1)
var date1 = new Date(2019, 10, 1);
console.log(date1); // 返回的是 11月 不是 10月
var date2 = new Date('2019-10-1 8:8:8');// 常用
console.log(date2);// 返回的正常的10月
/格式化日期 年月日/
var date=new Date();
var year=date.getFullYear();//返回当前日期的年 2019
var month = date.getMonth() + 1;// 月份 返回的月份小1个月 记得月份+1
var dates = date.getDate();// 返回的是 几号
var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
var day = date.getDay();//返回的是0-6 星期几
console.log('今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day]);
/格式化日期 时分秒/
// 要求封装一个函数返回当前的时分秒 格式 08:08:08
function getTimer(){
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(getTimer);
/获取日期的总毫米数(时间戳)/
/*总毫秒数的含义:基于1970年1月1日(世界标准时间)起的毫秒数
毫秒数是独一无二的,因此又称为时间戳
*/
// 实例化Date对象
var now = new Date();
// 1. 用于获取对象的原始值
console.log(date.valueOf());// 方法1
console.log(date.getTime());// 方法2
// 2. 简单写可以这么做
var now = + new Date(); // 方法3(常用)
// 3. HTML5中提供的方法,有兼容性问题
var now = Date.now();// 方法4
/倒计时案列/
function countDown(time){
var nowTime=+ new Data();
var inputTime=+ new Data(time);
var times=(inputTime-nowTime)/1000;//将ms转化为s
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('2021-5-22 21:30:00'));
数组对象
创建数组的两种方式
-
字面量方式
var arr = [1,"test",true]; -
new Array()
var arr = new Array(); 注意:上面代码中arr创建出的是一个空数组,如果需要使用构造函数Array创建非空数组,可以在创建数组时传入参数
参数传递规则如下:
-
如果只传入一个参数,则参数规定了数组的长度
-
如果传入了多个参数,则参数称为数组的元素
var arr1 = new Array(2); // 这个2 表示 数组的长度为 2 里面有2个空的数组元素 var arr1 = new Array(2, 3); // 等价于 [2,3] 这样写表示 里面有2个数组元素 是 2和3
-
检测是否为数组
-
instanceof 运算符
-
instanceof 可以判断一个对象是否是某个构造函数的实例
var arr = [1, 23]; var obj = {}; console.log(arr instanceof Array); // true console.log(obj instanceof Array); // false
-
-
Array.isArray()
-
Array.isArray()用于判断一个对象是否为数组,isArray() 是 HTML5 中提供的方法(ie9以上版本支持)
var arr = [1, 23]; var obj = {}; console.log(Array.isArray(arr)); // true console.log(Array.isArray(obj)); // false
-
添加删除数组元素的方法
- 数组中有进行增加、删除元素的方法,部分方法如下表!
注意:push、unshift为增加元素方法;pop、shift为删除元素的方法
// 1. push() 在我们数组的末尾 添加一个或者多个数组元素(原数组也会发生变化)
var arr = [1, 2, 3];
console.log(arr.push(4, 'pink'));// 返回的结果是 新数组的长度
// 2. unshift 在我们数组的开头 添加一个或者多个数组元素(原数组也会发生变化)
console.log(arr.unshift('red', 'purple'));// 返回的结果是 新数组的长度
// 3. pop() 它可以删除数组的最后一个元素(原数组也会发生变化)
console.log(arr.pop());// 返回的结果是 删除的那个元素
// (1) pop是可以删除数组的最后一个元素 记住一次只能删除一个元素
// (2) pop() 没有参数
// 4. shift() 它可以删除数组的第一个元素 (原数组也会发生变化)
console.log(arr.shift());// 返回的结果是 删除的那个元素
// (1) shift是可以删除数组的第一个元素 记住一次只能删除一个元素
// (2) shift() 没有参数
splice(删除/添加/替换)
splice() 方法通过
删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
-
start
- 指定修改的开始位置(从0计数)。
- 如果超出了数组的长度,则从数组末尾开始添加内容;
- 如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);
- 如果负数的绝对值大于数组的长度,则表示开始位置为第0位。
-
deleteCount 可选
- 整数,表示要移除的数组元素的个数。
- 如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。
- 如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。
- 如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
-
item1, item2, ... 可选
- 要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。
-
返回值
- 由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。
1. 从第 2 位开始删除 0 个元素,插入“drum”
var myFish = ["angel", "clown", "mandarin", "sturgeon"];
var removed = myFish.splice(2, 0, "drum");
// 运算后的 myFish: ["angel", "clown", "drum", "mandarin", "sturgeon"]
// 被删除的元素: [], 没有元素被删除
2. 从第 2 位开始删除 0 个元素,插入“drum” 和 "guitar"
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(2, 0, 'drum', 'guitar');
// 运算后的 myFish: ["angel", "clown", "drum", "guitar", "mandarin", "sturgeon"]
// 被删除的元素: [], 没有元素被删除
3. 从第 3 位开始删除 1 个元素
var myFish = ['angel', 'clown', 'drum', 'mandarin', 'sturgeon'];
var removed = myFish.splice(3, 1);
// 运算后的 myFish: ["angel", "clown", "drum", "sturgeon"]
// 被删除的元素: ["mandarin"]
4. 从第 2 位开始删除 1 个元素,插入“trumpet”
var myFish = ['angel', 'clown', 'drum', 'sturgeon'];
var removed = myFish.splice(2, 1, "trumpet");
// 运算后的 myFish: ["angel", "clown", "trumpet", "sturgeon"]
// 被删除的元素: ["drum"]
5. 从第 2 位开始删除 2 个元素
var myFish = ['parrot', 'anemone', 'blue', 'trumpet', 'sturgeon'];
var removed = myFish.splice(myFish.length - 3, 2);
// 运算后的 myFish: ["parrot", "anemone", "sturgeon"]
// 被删除的元素: ["blue", "trumpet"]
6. 从倒数第 2 位开始删除 1 个元素
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(-2, 1);
// 运算后的 myFish: ["angel", "clown", "sturgeon"]
// 被删除的元素: ["mandarin"]
7. 从第 2 位开始删除所有元素
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(2);
// 运算后的 myFish: ["angel", "clown"]
// 被删除的元素: ["mandarin", "sturgeon"]
数组排序/翻转
// 1. 翻转数组
var arr = ['pink', 'red', 'blue'];
arr.reverse();
console.log(arr);
// 2. 数组排序(冒泡排序)
var arr1 = [13, 4, 77, 1, 7];
arr1.sort();//只会排个位数
//注意:sort方法需要传入参数来设置升序、降序排序
arr1.sort(function(a, b) {
// return a - b; 升序的顺序排列
return b - a; // 降序的顺序排列
});
console.log(arr1);
数组索引方法
var arr = ['red', 'green', 'blue', 'pink', 'blue'];// 它只返回第一个满足条件的索引号 // 它如果在该数组里面找不到元素,则返回的是 -1
console.log(arr.indexOf('blue'));// 2
//lastIndexOf(数组元素) 作用就是返回该数组元素的索引号 从后面开始查找
var arr = ['red', 'green', 'blue', 'pink', 'blue'];
console.log(arr.lastIndexOf('blue')); // 4
/数组去重案例/
// 核心算法: 我们遍历旧数组, 然后拿着旧数组元素去查询新数组, 如果该元素在新数组里面没有出现过, 我们就添加, 否则不添加。
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;
}
数组转换为字符串
// 1. toString() 将我们的数组转换为字符串
var arr = [1, 2, 3];
console.log(arr.toString()); // 1,2,3
// 2. join(分隔符)
var arr1 = ['green', 'blue', 'pink'];
//join方法如果不传入参数,则按照 “ , ”拼接元素
console.log(arr1.join()); // green,blue,pink
console.log(arr1.join('-')); // green-blue-pink
console.log(arr1.join('&')); // green&blue&pink
toString(16)
// 将任意数字转换为16进制
function getRandom(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomColor() {
var color = '#'
for (var i = 0; i < 6; i++) {
//num.toString(16)将数字转换为16进制
color += getRandom(0, 15).toString(16);
}
return color;
}
其他方法
字符串对象
字符串的所有方法,都不会修改字符串本身,每次操作完成都会返回一个新的字符串
基本包装类型
为了方便操作基本数据类型,JavaScript 还提供了三个特殊的引用类型:String、Number和 Boolean。
基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。
// 下面代码有什么问题?
var str = 'andy';
console.log(str.length);
按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为
js 会把基本数据类型包装为复杂数据类型,其执行过程如下 :
// 1. 生成临时变量,把简单类型包装为复杂数据类型
var temp = new String('andy');
// 2. 赋值给我们声明的字符变量
str = temp;
// 3. 销毁临时变量
temp = null;
字符串的不可变
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
当重新给字符串变量赋值的时候,变量之前保存的字符串不会被修改,依然在内存中重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变。 由于字符串的不可变,在大量拼接字符串的时候会有效率问题(尽量避免)
根据字符返回位置
案例:查找字符串"abcoefoxyozzopp"中所有o出现的位置以及次数
1. 先查找第一个o出现的位置
2. 然后 只要indexOf 返回的结果不是 -1 就继续往后查找
3. 因为indexOf 只能查找到第一个,所以后面的查找,利用第二个参数,当前索引加1,从而继续查找
function(str,a){
var index=str.indexOf(a,0);
var count=0;
var s=[]
while(index!=-1){
s.push(index);
index=str.indexOf('o',index+1);
count++;
}
return [s,count];
}
var str='abcoefoxyozzopp';
console.log(find(str, 'o'));
根据位置返回字符
// 1. charAt(index) 根据位置返回字符
var str = 'andy';
console.log(str.charAt(3));//y
// 遍历所有的字符
for (var i = 0; i < str.length; i++) {
console.log(str.charAt(i));
}
// 2. charCodeAt(index) 返回相应索引号的字符ASCII值 目的: 判断用户按下了那个键
console.log(str.charCodeAt(0)); // 第0个对应的是a 对应的ASCII值是97
// 3. str[index] H5 新增的
console.log(str[0]); // a
在上述方法中,charCodeAt方法返回的是指定位置上字符对应的ASCII码,ASCII码对照表如下:
案例:判断一个字符串 'abcoefoxyozzopp' 中出现次数最多的字符,并统计其次数
1. 核心算法:利用 charAt() 遍历这个字符串
2. 把每个字符都存储给对象, 如果对象没有该属性,就为1,如果存在了就 +1
3. 遍历对象,得到最大值和该字符
/*注意:在遍历的过程中,把字符串中的每个字符作为对象的属性存储在对象总,对应的属性值是该字符出现的次数*/
function countMax(str){
var o={};
var max=0;
// 遍历字符串,记录每个字符串对应的次数
for(var i=0;i<str.length;i++){
var chars=str.charAt(i);
if(o[chars]){
o[chars]++;
}else{
o[chars]=1;
}
}
// 遍历对象,找到出现次数最大的字符,以及其出现的次数
for(var k in o){
if(o[k]>max){
max=o[k];
m=k;
}
}
return [m,max];
}
var str = 'abcoefoxyozzopp';
console.log(countMax(str));
字符串操作方法(重点)
// 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 是取几个字符
replace()方法
// 1. 替换字符 replace('被替换的字符', '替换为的字符') 它只会替换第一个字符
var str = 'andyandy';
console.log(str.replace('a', 'b'));// bndyandy
// 有一个字符串 'abcoefoxyozzopp' 要求把里面所有的 o 替换为 *
var str1 = 'abcoefoxyozzopp';
while (str1.indexOf('o') !== -1) {
// replace返回的是替换后新的字符串。不会改变原有字符串,需要重新赋值
str1 = str1.replace('o', '*');
}
console.log(str1);
split()方法
// 2. 字符转换为数组 split('分隔符') 前面我们学过 join 把数组转换为字符串
var str2 = 'red, pink, blue';
console.log(str2.split(','));// ['red','pink','blue']
var str3 = 'red&pink&blue';
console.log(str3.split('&'));
trim()
// 去掉字符串两边的空格
// 如果是 空格字符串 ,则会去掉所有的空格
var str=' ref ';
console.log(str.trim());//ref
简单数据类型和复杂数据类型
简单数据类型
简单类型(基本数据类型、值类型):在存储时变量中存储的是值本身,包括string ,number,boolean,undefined,null
// 简单数据类型 null 返回的是一个空的对象 object
var timer = null;
console.log(typeof timer);
// 如果有个变量我们以后打算存储为对象,暂时没想好放啥, 这个时候就给 null
复杂数据类型
复杂数据类型(引用类型):在存储时变量中存储的仅仅是地址(引用),通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等;
堆栈
- 堆栈空间分配区别:
1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
简单数据类型存放到栈里面
2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
复杂数据类型存放到堆里面
数据的存储方式
1. 简单数据类型(值类型)的存储方式
- 是存放在栈里面 里面直接开辟一个空间存放的是值
- 值类型变量的数据直接存放在变量(栈空间)中
2. 复杂数据类型的存储方式
- 首先在栈里面存放地址 十六进制表示 然后这个地址指向堆里面的数据
- 引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中
简单类型传参
函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。
function fn(a) {
a++;
console.log(a);
}
var x = 10;
fn(x);// 11
console.log(x);// 10
复杂数据类型传参
函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。
function Person(name) {
this.name = name;
}
function f1(x) { // x = p,xp都指向同一个地址
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. 张学友
var a={
name:'zs'
}
var b=a;
a.name= 'ls';
console.log(b.name);// ls
// 新开辟一块空间,新的地址指向新的内存块
a = {
name:'ww';
}
console.log(b);// ls