JavaScript网页编程(Ⅱ)
JavaScript 函数
函数的概念
在 JS 里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用。虽然 for循环语句也能实现一些简单的重复操作,但是比较具有局限性,此时我们就可以使用 JS 中的函数。
函数:就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。
函数的使用
函数在使用时分为两步:声明函数和调用函数。
声明函数:
// 声明函数
function 函数名() {
//函数体代码
}
注意:function 是声明函数的关键字,必须小写;由于函数一般是为了实现某个功能才定义的, 所以通常我们将函数名命名为动词,比如 getSum。
调用函数:
// 调用函数
函数名(); // 通过调用函数名来执行函数体代码
注意: 调用的时候千万不要忘记添加小括号;声明函数本身并不会执行代码,只有调用函数时才会执行函数体代码。
函数的封装:
函数的封装是把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口。
函数的参数
形参和实参:在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参,而在调用该函数时,同样也需要传递相应的参数,这些参数被称为实参。
参数的作用 : 在函数内部某些值不能固定,我们可以通过参数在调用函数时传递不同的值进去。
代码如下:
// 带参数的函数声明
function 函数名(形参1, 形参2 , 形参3...) { // 可以定义任意多的参数,用逗号分隔
// 函数体
}
// 带参数的函数调用
函数名(实参1, 实参2, 实参3...);
函数参数的传递过程:
例如:
// 声明函数
function getSum(num1, num2) {
console.log(num1 + num2);
}
// 调用函数
getSum(1, 3); // 4
getSum(6, 5); // 11
调用的时候实参值是传递给形参的, 形参简单理解为:不用声明的变量,实参和形参的多个参数之间用逗号(,)分隔。
函数形参和实参个数不匹配问题:
注意:在JavaScript中,形参的默认值是undefined。
小结:函数可以带参数也可以不带参数;声明函数的时候,函数名括号里面的是形参,形参的默认值为 undefined;调用函数的时候,函数名括号里面的是参;形参的个数可以和实参个数不匹配,但是结果不可预计,我们尽量要匹配。
函数的返回值
return 语句:
有的时候,我们会希望函数将值返回给调用者,此时通过使用 return 语句就可以实现。
return 语句的语法格式如下:
// 声明函数
function 函数名(){
...
return 需要返回的值;
}
// 调用函数
函数名(); // 此时调用函数就可以得到函数体内return 后面的值
在使用 return 语句时,函数会停止执行,并返回指定的值,如果函数没有 return ,返回的值是 undefined。
return 终止函数,return 语句之后的代码不被执行。
function add(num1,num2){
//函数体
return num1 + num2; // 注意:return 后的代码不执行
alert('我不会被执行,因为前面有 return');
}
var resNum = add(21,6); // 调用函数,传入两个实参,并通过 resNum 接收函数返回值
alert(resNum); // 27
return 的返回值:return 只能返回一个值。如果用逗号隔开多个值,以最后一个为准。
function add(num1,num2){
//函数体
return num1,num2;
}
var resNum = add(21,6); // 调用函数,传入两个实参,并通过 resNum 接收函数返回值
alert(resNum); //
函数都是有返回值的,如果有return 则返回 return 后面的值,如果没有return 则返回 undefined。
拓展:break ,continue ,return 的区别?
- break :结束当前的循环体(如 for、while)。
- continue :跳出本次循环,继续执行下次循环(如 for、while)。
- return :不仅可以退出循环,还能够返回 return 语句中的值,同时还可以结束当前的函数体内的代码。
arguments的使用
当我们不确定有多少个参数传递的时候,可以用 arguments 来获取。
在 JavaScript 中,arguments 实际上它是当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有实参。
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
- 具有 length 属性
- 按索引方式储存数据
- 不具有数组的 push , pop 等方法
函数的两种声明方式
自定义函数方式(命名函数):利用函数关键字 function 自定义函数方式。
// 声明定义方式
function fn() {...}
// 调用
fn();
因为有名字,所以也被称为命名函数;调用函数的代码既可以放到声明函数的前面,也可以放在声明函数的后面。
函数表达式方式(匿名函数):
利用函数表达式方式的写法如下:
// 这是函数表达式写法,匿名函数后面跟分号结束
var fn = function(){...};
// 调用的方式,函数调用必须写到函数体下面
fn();
因为函数没有名字,所以也被称为匿名函数;这个fn 里面存储的是一个函数;函数表达式方式原理跟声明变量方式是一致的;函数调用的代码必须写到函数体后面。
JavaScript 作用域
作用域
作用域概述:通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
JavaScript(es6前)中的作用域有两种:
- 全局作用域
- 局部作用域(函数作用域)
全局作用域:作用于所有代码执行的环境(整个 script 标签内部)或者一个独立的 js 文件。
局部作用域 (函数作用域):作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系,所以也称为函数作用域。
JS 没有块级作用域,块作用域由 { } 包括。在其他编程语言中(如 java、c#等),在 if 语句、循环语句中创建的变量,仅仅只能在本 if 语句、本循环语句中使用,如下面的Java代码:
if(true){
int num = 123;
system.out.print(num); // 123
}
system.out.print(num); // 报错
Js中没有块级作用域(在ES6之前)。
if(true){
var num = 123;
console.log(123); //123
}
console.log(123); //123
变量的作用域
在JavaScript中,根据作用域的不同,变量可以分为两种:
- 全局变量
- 局部变量
在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。全局变量在代码的任何位置都可以使用;在全局作用域下 var 声明的变量是全局变量;特殊情况下,在函数内不使用 var 声明的变量也是全局变量(不建议使用)。
在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)。局部变量只能在该函数内部使用;在函数内部 var 声明的变量是局部变量; 函数的形参实际上就是局部变量。
全局变量和局部变量的区别:
全局变量:在任何一个地方都可以使用,只有在浏览器关闭时才会被销毁,因此比较占内存。局部变量:只在函数内部使用,当其所在的代码块被执行时,会被初始化;当代码块运行结束后,就会被
销毁,因此更节省内存空间。
作用域链
只要是代码,就至少有一个作用域;写在函数内部的局部作用域;如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。
根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链。
作用域链:采取就近原则的方式来查找变量最终的值。
JavaScript 预解析
JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析和代码执行。
预解析:在当前作用域下, JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中进行提前声明或者定义。
代码执行: 从上到下执行JS语句。
预解析只会发生在通过 var 定义的变量和 function 上。学习预解析能够让我们知道为什么在变量声明之前访问变量的值是 undefined,为什么在函数声明之前就可以调用函数。
变量预解析和函数预解析
预解析也叫做变量、函数提升。
变量预解析(变量提升):
变量提升: 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。
函数预解析(函数提升):
函数提升: 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。
JavaScript 对象
什么是对象?现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一张网页、一个与远程服务器的连接也可以是“对象”。
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。对象是由属性和方法组成的。
属性:事物的特征,在对象中用属性来表示(常用名词);方法:事物的行为,在对象中用方法来表示(常用动词)。
保存一个值时,可以使用变量,保存多个值(一组值)时,可以使用数组。JS 中的对象表达结构更清晰,更强大。
创建对象的三种方式
在 JavaScript 中,现阶段我们可以采用三种方式创建对象(object):
- 利用字面量创建对象
- 利用 new Object 创建对象
- 利用构造函数创建对象
利用字面量创建对象:
对象字面量:就是花括号 { } 里面包含了表达这个具体事物(对象)的属性和方法。
{ } 里面采取键值对的形式表示;
创建的案例:
var star = {
name : 'pink',
age : 18,
sex : '男',
sayHi : function(){
alert('大家好啊~');
}
};
对象的调用:对象里面的属性调用 : 对象.属性名 ,这个小点 . 就理解为“ 的 ”;对象里面属性的另一种调用方式 : 对象[‘属性名’],注意方括号里面的属性必须加引号;对象里面的方法调用:对象.方法名() ,注意这个方法名字后面一定加括号。
代码如下:
console.log(star.name) // 调用名字属性
console.log(star['name']) // 调用名字属性
star.sayHi(); // 调用 sayHi 方法,注意,一定不要忘记带后面的括号
变量、属性、函数、方法总结:
- 变量:单独声明赋值,单独存在
- 属性:对象里面的变量称为属性,不需要声明,用来描述该对象的特征
- 函数:单独存在的,通过“函数名()”的方式就可以调用
- 方法:对象里面的函数称为方法,方法不需要声明,使用“对象.方法名()”的方式就可以调用,方法用来描述该对象的行为和功能
利用new Object创建对象:
代码案例:
var andy = new Obect();
andy.name = 'pink';
andy.age = 18;
andy.sex = '男';
andy.sayHi = function(){
alert('大家好啊~');
}
Object() :第一个字母大写;new Object() :需要 new 关键字;使用的格式:对象.属性 = 值;
利用构造函数创建对象:
构造函数 :是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在 js 中,使用构造函数要时要注意以下两点:
- 构造函数用于创建某一类对象,其首字母要大写
- 构造函数要和 new 一起使用才有意义
代码案例:
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sayHi = function() {
alert('我的名字叫:' + this.name + ',年龄:' + this.age + ',性别:' + this.sex);
}
}
var bigbai = new Person('大白', 100, '男');
var smallbai = new Person('小白', 21, '男');
console.log(bigbai.name);
console.log(smallbai.name);
注意
- 构造函数约定首字母大写。
- 函数内的属性和方法前面需要添加 this ,表示当前对象的属性和方法。
- 构造函数中不需要 return 返回结果。
- 当我们创建对象的时候,必须用 new 来调用构造函数。
构造函数和对象:
构造函数,如 Stars(),抽象了对象的公共部分,封装到了函数里面,它泛指某一大类(class);
创建对象,如 new Stars(),特指某一个,通过 new 关键字创建对象的过程我们也称为对象实例化;
new 关键字
new 在执行时会做四件事情:
- 在内存中创建一个新的空对象。
- 让 this 指向这个新的对象。
- 执行构造函数里面的代码,给这个新对象添加属性和方法。
- 返回这个新对象(所以构造函数里面不需要return)。
遍历对象属性
for…in 语句用于对数组或者对象的属性进行循环操作。
其语法如下:
for (变量 in 对象名字) {
// 在此执行代码
}
语法中的变量是自定义的,它需要符合命名规范,通常我们会将这个变量写为 k 或者 key。
for (var k in obj) {
console.log(k); // 这里的 k 是属性名
console.log(obj[k]); // 这里的 obj[k] 是属性值
}
JavaScript 内置对象
内置对象
JavaScript 中的对象分为3种:自定义对象 、内置对象、 浏览器对象。前面两种对象是JS 基础 内容,属于 ECMAScript; 第三个浏览器对象属于我们JS 独有的;内置对象就是指 JS 语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能(属性和方法);内置对象最大的优点就是帮助我们快速开发;JavaScript 提供了多个内置对象:Math、 Date 、Array、String等。
查文档
MDN:学习一个内置对象的使用,只要学会其常用成员的使用即可,我们可以通过查文档学习,可以通过MDN/W3C来查询。
Mozilla 开发者网络(MDN)提供了有关开放网络技术(Open Web)的信息,包括 HTML、CSS 和万维网及HTML5 应用的 API。
Math对象
Math 对象不是构造函数,它具有数学常数和函数的属性和方法。跟数学相关的运算(求绝对值,取整、最大值等)可以使用 Math 中的成员。
随机数方法 random():random() 方法可以随机返回一个小数,其取值范围是 [0,1),左闭右开 0 <= x < 1。
得到一个两数之间的随机整数,包括两个数在内:
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
日期对象
Date 概述:Date 对象和 Math 对象不一样,他是一个构造函数,所以我们需要实例化后才能使用; Date 实例用来处理日期和时间。
Date()方法的使用:
- 获取当前时间必须实例化:
var now = new Date();
console.log(now);
- Date() 构造函数的参数
如果括号里面有时间,就返回参数里面的时间。例如日期格式字符串为‘2019-5-1’,可以写成new Date(‘2019-5-1’) 或者 new Date(‘2019/5/1’)。如果Date()不写参数,就返回当前时间。
日期格式化:
需要获取日期指定的部分,所以我们要手动的得到这种格式。
获取日期的总的毫秒形式:
Date 对象是基于1970年1月1日(世界标准时间)起的毫秒数。
我们经常利用总的毫秒数来计算时间,因为它更精确。
// 实例化Date对象
var now = new Date();
// 1. 用于获取对象的原始值
console.log(date.valueOf())
console.log(date.getTime())
// 2. 简单写可以这么做
var now = + new Date();
// 3. HTML5中提供的方法,有兼容性问题
var now = Date.now();
数组对象
数组对象的创建:
创建数组对象的两种方式:字面量方式和 new Array()。
检测是否为数组:
- instanceof 运算符,可以判断一个对象是否属于某种类型
- Array.isArray()用于判断一个对象是否为数组,isArray() 是 HTML5 中提供的方法
var arr = [1, 23];
var obj = {};
console.log(arr instanceof Array); // true
console.log(obj instanceof Array); // false
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
添加删除数组元素的方法:
数组排序:
var arr = [1, 64, 9, 6];
arr.sort(function(a, b) {
return b - a; // 降a序
// return a - b; // 升序
});
console.log(arr);
数组索引方法:
数组转换为字符串:
查询:
字符串对象
基本包装类型:为了方便操作基本数据类型,JavaScript 还提供了三个特殊的引用类型:String、Number和 Boolean。基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。
// 1. 生成临时变量,把简单类型包装为复杂数据类型
var temp = new String('andy');
// 2. 赋值给我们声明的字符变量
str = temp;
// 3. 销毁临时变量
temp = null;
字符串的不可变:
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
var str = 'abc';
str = 'hello';
// 当重新给 str 赋值的时候,常量'abc'不会被修改,依然在内存中
// 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
// 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
var str = '';
for (var i = 0; i < 100000; i++) {
str += i;
}
console.log(str); // 这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间
根据字符返回位置:
字符串所有的方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串。
根据位置返回字符:
字符串操作方法(重点):
replace()方法:
replace() 方法用于在字符串中用一些字符替换另一些字符。
其使用格式如下:
replace(被替换的字符串, 要替换为的字符串);
split()方法:
split()方法用于切分字符串,它可以将字符串切分为数组。在切分完毕之后,返回的是一个新数组。
例如下面代码:
var str = 'a,b,c,d';
console.log(str.split(',')); // 返回的是一个数组 [a, b, c, d]
课下查阅:
- toUpperCase() //转换大写
- toLowerCase() //转换小写
JavaScript 简单类型与复杂类型
简单类型与复杂类型
简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型。
- 值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型
string ,number,boolean,undefined,null - 引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型
通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等
堆和栈
堆栈空间分配区别:
栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;简单数据类型存放到栈里面。
堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型存放到堆里面。
简单类型的内存分配
- 值类型(简单数据类型): string ,number,boolean,undefined,null
- 值类型变量的数据直接存放在变量(栈空间)中
复杂类型的内存分配
- 引用类型(复杂数据类型):通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等
- 引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中
简单类型传参和复杂类型传参
函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。
函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。