一、数组
- 为什么要学习数组
- 之前学习的数据类型,只能存储一个值(比如:Number/String。
- 如果我们想存储班级中所有学生的姓名,此时该如何存储?
- 数组的定义
- 所谓数组,就是将多个元素(通常是同一类型)按一定顺序排列放到一个集合中,那么这个集合我们就称之为数组。
- 数组是一个有序的列表,可以在数组中存放任意的数据,并且数组的长度可以动态的调整。
- 通过数组字面量创建数组
-
// 创建一个空数组 var arr1 = []; // 创建一个包含3个数值的数组,多个数组项以逗号隔开 var arr2 = [1, 3, 4]; // 创建一个包含2个字符串的数组 var arr3 = ['a', 'c']; // 可以通过数组的length属性获取数组的长度 console.log(arr3.length); // 可以设置length属性改变数组中元素的个数 arr3.length = 0;
-
- 获取数组元素
-
数组的取值
[0] 是数组中的第一个元素。[1] 是第二个。数组索引从 0 开始。
// 格式:数组名[下标] 下标又称索引 // 功能:获取数组对应下标的那个值,如果下标不存在,则返回undefined。 var arr = ['red', 'green', 'blue']; arr[0]; // red arr[2]; // blue arr[3]; // 这个数组的最大下标为2,因此返回undefined
-
- 遍历数组
-
遍历:遍及所有,对数组的每一个元素都访问一次就叫遍历。
数组遍历的基本语法:
for(var i = 0; i < arr.length; i++) { // 数组遍历的固定结构 }
-
- 数组中新增元素
-
数组的赋值
// 格式:数组名[下标/索引] = 值; // 如果下标有对应的值,会把原来的值覆盖,如果下标不存在,会给数组新增一个元素。 var arr = ["red", "green", "blue"]; // 把red替换成了yellow arr[0] = "yellow"; // 给数组新增加了一个pink的值 arr[3] = "pink";
- 数组的push方法
// 格式:数组名.push() // 如果下标有对应的值,会把原来的值覆盖,如果下标不存在,会给数组新增一个元素。 var arr = ["red", "green", "blue"]; arr.push(“pink”);
-
二、函数
-
为什么要有函数
如果要在多个地方求1-100之间所有数的和,应该怎么做?
-
什么是函数
把一段相对独立的具有特定功能的代码块封装起来,形成一个独立实体,就是函数。
起个名字(函数名),在后续开发中可以反复调用。
函数的作用就是封装一段代码,将来可以重复使用。
-
函数的定义
//函数声明 function 函数名(){ // 函数体 } //函数表达式 var fn = function() { // 函数体 }
函数定义好后,函数体并不会执行,只要当函数被调用的时候才会执行。函数一般都用来干一件事情,需用使用动词+名词,表示做一件事情tellStory sayHello等。
-
函数的调用
调用函数的语法
函数名();
特点:函数体只有在调用的时候才会执行,调用需要()进行调用。可以调用多次(重复使用)
代码示例
// 声明函数 function sayHi() { console.log("吃了没?"); } // 调用函数 sayHi(); // 求1-100之间所有数的和 function getSum() { var sum = 0; for (var i = 0; i < 100; i++) { sum += i; } console.log(sum); } // 调用 getSum();
-
函数的参数
为什么要有参数
function getSum() { var sum = 0; for (var i = 1; i <= 100; i++) { sum += i; } console.log(); } // 虽然上面代码可以重复调用,但是只能计算1-100之间的值 // 如果想要计算n-m之间所有数的和,应该怎么办呢?
语法:
// 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部 // 带参数的函数声明 function 函数名(形参1, 形参2, 形参...){ // 函数体 } // 带参数的函数调用 函数名(实参1, 实参2, 实参3);
形参和实参
1. 形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。
2. 实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。
var x = 5, y = 6; fn(x,y); function fn(a, b) { console.log(a + b); } //x,y实参,有具体的值。函数执行的时候会把x,y复制一份给函数内部的a和b,函数内部的值是复制的新值,无法修改外部的x,y
三、函数的返回值
-
什么是返回值
当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西,也就是返回值。函数通过return返回一个返回值。
返回值语法:
//声明一个带返回值的函数 function 函数名(形参1, 形参2, 形参...){ //函数体 return 返回值; } //可以通过变量来接收这个返回值 var 变量 = 函数名(实参1, 实参2, 实参3);
函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。
-
返回值总结
-
如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined
-
如果函数使用 return语句,那么跟再return后面的值,就成了函数的返回值
-
如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined
-
函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。
推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。
-
四、函数其他
- 函数的命名
-
规则
由字母、数字、下划线、$符号组成,不能以数字开头
不能是关键字和保留字,例如:for,while,this, name
区分大小写 -
规范
函数名必须有意义
遵守驼峰命名法
建议不要用$作为函数名
-
-
函数的覆盖问题
//如果两个函数都是函数的声明,且函数名相同,那么后边的函数会覆盖前边的 function fn1() { console.log("函数声明"); } function fn1() { console.log("我是第二个fn1"); } fn1();//我是第二个fn1 //如果两个函数都是函数表达式,且函数名相同,函数的调用会调用最近的一个 var fn2 = function () { console.log("第一个函数表达式fn2"); } fn2();//第一个函数表达式fn2 var fn2 = function () { console.log("我是第二个fn2表达式"); }
- 函数的内置对象
-
JavaScript中,arguments对象是比较特别的一个对象,实际上是当前函数的一个内置属性。也就是说所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有的实参。arguments是一个伪数组,因此也可以进行遍历。
arguments数组中前几个元素是函数的参数
callee属性:函数的本身
callee.name:函数的名字
length属性:实参的个数
callee.length:形参的个数
-
-
匿名函数
-
匿名函数:没有名字的函数
匿名函数如何使用?
匿名函数不能通过直接调用来执行,因此可以通过匿名函数的自调用的方式来执行
(function () { alert(123); })();
-
- 函数是一种数据类型
-
函数是引用数据类型
<script> //1.可以typeof一下 function run(){ } console.log(typeof run)//function
-
- 函数作为参数
//因为函数也是一种类型,可以把函数作为参数传入 function fnNum1(x) { console.log(x); x(); } fnNum1(function () { console.log("函数作为参数传入") }); //因为函数是一种类型,可以把函数作为参数反出 function fnNum2(x) { return x } // var num1 = fnNum2(120); var num1 = fnNum2(function () { console.log("函数作为参数反出") }); console.log(num1); num1();
五、预解析
JavaScript引擎在对JavaScript代码进行解释执行之前,会对JavaScript代码进行预解析,在预解析阶段,会将以关键字var和function开头的语句块提前进行处理。
关键问题是怎么处理呢?
当变量和函数的声明处在作用域比较靠后的位置的时候,变量和函数的声明会被提升到作用域的开头。
-
函数提升
func(); function func(){ alert("Funciton has been called"); } //由于JavaScript的预解析机制,上面的代码就等效于: function func(){ alert("Funciton has been called"); } func()
-
变量提升
看完函数声明的提升,再来看一个变量声明提升的例子
alert(a); var a = 1;
由于JavaScript的预解析机制,上面这段代码,alert出来的值是undefined,如果没有预解析,代码应该会直接报错a is not defined,而不是输出值,不是说要提前的吗?那不是应该alert出来1,为什么是undefined?
所以我们说的提升,是声明的提升。
那么再回过头看,上面的代码就等效于
var a; //这里是声明 alert(a);//变量声明之后并未有初始化和赋值操作,所以这里是 undefined a = 1
所以变量的提升只是声明的提升
-
函数同名
通过上一小节的内容,我们对变量、函数声明提升已经有了一个最基本的理解。那么接下来,我们就来分析一些略复杂的情况。
观察下面这段代码
func1(); function func1(){ console.log('This is func1'); } func1(); function func1(){ console.log('This is last func1'); }
输出结果为
This is last func1
This is last func1
原因分析:由于预解析机制,func1的声明会被提升,提升之后的代码为
function func1(){ console.log('This is last func1'); } func1(); func1();
同名的函数,后面的会覆盖前面的,所以两次输出结果都是This is last func1。
-
变量和函数同名
alert(foo); function foo(){} var foo = 2;
当出现变量声明和函数同名的时候,只会对函数声明进行提升,变量会被忽略。所以上面的代码的输出结果为
function foo(){}
我们还是来把预解析之后的代码展现出来:
function foo(){}; alert(foo); foo = 2;
再来看一种
var num = 1; function num () { alert( num ); } num();
代码执行结果为:
Uncaught TypeError: num is not a function
按照常规的书写顺序,同名的函数与变量,变量会覆盖函数
直接上预解析后的代码:
function num(){ alert(num); } var num = 1; num();
-
预解析是分作用域的
//提升原则是提升到变量运行的环境(作用域)中去。 function showMsg() { var msg = 'This is message'; } alert(msg); // msg未定义 //还是直接把预解析之后的代码写出来: function showMsg() { var msg; msg = 'This is message'; } alert(msg); // msg未定义
-
函数表达式不会提升
func(); var func = function(){ alert("我被提升了"); }; //这里会直接报错,func is not a function,原因就是函数表达式,并不会被提升。只是简单地当做 //变量声明进行了处理,如下 var func; func(); func = function(){ alert("我被提升了"); }
六、作用域
-
全局作用域
直接编写在 script 标签之中的JS代码,都是全局作用域;
或者是一个单独的 JS 文件中的。
全局作用域在页面打开时创建,页面关闭时销毁;
在全局作用域中有一个全局对象 window(代表的是一个浏览器的窗口,由浏览器创建),可以直接使用。
所有创建的变量都会作为 window 对象的属性保存。所有创建的函数都会作为 window 对象的方法保存。
-
局部作用域(函数作用域):
在函数内部就是局部作用域,这个代码的名字只在函数的内部起作用
调用函数时创建函数作用域,函数执行完毕之后,函数作用域销毁;
每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的。
将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。
例如:
function f1() { function f2() { } } var num = 456; function f3() { function f4() { } }
-
隐式全局变量
声明变量使用`var`, 如果不使用`var`声明的变量就是全局变量( 禁用 )
因为在任何代码结构中都可以使用该语法. 那么再代码维护的时候会有问题. 所以除非特殊原因不要这么用.
下面的代码的错误
function foo () { var i1 = 1 // 局部 i2 = 2, // 全局 i3 = 3; // 全局 }
七、对象
-
为什么要有对象
function printPerson(name, age, sex....) { } // 函数的参数如果特别多的话,可以使用对象简化 function printPerson(person) { console.log(person.name); …… }
-
什么是对象
现实生活中:万物皆对象,对象是一个具体的事物,一个具体的事物就会有行为和特征。
举例: 一辆车、一部手机、一台电脑、一张桌子
车是一类事物,门口停的那辆车才是对象。特征:红色、四个轮子,行为:驾驶、刹车
-
JavaScript中的对象
JavaScript中的对象其实就是生活中对象的一个抽象。
JavaScript的对象是无序属性的集合。
其属性可以包含基本值、对象或函数。对象就是一组没有顺序的值。我们可以把JavaScript中的对象想象成键值对,其中值可以是数据和函数。
Class=”d1”
Key = value
对象的行为和特征
特征---属性
行为---方法
Tips:
事物的特征在对象中用属性来表示。
事物的行为在对象中用方法来表示。
-
对象创建方式
//对象字面量 var o = { name: 'zs', age: 18, sex: true, sayHi: function () { console.log(this.name); } }; //new Object()创建对象 var person = new Object(); person.name = 'lisi'; person.age = 35; person.job = 'actor'; person.sayHi = function(){ console.log('Hello,everyBody'); } //工厂函数创建对象 function createPerson(name, age, job) { var person = new Object(); person.name = name; person.age = age; person.job = job; person.sayHi = function(){ console.log('Hello,everyBody'); } return person; } var p1 = createPerson('张三', 22, 'actor'); //自定义构造函数 function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayHi = function(){ console.log('Hello,everyBody'); } } var p1 = new Person('张三', 22, 'actor');
-
属性和方法
-
如果一个变量属于一个对象所有,那么该变量就可以称之为该对象的一个属性,属性一般是名词,用来描述事物的特征
-
如果一个函数属于一个对象所有,那么该函数就可以称之为该对象的一个方法,方法是动词,描述事物的行为和功能
-
-
new关键字
-
构造函数,是一种特殊的函数。主要用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
-
构造函数用于创建一类对象,首字母要大写。
-
构造函数要和new一起使用才有意义。
-
-
new在执行时会做三件事情:
-
new会在内存中创建一个新的空对象
-
new会让this指向这个新的对象
-
new会返回这个新对象
-
-
-
this详解
JS中this的指向问题,有时会让人难以捉摸,随着学习的深入,我们可以逐渐了解。
函数内部的this几个特点:
-
函数在定义的时候this是不确定的,只有在调用的时候才可以确定
-
一般函数直接执行,内部this指向全局window
-
函数作为一个对象的方法,被该对象所调用,那么this指向的是该对象
-
构造函数中的this其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个隐式对象身上,后续通过new关键字来调用,从而实现实例化
-
-
对象的使用
遍历对象的属性
通过for..in语法可以遍历一个对象
删除对象的属性
function fun() { this.name = 'mm'; } var obj = new fun(); console.log(obj.name); // mm delete obj.name; console.log(obj.name); // undefined
八、JavaScript 错误
- JavaScript错误
-
当 JavaScript 引擎执行 JavaScript 代码时,会发生各种错误。可能是语法错误,通常是程序员造成的编码错误或错别字。可能是拼写错误或语言中缺少的功能(可能由于浏览器差异)。可能是由于来自服务器或用户的错误输出而导致的错误。当然,也可能是由于许多其他不可预知的因素。
-
- JavaScript try 和 catch
-
try 语句允许我们定义在执行时进行错误测试的代码块。
catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。
JavaScript 语句 try 和 catch 是成对出现的。
在下面的例子中,我们故意在 try 块的代码中写了一个错字。catch 块会捕捉到 try 块中的错误,并执行代码来处理它。var txt=""; function message() { try { adddlert("Welcome guest!"); } catch(err) { txt="本页有一个错误。\n\n"; txt+="错误描述:" + err.message + "\n\n"; txt+="点击确定继续。\n\n"; alert(txt); } }
-
-
finally 语句
-
finally 语句不论之前的 try 和 catch 中是否产生异常都会执行该代码块。
function myFunction() { var message, x; message = document.getElementById("p01"); message.innerHTML = ""; x = document.getElementById("demo").value; try { if(x == "") throw "值是空的"; if(isNaN(x)) throw "值不是一个数字"; x = Number(x); if(x > 10) throw "太大"; if(x < 5) throw "太小"; } catch(err) { message.innerHTML = "错误: " + err + "."; } finally { document.getElementById("demo").value = ""; } }
-
- Throw 语句
-
throw 语句允许我们创建自定义错误。
正确的技术术语是:创建或抛出异常(exception)。
如果把 throw 与 try 和 catch 一起使用,那么您能够控制程序流,并生成自定义的错误消息。
语法:throw exception
异常可以是 JavaScript 字符串、数字、逻辑值或对象。
本例检测输入变量的值。如果值是错误的,会抛出一个异常(错误)。catch 会捕捉到这个错误,并显示一段自定义的错误消息:
function myFunction() { var message, x; message = document.getElementById("message"); message.innerHTML = ""; x = document.getElementById("demo").value; try { if(x == "") throw "值为空"; if(isNaN(x)) throw "不是数字"; x = Number(x); if(x < 5) throw "太小"; if(x > 10) throw "太大"; } catch(err) { message.innerHTML = "错误: " + err; } }
-
九、内置对象
JavaScript中的对象分为4种:内置对象、浏览器对象、自定义对象、DOM对象。JavaScript 提供多个内置对象:Math/Array/Number/String/Boolean ... ...。对象只是带有属性和方法的特殊数据类型。学习一个内置对象的使用,只要学会其常用的成员的使用(通过查文档学习)。内置对象的方法很多,我们只需要知道内置对象提供的常用方法,使用的时候查询文档。
-
如何学习一个方法?
方法的功能、参数的意义和类型、返回值意义和类型、demo进行测试
十、Math对象
Math对象不是构造函数,它具有数学常数和函数的属性和方法,都以静态成员的方式提供。
跟数学相关的运算来找Math中的成员(求绝对值,取整)。
-
常用属性
Math.PI 圆周率
-
常用方法
Math.random() 生成随机数
Math.floor() 向下取整Math.ceil() 向上取整
Math.round() 取整,四舍五入
Math.abs() 绝对值
Math.max() 最大值Math.min() 最小值
Math.sin() 正弦
Math.cos() 余弦
Math.pow() 求指数次幂
Math.sqrt() 求平方根
十一、Date对象
创建Date实例用来处理日期和时间。Date 对象基于1970年1月1日(世界标准时间)起的毫秒数。
- 创建日期对象
-
Date()是构造函数
var date = new Date();
-
- 日期原始值
- valueOf();原始值,获取1970年1月1日至今的毫秒数
- getTime():获取1970年1月1日至今的毫秒数
-
获取日期指定部分
getMilliseconds()
getSeconds() // 返回0-59
getMinutes() // 返回0-59
getHours() // 返回0-23
getDay() // 返回星期几 0周日 6周6
getDate() // 返回当前月的第几天
getMonth() // 返回月份,***从0开始***
getFullYear() //返回4位的年份 如 2016
十二、Array对象
- 创建数组对象的两种方式
-
字面量方式
-
new Array()
-
- 检测一个对象是否是数组
-
instanceof 如果返回true就是数组,false是非数组
Array.isArray() 如果返回true就是数组,false是非数组
valueOf() 返回数组对象本身
-
- 栈操作(先进后出)
-
栈:表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素
push() 添加元素
pop() 删除元素
-
- 队列操作(先进先出)
-
shift() 删除元素
-
unshift() 添加元素
-
- 排序方法
-
reverse() 翻转数组
-
sort(sortby); sortby,可选参数,规定排序顺序,必须是函数 如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
-
-
操作方法
-
concat() 把参数拼接到当前数组、 或者用于连接两个或多个数组
-
slice(start,end)
从start开始截取元素,到end结束,包括start,不包括end,返回新数组,start,end是索引,不会改变原始数组 -
splice() 从start开始截取元素,截取length个,返回新数组,start是索引,length是个数,但是会删除原数组中截取出来的内容
-
- 位置方法
-
indexOf() 都是找位置 返回索引值 没有找到返回 -1,第一次出现位置
-
lastIndexOf() 如果没找到返回-1,元素最后一次出现的位置
上述方法只是查找顺序不一样 结果都是索引值
-
- 数组迭代方法
-
迭代(即是不停的代换)
-
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
array.forEach(function(currentValue, index)) currentValue 必需。当前元素 Index 可选。当前元素索引值
可以拿到每个数组中的值,没有返回值
问题思考:迭代方法究竟会不会改变原来的值?
-
every() some()方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
some(),every()方法的参数是一个回调函数,回调函数中的第一个参数是数组的元素,第二个参数是数组的索引
some(),every()方法都会返回新的数组
var flag1 = arr3.every(function (value, index) { return value > 55; }) console.log(flag1); var flag2 = arr3.some(function (value, index) { return value >= 88; }) console.log(flag2)
every():判断回调函数中的表达式是否全部满足,如果满足,返回值就是true,只要有一个不满足就是false
some判断回调函数中的表达式是否有一个满足,如果至少一个满足,返回值就是true
-
filter ()与map ()
filter()根据指定条件过滤元素,返回新数组 ;
map()数根据数学运算,返回新数组
//filter():根据指定条件过滤元素,返回新数组 var new1 = arr.filter(function (value, index) { return value >= 33; }) console.log(new1); //map():根据数学运算,返回新数组 var new2 = arr.map(function (value, index) { return Math.pow(value, 2); })
-
- 清空数组
-
方式1 推荐
arr = [] -
方式2
arr.length = 0 -
方式3
arr.splice(0, arr.length)
-
- 数组转化字符串
-
join()数组转化为字符串,以参数分割
-
十三、基本包装类型
-
为了方便操作基本数据类型,JavaScript还提供了三个特殊的引用类型:String/Number/Boolean
// 下面代码的问题? // s1是基本类型,基本类型是没有方法来操作的 var s1 = 'zhangsan'; var s2 = s1.substring(5); // 当调用s1.substring(5)的时候,先把s1包装成String类型的临时对象,再调用substring方法,最后销毁临时对象, 相当于: var s1 = new String('zhangsan'); var s2 = s1.substring(5); s1 = null; // 创建基本包装类型的对象 var num = 18; //数值,基本类型 var num = Number('18'); //类型转换 var num = new Number(18); //基本包装类型,对象 // Number和Boolean基本包装类型基本不用,使用的话可能会引起歧义。例如: var b1 = new Boolean(false); var b2 = b1 && true; // 结果是什么
十四、String对象
-
字符串的不可变
var str = 'abc'; str = 'hello'; // 当重新给str赋值的时候,常量'abc'不会被修改,依然在内存中 // 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变 // 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
-
创建字符串对象
var str = new String('Hello World'); // 获取字符串中字符的个数 console.log(str.length);
-
字符串对象的常用方法
字符串所有的方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串
字符方法
charAt() //获取指定位置处字符
charCodeAt() //获取指定位置处字符的ASCII码
str[0] //HTML5,IE8+支持 和charAt()等效
2 字符串操作方法
concat() //拼接字符串,等效于+,+更常用
slice(start,end) //从start位置开始,截取到end位置,end取不到
substring(start,end) //从start位置开始,截取到end位置,end取不到
substr(start,length) 从start位置开始,截取length个字符
indexOf() //返回指定内容在元字符串中的位置,,如果没有,返回-1;(从前往后,检索到第一个就结束)
lastIndexOf() //返回指定内容在元字符串中的位置,,如果没有,返回-1;(从后往前,检索到第一个就结束)
trim() //只能去除字符串前后的空白
大小写转换方法
toUpperCase() //转换大写
toLowerCase() //转换小写
search()//方法用于检索字符串中指定的子字符串,返回子字符串的起始位置
replace(old,new) //替换字符串替换字符串 new替换old
split() //分割字符串 返回的是一个数组。。数组的元素就是以参数的分割的