JavaScript 数组,对象,函数基本知识
数组
概念:数组(Array) 就是将多个元素(可以是不同类型)按一定顺序排列放到一个集合中,这个集合就称为数组
创建数组
//创建一个空数组 这是一种字面量定义数组的方法
var arr = [];
// 带数据数组
var arr = [1,"a",null,undefined,[2,3]];
// 格式创建 这是调用数组构造函数生成的数组
var arr = new Array();
获取数组元素
数组可以通过一个index(索引,下标)去获取某一个值,下表都是从0开始计数
var arr = [a,b,c];
//arr[0] 的值就是 a
arr[0] = "d";
// 可以改变数组 0 下表的值为 d
// 超出数组大小的下标
arr[3] 的值是 undefined
2 4 6 8 14 22 36
f(n) = f(n-1) + f(n-2)
数组长度
数组的 length 属性可以取到数组的长度
arr.length
- 最后一项下标值是(arr.length - 1)
- 数组的长度不是固定不变的,可以通过一些方法改变
- 增加数组长度:
arr.length = 比原来大的值
- 给一个比原来最大下标大的下标一个值:
arr[maxIndex + i] = "asd"
也能增加数组长度
- 给一个比原来最大下标大的下标一个值:
- 缩短数组长度:强制给
arr.length = 比原来小的值
缩小后的,之前超出长度的值会被删除,且不可还原数据,使用时应特别注意
- 增加数组长度:
数组遍历
var arr = [3,45,63,123,53,15,21];
for (var i = 0 ; i < arr.length ; i++) {
console.log(arr[i]);
}
函数
又被称为功能,方法;函数可以将一段代码封装起来,重复使用。
函数的声明和调用
函数声明
函数声明又称为函数定义,函数必须先定义才能使用
//函数声明
function 函数名(参数) {
代码块
}
// 函数名的组成:不能以数字开头,对大小写敏感,其他参照参数的命名规范
注意:声明函数的时候,函数并不会执行;只有函数调用的时候,才会执行函数
函数调用
function fn(a) {
consloe.log(a);
}
//调用
fn(1);
-
函数调用也叫函数执行,调用时会将函数内部所有代码依次全部执行。
-
函数内部语句执行位置与函数定义位置无关,与函数调用位置有关。
-
函数可以一次调用,多次执行
函数的参数
函数参数的本质就是变量,可以接受任意数据类型,导致函数执行结果依据参数不同,结果也不同。
-
一个函数可以设置多个参数,参数之间用逗号分隔。
-
形式参数:定义的()内部的参数,本质是变量,可以接受实参传过来的值。
-
实际参数:调用的()内部的参数,本质就是传递各种各样类型的数据,传递给每个形参,简称实参。
优点:通过API接口说明,告诉我们需要传什么样的参数,能够实现什么样的功能。
函数的返回值
用函数内部的一个 return 关键字设置函数返回的关键值
- 函数的内部如果执行到一个 return 关键字,会立即停止后面代码的执行。
- 可以 return 后面写空格,空格后面可以定义任意一个字面量或者是表达式
- 函数返回值可以继续参与程序
注意:如果函数没有设置 return 语句,那么函数有默认的返回值 undefined,有 return 但是后面没有跟任何语句或值,也是 undefined
函数表达式
函数表达式是函数定义的另一种方式。
定义方法:
// 第一种
var fn = function sub(a,b) {
return a + b;
};
fn(1,2); //执行
//第二种 也称为匿名函数 没有函数名
var fn2 = function (a,b) {
return a - b;
};
fn2(2,1)
注意:调用函数表达式,是变量名()执行,不是执行函数名() ,注意结尾要加分号
函数的数据类型
函数是一种单独的数据类型 Function
var fn = function (){
console.log(1);
}
console.log(typeof(fn())); // Function
- 函数是一种数据类型,可以参与其他程序
arguments 对象
- arguments 对象中存储了所有函数传递的实参,arguments 是一个伪数组,因此可以进行遍历。
- 函数的形参跟实参个数可以不一致,所有实参的存在 arguments 中
throw new Error("sdad") //模拟控制台报错信息
function test (a,b) {
console.log(arguments);
}
test(1,2,3,4,5);
// arguments 值是 [1,2,3,4,5];
函数递归
函数内部,通过函数名调用自身的方式,就是函数递归现象。
- 递归次数太多容易出现错误:超出计算机的计算最大能力
典例:斐波那契数列
function Fn (a) {
if (a === 1 || a === 2) {
return 1;
} else {
return Fn(a - 1) + Fn(a - 2);
}
}
作用域
变量可以起作用的范围。
- 如果变量定义在一个函数内部,只能在函数内部被访问到,外部不能使用这个变量。函数就是变量定义的作用域
- 任何一对 {} 中的结构体都属于一个块,在这之中定义的所有变量在代码块外都无法使用,称之为块级作用域。
- 在 ES6 之前没有块级作用域的概念,只有函数作用域。
全局变量
定义在全局的变量,作用域范围是全局,任何位置都能访问到
局部变量
定义在函数内部,只有函数内部能访问,外部无法访问
-
函数的参数也是局部变量,只能在函数内部访问
-
如果在函数内部定义一个函数,那么这个被定义的函数也是局部的。
function f1() { function f2() { console.log(1); } f2(); } f1(); f2(); //错误 // f2 只能在f1作用域调用
作用域链和遮蔽效应
只有函数可以制造作用域结构,只要是代码,至少存在一个作用域,即全局作用域。凡是代码中有函数,这个函数就构成一个新的作用域。函数再套函数,又会产生新作用域。
就这样将所有的作用域列出来,可以有一个结构:函数内指向函数外的链式结构,称为作用域链。
// 全局
var a = 1;
function f1() {
// 二级作用域
var a = 2;
function f2() {
//三级作用域
var a = 3;
console.log(a);
}
f2();
}
f1();
/*
一级链: a = 1 f1()
二级链: a = 2 f2()
三级链: a = 3 console.log(a)
*/
遮蔽效应
程序在遇到一个变量时,使用作用域查找顺序,不同的作用域可能有相同的变量,会优先从本作用域查找,依次往外,直到找到第一个变量定义。整个过程会发生内层变量遮蔽外层变量,这就是遮蔽效应。
不写 var 关键字的影响
在函数内部定义变量要是不加 var 关键字,那么变量就会被定义成全局变量,如果存在这个全局变量,就会改变全局变量的值,造成全局变量污染。
var a = 1;
function f1() {
a = 2;
}
console.log(a);
预解析和声明提升
预解析
- JavaScript 代码执行是由浏览器中的 JavaScript 解析器来执行的,分为俩个过程:预解析过程 和 执行代码过程。
- 预解析过程:
- 把变量声明提升到当前作用域最前面,只会提升声明,不会赋值。
- 把函数声明提升当当前作用域最前面,只会提升声明,不会调用。
- 先提升var 再提升 function
- 执行代码过程:在预解析之后,按照新的代码顺序,从上往下执行
//正式代码
console.log(a);
function fn(){
console.log(2);
}
var a = 3;
fn();
// 预解析
var a; //变量声明提升
// 函数声明提升
function fn(){
console.log(2);
}
console.log(a);
a = 3;
fn();
变量声明提升
在预解析过程中,所有的变量声明提升到作用域组上面,将来代码执行,按照先后顺序执行新的顺序。
注意:变量声明提升,只提升声明,不会赋值,因此 js 中会出现一种现象,在前面调用后定义的变量,不会报错,但是值是 undefined
函数声明提升
在预解析过程中,所有定义的函数,都会将声明提升到当前作用域的最上面,将来代码执行,按照新的顺序。
因此,js中,在前面调用后面定义的函数,不会报错,而且能正常执行函数内部的代码
函数表达式的声明提升
在预解析的过程中,函数表达式进行的是变量声明提升,而不是函数声明提升。因此,这种定义在js中,如果在声明后调用函数,会报错
定义函数时,最好还是使用 function 关键字进行定义
提升顺序
先 var 后函数
- 假如变量名和函数名相同,那么按照提升顺序来看,后提升的函数名,会覆盖先提升的变量名。
- 尽量不要书写相同的标识符给变量名或函数名,避免出现覆盖
//正常代码书写
function fn(){
console.log(1);
}
var fn = 2;
console.log(fn);
fn();
// 预解析
var fn;
function fn(){
console.log(1);
}
//console.log(fn)
fn = 2;
console.log(fn) //2
fn(); //报错
IIFE 自调用函数
IIFE:immediately-invoked function express,叫做即时调用的函数表达式,也叫自调函数,表示函数在定义时就立即调用。
- function 定义的函数,无法实现立即调用,函数表达式声明的函数可以实现;想要实现 IIFE 必须将函数声明矮化成表达式。
- 函数矮化方式:给函数前面增加一些运算符:+,-,(),!
- IIFE 结构可以关主函数的作用域
- IIFE 最常用 () 运算符,可以不写函数名
//1 报错,不是正确形式
function fn() {
console.log(1);
}();
//2 正确形式,立即执行
var fn = function () {
console.log(1);
}();
//3 正确形式,立即执行
(function fn() {
console.log(1);
})();
//4 正确形式,立即执行 + - !
+ function fn() {
console.log(1);
}();
对象
对象可以自定义名称,存储一系列无序的相关数据。
对象的概念
现实生活中:万物皆对象,对象是一个具体的事物,一个具体的事物就会有行为和特征。
JavaScript中的对象:就是对生活中对象的一个抽象,一个对象就会有属性和方法。
对象的创建
对象字面量创建
创建一个对象最简单的方式是使用对象字面量赋值给变量。内部可以存放多条数据,数据与数据之间用逗号分隔,最后一个不加逗号。每条数据都采用键值对写法,可以是任意类型的数据。
var girl = {
name : "小李",
sex : "female",
age : 20,
SayHi : function () {
console.log("ni hao");
}
};
区分方法和属性
属性:对象的特征描述,一般是名词,相当于定义在对象内部的变量。
方法:对象的行为和功能,一般是动词,定义存在于对象中的函数。
对象数据的调用与更改
用对象的变量名打点调用某个属性名,得到属性值;
在对象内部用 this 打点调用属性名,this 替代对象;
用对象变量名后加 [] 调用,[] 内部是字符串格式的属性名。
调用方法时,需要在方法名后加 () 执行。
var girl = {
name : "小李",
sex : "female",
age : 20,
SayHi : function () {
console.log("ni hao");
},
ChangeAge : function () {
this.age = this.age + 1;
}
};
//1 调用属性
console.log(girl.name);
//2 调用属性
console.log(girl["name"]);
//3 改变属性值
girl.name = "小王";
//4 增加对象属性
girl.weight = 140;
//5 调用对象方法
girl.SayHi();
//6 删除一条属性
delete girl.weight;
通过 new Object() 方法创建一个对象
Object() 构造函数,是一种特殊的函数。主要用来创建对象时,初始化对象,即为对象成员变量赋初始值,与 new 运算符一起使用在创建对象的语句中。
- 构造函数用于创建一类对象,首字母要大写。
- 构造函数要和 new 一起使用才有意义。
// 创建
var girl = new Object();
girl.name = "张飞";
girl.age = 18;
girl.sex = "female";
girl.SayHi = function () {
console.log("你好");
}
new 在执行时会做的四件事情
- new 会在内存中创建一个新的空对象。
- new 会让 this 指向这个新对象
- 执行构造函数 : 给这个新对象加属性和方法
- new 会返回这个新对象
工厂函数创建对象
如果要创建多个类似的对象,可以将创建对象的过程封装起来,将来调函数就能创建一个对象,简化代码
function CreatePerson (name,age,sex) {
var person = new Object();
person.name = name;
person.age = age;
person.sex = sex;
person.SayHi = function () {
console.log("你好");
}
return person;
}
var p1 = CreatePerson("lisi",18,"female");
自定义构造函数方法创建对象
自定义一个创建具体对象的构造函数,函数内部不需要new一个构造函数的过程,直接使用 this 代替对象进行属性和方法的书写,也不需要return一个返回值
注意:构造函数名称,首字母必须大写,区别于其他普通函数
// 自定义构造函数
function Person (name,sex,age) {
this.name = name;
this.sex = sex;
this.age = age;
this.SayHi = function () {
console.log("nihao");
}
// 不需要return
}
// 创建对象
var p1 = new Person("liz","nv",19);
console.log(p1);
遍历对象
使用 for…in,内部会定义一个 k 变量,循环时从第一个对象的属性名 或 方法名开始,一直到最后。
// 自定义构造函数
function Person (name,sex,age) {
this.name = name;
this.sex = sex;
this.age = age;
this.SayHi = function () {
console.log("nihao");
}
// 不需要return
}
// 创建对象
var p1 = new Person("liz","nv",19);
console.log(p1);
// 遍历对象
for (var k in p1) {
console.log(k + "的属性值是" + p1[k]);
}
简单数据类型和复杂数据类型
简单数据类型又叫基本类型,也称为值类型:数字,字符,布尔,null,undefined…这些都是。
复杂数据类型又称为引用数据类型:数组,function,对象都是复杂数据类型。
区别:值类型,变量在存储时,存储的是值;引用类型在存储时,存储的是地址
简单数据类型在内存中的存储
- 变量中存储的是值本身,当把值赋值给另一个变量时,也是值赋值,值的改变互不影响。
复杂数据类型在内存中的存储
- 如果将复杂类型的数据赋值给一个变量,复杂数据类型会在内存中创建一个原型,而变量中存储的是指向数据的一个地址,如果将变量赋值给其他变量,赋值的是变量中存储的地址,俩个变量存储的地址相同,当其中一个变量通过地址修改了原型数据,另一个变量通过地址取得的数据也会发生改变。
// 自定义构造函数
function Person (name,sex,age) {
this.name = name;
this.sex = sex;
this.age = age;
this.SayHi = function () {
console.log("nihao");
}
}
// 创建对象
var p1 = new Person("liz","nv",19);
var p2 = p1;
p1.name = "ers";
console.log(p1);
console.log(p2);
内置对象
JavaScript 的对象包含三种:自定义对象,内置对象,浏览器对象
ECMAscript 的对象:自定义对象 内置对象
内置对象就是 JavaScript 自带的一些对象:Math 等。
内置对象可以使用 MDN 网站来学习
Math 对象
- Math.PI 圆周率
- Math.random() 生成随机数 0-1 之间的浮点数
- Math.floor() 向下取整
- Math.ceil() 向上取整
- Math.round() 取整,四舍五入
- Math.abs() 绝对值
- Math.max() 取一组数中的最大值
- Math.min() 取一组数中的最小值
- Math.sin() 正
- Math.cos() 余
- Math.pow(a,b) a 的 b 次方
- Math.sqrt() 求平方根
数组对象的创建,判断复杂数据类型的方法
// 数组对象的创建
var arr = new Array(1,3,56);
判断复杂数据类型
instanceof : 检测某个实例是否是某个对象类型。
返回值:true / false
var arr = new Array(1,3,56);
console.log(arr instanceof Array); //true
数组的常用方法
-
push() 在数组末尾添加一个或多个元素,返回添加后的数组长度
-
unshift() 在数组的开头添加一个或多个元素,返回添加后的数组长度
-
pop() 删除数组的最后一项,返回删除项
-
shift() 删除数组的第一项,返回删除项
-
concat() 将俩个数组合并成一个新的数组,原数组不受影响。参数可以是数组字面量,数组变量,零散的值。
-
slice(start,end) 从当前的数组中截取一个新的数组,不影响原来的数组,返回一个新的数组(从 start 到 end (不包含最后一位元素))
- 参数区分正负,正值表示下标位置,负数表示从后往前数第几个位置,参数只传一个时,表示从开始位置到最后全部。
-
splice(index,howmany,e1,e2,…) 用于插入,删除或替换数组的元素,删除项返回值是删除的元素数组。
- index : 操作元素的开始位置
- howmany:操作元素的个数,插入元素时,个数是0
- e:元素(插入或替换元素)
-
位置方法
- indexOf() 查找数据在数组中最先出现位置的下标,不存在时返回 -1
- lastIndexOf() 查找数据在数组中最后出现位置的下标,不存在时返回 -1
-
reverse() 倒叙排列数组
-
sort() 排序
-
默认情况下,根据字符编码(ASCII) 大小进行排序,从小到大。
-
想要根据数值大小进行排序,必须添加 sort 的比较函数参数; 根据 a 和 b 的判断条件作为条件。
var arr = [1,2,3,4,10,20,30]; arr.sort(function (a,b) { if (a > b) { return 1; // a 排 b 的后面 } else if (a < b) { return -1; // a 排 b 的前面 } else { return 0; // 不做改变 } }); console.log(arr);
-
-
数组转字符串
- toString() 数组转成字符串用逗号链接。
- join(链接字符) 参数为链接字符,通过链接字符转为字符串。
-
清空数组
- arr = []
- arr.length = 0
- arr.splice(0,arr.length)
基本包装类型
基本数据类型没有属性和方法,那么为什么一个字符串可以使用截取方法呢
var str = "abcdef";
var str2 = str.slice(2,5);
console.log(str2);
字符串在使用方法时,被转换成了 String 类型的临时对象,用完后就销毁了
var str = new String("abcdef");
var str2 = str.slice(2,5);
console.log(str2);
str = null; //销毁
字符串
字符串的特点:字符串是不可变的,当一个字符串有初始值后,重新赋值时,原有的值会占据一块内存,而新值会重新开辟一块内存,直到 js 解析器用完,进行垃圾回收会清理内存。因此,在大量拼接字符串时,会有效率问题。
字符串所有的方法,都不会修改字符串本身,操作完成会返回一个新的字符串。
字符串方法:
- charAt(下标) 返回指定位置的字符串
- indexOf() 返回某个指定的字符串在字符串中首次出现的位置,没有就返回-1
- concat() 拼接另一个字符串
- split() 把一个字符串分隔成数组,参数是分隔符
- toLowerCase() 把字符串转小写,生成新字符串,原来字符串没有变化
- toUpperCase() 把字符串转大写,生成新字符串,原来字符串没有变化
- slice()
- 参数: slice(st , ed) 开始位置,结束位置,若是只有一个参数,那就是从开始位置到字符串末尾
- 参数区分正负,正数表示下标位置,负数表示从后往前第几个
- substr() 在字符串中抽取 st 下标开始到指定长度的字符串
- substr(st , howmany) st : 开始位置 , howmany : 截取长度
- start 区分正负,正数表示下标位置,负数表示从后往前第几个
- howmany : 参数必须是正数,不写表示,从start 截取到最后
- substring() 用于提取字符串,介于俩个下标之间的字符。
- substring(start , end)
- 参数只能为正数,俩个参数大小不限制,系统会根据大小自动来选取开始位置和结束位置,如果不写第二个参数,那就是从开始提取到末尾。
字符串时,会有效率问题。
字符串所有的方法,都不会修改字符串本身,操作完成会返回一个新的字符串。
字符串方法:
- charAt(下标) 返回指定位置的字符串
- indexOf() 返回某个指定的字符串在字符串中首次出现的位置,没有就返回-1
- concat() 拼接另一个字符串
- split() 把一个字符串分隔成数组,参数是分隔符
- toLowerCase() 把字符串转小写,生成新字符串,原来字符串没有变化
- toUpperCase() 把字符串转大写,生成新字符串,原来字符串没有变化
- slice()
- 参数: slice(st , ed) 开始位置,结束位置,若是只有一个参数,那就是从开始位置到字符串末尾
- 参数区分正负,正数表示下标位置,负数表示从后往前第几个
- substr() 在字符串中抽取 st 下标开始到指定长度的字符串
- substr(st , howmany) st : 开始位置 , howmany : 截取长度
- start 区分正负,正数表示下标位置,负数表示从后往前第几个
- howmany : 参数必须是正数,不写表示,从start 截取到最后
- substring() 用于提取字符串,介于俩个下标之间的字符。
- substring(start , end)
- 参数只能为正数,俩个参数大小不限制,系统会根据大小自动来选取开始位置和结束位置,如果不写第二个参数,那就是从开始提取到末尾。