一、函数
1. 函数概念
函数也叫做功能、方法,它可以将一段代码一起封装起来,被封装的函数具备一项特殊的功能,内部封装的一段代码作为一个完整的结构体,要执行就都执行,不执行就都不执行。函数就是封装一段代码,可以重复使用。
2. 函数定义(声明)
函数命名规则:可以使用字母、数字、下划线、$,不能用数字作为开头,区分大小写,不能用关键字和保留字。
//定义函数
function 函数名fun(参数){
封装的结构体;
}
//调用函数
fun();
3. 函数的参数
-
形式参数:定义的()内部的参数,本质是变量,可以接受实际参数传递过来的数据,简称形参。
-
实际参数:调用的()内部的参数,本质是传递的各种类型的数据,传递给每个形参,简称实参。
-
函数执行的过程,伴随着传参的过程:
-
一般自己封装的函数或者其他人封装的函数需要有一个API接口说明,告诉用户参数需要传递什么类型的数据,实现什么功能。
4. 函数表达式
定义方法:将函数的定义、匿名函数赋值给一个变量,相当于将函数矮化成了一个表达式。
匿名函数:函数没有函数名。
调用函数表达式,是给变量名加()执行,不能使用函数名加()执行。
//定义函数表达式
var foo = function fun(){
console.log(1);
};
//匿名函数定义(多用)
var foo2 = function (){
console.log(2);
};
//调用函数
foo();
foo2();
5. 函数的数据类型
例如,可以把函数作为另一个函数的参数,在另一个函数中调用。或者,可以把函数作为返回值从函数内部返回。
函数是一种单独的数据类型function,可以参与其他程序。
//定义函数表达式
var foo = function fun(){
console.log(1);
};
//检测函数的数据类型
console.log(typeof(fun)); //-->function
console.log(typeof(foo)); //-->function
//把函数作为另一个函数的参数
setInterval(function(){
console.log(i);
},1000);
//把函数作为返回值从函数内部返回。
function fn(b){
var a = 10;
return function (){
alert(a + b);
};
}
6. arguments对象
- JS中,arguments对象是一个比较特别的对象,实际上是当前函数的一个内置属性,也就是说所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有的实参。arguments是一个伪数组,因此可以进行遍历。
- 函数的形参和实参的数量可以不一致,所有的实参都会存储在函数内部的arguments类数组对象中。
- 可以通过arguments.length获取用户传递了多少个实参。
7. 作用域链和遮蔽效应
不同层次的函数内部可能定义相同名字的变量,变量使用时会优先从自己所在层作用域查找变量,如果当前层没有回按照顺序从本层往外层依次查找,直到找到第一个变量定义,这个内层变量遮蔽外层变量的效果,叫做“遮蔽效应”。
8. 不写var关键字的影响
在函数内部定义新的变量,不写var,相当于定义的是全局变量,如果全局也有相同标识符,会被函数内部的变量影响。
9. 预解析和声明提升
预解析
- 在预解析过程中,所有定义的变量都会将声明的过程提升到所在的作用域最上面,在代码执行过程中,按先后顺序回先执行被提升的声明变量过程。
- 提升过程中,只提升声明过程,不提升变量赋值,相当于变量定义未赋值,变量内存储undefined值。
// var a; 相当于存了一个undefined值
console.log(a); //-->undefined
var a = 1;
函数声明提升
在预解析之后的代码执行过程中,函数定义过程已经在最开始就会执行,一旦函数定义成功,后续无论在哪里都可以直接调用函数。在前面调用后面定义的函数,不会报错且能顺利执行。
//先调用函数
fun();
function fun(){
console.log(2); //-->可以得到2,相当于把函数提升在最前面
}
提升顺序
- 先提升var变量声明,再提升function函数声明。
- 假设变量名和函数名相同,那么后提升的函数名标识符会覆盖先提升的变量名,那么在后续代码中调用标识符时,内部是函数的定义过程,而不是undefined。
- 建议:不要书写相同的标识符给变量名或函数名,避免出现覆盖。
console.log(a); //——>undefined
console.log(fun); //——>fun(){console.log(2);}
var a = 1;
var fun = "haha"; //相当于没有声明过程,只有赋值为“haha”
fun(); //此时“haha”覆盖了函数,调用的不是一个函数了,报错
function fun(){
console.log(2);
}
- 函数表达式进行的是变量声明提升
console.log(foo); //——>undefined,不是函数fun(){console.log(2);}
foo();
var foo = function fun(){
console.log(2);
};
10. IIFE 自调用函数
IIFE: immediately-invoked function expression, 即时调用的函数表达式,也称为自调用函数,表示函数在定义时就立即调用。
//函数表达式定义的方式, 可以在定义时被立即执行
var foo = function fun(){
console.log(1);
}();
-
函数矮化成表达式,就可以实现自调用。
-
函数矮化成表达式的方法:让函数参与一些运算,即给函数前面加一些运算符。
- 数学运算符:+ - ()
- 逻辑运算符:! 非运算
//添加操作符 + function fun(){ console.log(1); }(); - function fun(){ console.log(1); }(); ( function fun(){ console.log(1); })(); ! function fun(){ console.log(1); }(); fun(); //——>报错Uncaught ReferenceError: fun is not defined -
IIFE关住了函数的作用域,在外面调用不了函数。
//常用的IIFE结构 (function(a){ console.log(a); })(4);
二、对象
1. 对象字面量
-
对象字面量语法:{}
-
内部可以存放多条数据,数据与数据之间用逗号分隔
-
每条数据都有属性名和属性值组成,键值对写法: k: v
-
k: 属性名 v:属性值,可以是任意类型的数据
-
语法:
var obj = { k: v, k: v, k: v}; -
区分属性和方法:
属性:对象的描述性特征,一般是名词,相当于定义在对象内部的变量。
方法:对象的行为和功能,一般是动词,定义在对象中的函数。
2. 对象数据的调用和更改
-
调用属性:
对象名.属性名
person1.name对象名["属性名"]
person1["name"]在对象内部:this.属性名
this.name -
调用方法:
对象名.方法名()
person1.sayHi()对象名"方法名"
person1["sayHi"](); -
更改属性的属性值:先调用,再赋值。例如:
person1.age = 19; -
增加新属性和属性值:直接定义,再赋值。例如:
person1.weight = 140; -
删除一条属性:用delete关键字,空格加属性。例如:
delete person1.sex;
3. 创建对象的方法
(1) new Object() 创建对象
Object()构造函数是一种特殊的函数,用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
-
构造韩素用于创建一类对象,首字母要大写。
-
构造函数要和new一起使用才有意义。
-
new在执行时会做的四件事情:
- new会在内存中创建一个新的空对象
- new会让this指向这个新的对象
- 执行构造函数 目的:给这个新的对象加属性和方法
- new自动生成返回值,会返回这个新对象
(2) 工厂方法创建对象
作用:便于多次创建。
function creatPerson(name, age, sex){
//创建对象
var person = new Object();
//添加属性和方法
person.name = name;
person.age = age;
person.sex = sex;
person.sayHi = function(){
console.log("hello");
};
//将对象作为函数的返回值
return person;
}
//想创建对象可以调用工厂函数
var p1 = creatPerson("zs", 18, true);
var p2 = creatPerson("ls", 19, false);
console.log(p1);
console.log(p2);
(3) 自定义构造函数方法创建对象
函数内部不需要new一个构造函数的过程,直接使用this代替对象进行属性和方法的书写,也不需要return一个返回值。比工厂方法更方便。
使用时,利用new关键字调用自定义的构造函数即可。
注意:构造函数的函数名首字母需要大写,区别于其他普通函数。
function Person(name, age, sex){
//不需要使用new一个新对象,直接用this替代将来创建的新对象
this.name = name;
this.age = age;
this.sex = sex;
this.sayHi = function(){
console.log("hi");
};
//不需要return
}
var p1 = new Person("zs", 18, true);
console.log(p1);
4. 对象遍历
for (var k in person1(对象名)){
//k存储的是每一条数据的属性名或者方法名
console.log(k + "属性的属性值为" +person1[k]);
}
5. 简单数据类型和复杂数据类型
(1) 堆和栈
- 栈:由操作系统自动分配释放,存放函数的参数值,局部变量的值等。
- 堆:存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
(2) 简单数据类型在内存中的存储
单独开辟内存空间存储,互相之间不会有影响。
(3) 复杂数据类型在内存中的存储
如果将复杂数据类型的数据赋值给一个变量,复杂类型的数据会在内存中创建一个原型,而变量中存储的是指向对象的一个地址,如果将变量赋值给另一个变量,相当于将地址复制一份给了新的变量,两个变量的地址相同,指向的是同一个原型,不论通过哪个地址更改了原型,都是在原型上发生的更改,两个变量下次访问时,都会发生变化。
var p1 = {
name : "zs",
age : 18,
sex : "male"
};
var p = p1;//p1将内部存储的指向对象原型的地址复制给了p
//两个变量是联动的关系,一个变化,会引起另一个变化
p.name = "ls";
console.log(p);//——>name:"ls"
console.log(p1);//——>name:"ls"
注意:对象是数组也是一样。
6. MDN
网站:developer.mozilla.org/zh-CN/
用于查询html、css、html5的应用。
-
如何学习一个方法?
- 方法的功能
- 参数的意义和类型
- 返回值意义和类型
- demo进行测试
7. 数组
(1) 创建数组对象
var arr = new Array();
(2) 检测数组类型
instanceof 检测是否是某个对象类型
例如:arr instanceof Array
(3) 数组常用方法
-
toString()方法
把数组转换成字符串,用逗号分隔每一项。
var arr[1,2,3,4]; console.log(arr.toStirng());//——>1,2,3,4 -
首位数据操作
方法 功能 push() 在数组末尾添加一个或多个元素,参数随意,返回数组操作后的新长度 pop() 删除数字最后一项,不需要传参,返回删除项 shift() 删除数组第一项,不需要传参,返回删除项 unshift() 在数组开头添加一个或多个元素,参数随意,返回数组新的长度 -
合并和拆分
- concat():将两个数组合并成为一个新的数组,原数组不受影响,参数位置可以是一个数组字面量、数组变量、零散的值。
-
slice(start, end):从当前数组中截取一个新的数组,不影响原来的的数组,返回一个新数组,包含从start到end(不包括该元素)的元素。
参数区分正负,正值表示下标位置,负值表示从后面往前数第几个位置。参数可以只传递一个,表示从开始位置截取到字符串结尾。
-
删除、插入、替换
splice(index, howmany,element1,element2...)
index: 删除元素的开始数组下标
howmany:删除元素的个数,可以是0
element:要替换的新的数据,可以是字符串
var arr = [1,2,3,4,5,6,7,8,9];
//删除功能
console.log(arr.splice(2,5));
console.log(arr);//——>[1,2,8,9]
//替换功能
arr.splice(2,5,"haha","hello");
console.log(arr);//——>[1,2,"haha","hello",8,9]
// 插入功能,删除的元素个数为0
arr.splice(2,0,"haha");
console.log(arr);//——>[1,2,"haha",3,4,5,6,7,8,9]
-
查找位置
indexOf(): 查找数据在数组中最先出现的下标,参数为要查找的数据
lastIndexOf(): 查找数据在数组中最后一次出现的下标,参数为要查找的数据
注意:没有查找到返回-1。
-
倒叙
reverse(): 将数组倒叙排列
-
排序
sort(): 默认按照字符编码顺序从小到大排列。如果想要根据数值大小进行排序,必须添加sort的比较函数参数,该函数需要比较a、b两个参数的值,返回一个说明这两个值的相对顺序的数字。
返回值是-1:a排在b前面
返回值是1:a排在b后面
返回值是0:a和b的顺序不变\
// 从大到小降序排列 arr.sort(function(a,b){ if(a > b){ return -1;//表示a要排在b的前面 }else if(a < b){ return 1; //表示a要排在b后面 }else { return 0;//表示a和b不换位置 } }); -
转字符串方法:将数组的所有元素连接到一个字符串中。
join():
var arr = [1,2,3,4,5,6,10,20,30]; var str = arr.join(""); console.log(str);//——>123456102030 var str = arr.join("*");//——>1*2*3*4*5*6*10*20*30 -
清空数组
方式1:arr = [];
方式2:arr.length = 0;
方式3:arr.splice(0, arr.length);
8. 基本包装类型
基本数据类型:byte、short、int、long、float、double、boolean、char。
Js提供的特殊的简单类型对象:String。
基本包装类型:(基本类型的数据str)在进行一些特殊操作(调用方法)时,会暂时被包装成一个临时对象(String类型),(再调用方法),结束后再销毁临时对象。
// 模拟计算机的工作
var str = "这是一个字符串";
// 进行了一个临时的包装
var str4 = new String(str);
var str2 = str4.slice(3,5);
//销毁临时对象
str4 = null;
9. 字符串
(1) 特点
字符串的所有方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串。
由于字符串的不可变,在大量拼接字符串的时候会有效率问题。每拼接一次会占一个内存,再添加一个新的字符串,加载速度会变慢。
// 大量拼接字符串时,会有效率问题
var sum = "";
for (var i = 1 ; i <= 1000000 ; i++) {
sum += i;
}
console.log(sum);
(2) 方法
| 方法 | 功能 |
|---|---|
| str.length | 长度 |
| charAt() | 返回指定下标位置的字符。参数是index字符串的下标。 |
| indexOf() | 返回子串在原始字符串中第一次出现位置的下标。没有的话返回-1。 |
| concat() | 字符串拼接 |
| split() | 分割字符串成一个数组。参数是分割符。 |
| toUpperCase() | 将所有英文字符转换成大写 |
| toLowerCase() | 将所有英文字符转换成小写 |
| slice(start, end) | 从当前字符串中截取一个新的字符串,包含从start到end(不包括该元素)的元素。参数区分正负,正值表示下标位置,负值表示从后面往前数第几个位置。end可以不写,表示从开始位置截取到字符串结尾。 |
| substr(start, howmany) | 从开始位置截取一定长度。start区分正负,正值表示下标位置,负值表示从后面往前数第几个位置。howmany必须为正数,可以不写,表示从开始位置截取到字符串结尾。 |
| substring(start, end) | 用于提取字符串中介于两个指定下标之间的字符。执行方法之前会比较两个参数大小,用小的当作开始位置,大的当作结束位置。从开始位置截取到结束位置(不包含)。 |