1.几组名词
1.1.javascrpit与 web标准
W3c制定的,web标准分成三个部分:
结构:xml,html (你是谁)
表现:css (长什么样)
行为:javascript (能做什么)
1.2.javascript与jquery
jquery就是众多工具库中应用最广泛的。
Underscore.js 是以_开头。(花一点时间自己看看,写在简历中)
Jquery.js 是$开头。
也可以自己写自己用的工具库。
1.3.javascript与 ECMA script
由三个部分组成:
1.ecmascript: js 最核心的语法:变量,运算符,数组,函数…
2.Dom:就是操作document元素,div,a,li,…
3.Bom:浏览器相关的操作。Window.location.href
js可以在 “ 两端 ”运行:
浏览器:把js代码嵌入到html中。
服务器:
ECMA是European Computer Manufacturers Association的缩写,即欧洲计算机制造商协会。欧洲计算机制造商协会是制定信息传输与通讯的国际化标准组织。
ECMAScript是一种语言标准,javascript是这个标准的具体实现之一。也就是说,还有其它的语言也实现了这个标准。ActionScript(flash)
2.JS最最基础内容
2.1.变量
用来保存数据的。
2.1.1.js中变量是没有类型
在js中,变量有没有类型?js中变量是没有类型的,因为js是一个弱语法的脚本语言。
Var a = 1;
a =” abc “;
由于变量a中既可以放数字,也可以放字符中,所以,我们说变量是没有类型的。
对比一下:在c中,变量是有的类型的
Int a = 1; // 这里有一个关键字int,也就是说,这个变量a中只能放整数。
A = “abc” //在c语言中,会报错!!!!
2.1.2.js数据类型有两大类
基本数据类型
Number,string,boolean,undefind,null
引用数据类型( 下面三种都是对象 )
数组
对象
Function
2.2.运算符与表达式
2.2.1.算术运算符
-
-
- / % (求余,取模) ++ –
-
%:在可以在表示一个区间时使用。例如,在学习轮播图时,图片的张数它的下标是[0,n-1] ,在点击下一张时,
Index++; //这里index不断自增,如何确保存index在有效的范围[0,n-1]之内?
If(index == n)
Index = 0;
Index = index % n;
上面还可以简写:
Index %= n;
2.2.2.位运算符
只对整数进行运行运算。先把数值转成2进制,然后进行位操作。
2.2.2.1.把数值转成二进制:
数值.toString(2)
2.2.2.2.把二进制转成10进制(了解)
0b二进制
与(&) 或(|) 异或(^) 非(~) 左移(<<)右移(>>)
2.2.3.关系运算符
== < === !=
注意:= 和 == 和===的区别?
= 表示赋值,把=右边的值保存到左边的变量中。
== 判断相等。只判断值是否相等,而不管类型是否相等。 我们写代码时,尽量保持一点:你要能够知道当前的这个变量中的数据是什么类型。
=== 判断全等。在类型要相等,值也要相等。
2.2.4.逻辑运算符
&& || !
我有两个择偶标准:
(1)长的漂亮
(2)有钱
这两个必须要同时满足!
长的漂亮 && 有钱
短路是:如果第一个条件不成立,则直接返回false。根本就不执行第二个条件判断
我有两个择偶标准:
(1)长的漂亮
(2)或者 : 有钱
这两个有一个成立即可。
长的漂亮 || 有钱
短路是:如果第一个条件成立,则直接返回true。根本不执行第二个条件判断。
2.2.4.1.改进代码1
下面的代码,可以用&&来代替。
if(a==b)
stop();
2.2.4.2.参数默认值
p = event || window.event;
(在es6中,已经新增加了一个功能就是参数的默认值功能。)
2.2.5.赋值运算符
Var a = 2; //把右边的值保存到左边的变量中。
Var a += 2;
-=
/=
%=
Var a = !b;
Var a != b
2.2.6.条件运算符
唯一的一个三元(有三个操作数)运算符。
If( a > b) {
c = a;
}
Else{
c = b;
}
Var c = a > b ? a : b ;
可以用来简化if else结构。
2.2.7.typeof 运算符
typeof不是一个函数,是一个运算符。
运算符不一定非要是符号,也可以是关键字。类似的,还有一个 delete 。它也是一个运算符,它的功能是删除对象的某个属性。
2.3.三大结构
2.3.1.顺序结构
2.3.2.选择结构
if else;
var week=“星期一”;
if( week == “星期一”){
console.info(“黑色星期一”);
}
if( week == “星期二”){
console.info(“明天是周末”);
}
if( week == “星期三”){
console.info(“今天是小周末”);
}
if( week == “星期四”){
console.info(“再坚持一下”);
}
if( week == “星期五”){
console.info(“明天是大周末”);
}
else{
console.info(“放假k…”);
} 结果是:
为什么?
if elseif elseif else;
switch case
作业1:
如果一个数i:
(1)可以被3整除,就输出”被3除”
(2)可以被5整除,就输出”被5除”
(3)可以被15整除,就输出”被15除”
(4)其它:就输出“不能被3,5除”
作业2:
一个分数mark:
如果
1)90以上,输出”A”
2)80以上,输出”B”
3)70以上,输出”C”
2.3.3.循环结构
2.3.3.1.While
If( 条件 ){
//代码段
} While( 条件 ){
//代码段
}
如果条件成立,只执行一次代码段 如果条件成立,只执行代码段,直到条件不成立为止。
循环一般要用三个要素:
(1)变量的初始值。
(2)循环体是什么?
(3)循环的条件
例:求sn = 1+1/2+1/3+… 1/n
(1)求前100项。
作业:
1)上面这个问题,用for来做
2)上面这个问题,用do…while来做。
3)
通过break跳出循环:
4)问,什么时候这个sn会 大于 5。当sn大于5时,n就是多少?
结果如下:
提问:
sn = 1+1/2+1/3+…1/n,当n趋于正无穷时,问sn是多少?
2.3.3.2.do …while
Do…while与while的区别在于:
(1)do…while先做循环体,后检查条件
(2)While,先检查条件,再做循环。
2.3.3.3.for
格式:
For( 表达式1 ;表达式2; 表达式3){
//循环体。
}
问题是:
表达式1要执行 1次
表达式2要执行 多次
表达式3要执行 多次
比while更直观一些。
Var lis = document.getElementsByTagName(“li”);
For(var i =0 ; i< lis.lenght; i++)
{
}
问题是:在循环过程中,每次都要去取出对象lis的 length属性,然后参与比较。
取出属性这个过程是要消耗资源的。
改写为:
For(var i=0 , len = lis.length; i < len ;i++)
{
}
这里的代码,只需要取一次length属性即可。
2.4.数组
理解数组:
(1)var i 只能定义一个变量。Var arr = new Array() ( 或者是 var arr = [] ) 定义了一个数组,相当于一次性定义了多个变量。
(2)Var arr = []; arr[0] 是一个变量,arr[1]也是一个变量。
(3)数组中的元素之间是有序的。我们通过下标去依次访问它们。
(4)数组中很多很多的方法可以供我们来调用;
a)Sort()排序
b)Push ,unshift
c)Pop ,shift
d)Split
e)Splice
f)Reverse
g)forEach
h)…
如果你记不得了,请用下面的技巧:
在控制台中,输入[],回车。
(5)数组也是一个对象,是一个特殊的对象。
(6)js支持多维数组。数组的元素又是一个数组。
作业:
有一个数列:
s1 = 1,
S2 = 1
S3 = s1 + s2
…
Sn = Sn-1 + Sn-2
规则就是从第3项开始,每一项都是前两项的和。
求 Sn的第100项?
扩展一下:如果不允许使用数组,又应该怎么算?
2.5.函数
理解函数:
函数是一等公民
函数是js中最重要的东西
函数是一种数据类型
函数的作用是:
有利于代码的重用。(回忆在淘宝中用到的轮播图的函数)
在js中,函数还有另一个高科技的作用:创建对象。
在js中,函数还被用来划分作用域。
函数的三要素:
形参(需要的原材料)。可以不要形参。
函数体(具体做的事情)
返回值(最后的结果)。如果你不明确地写return语句,则函数还是会有一个返回值,值是undefined.
2.5.1.示例
3.js中的错误
3.1.错误分类
根据浏览器的处理不同,把错误分成两类:
1.代码错误。浏览器会主动报错的错误。
2.逻辑错误。浏览器不会报错的错误,但执行的结果与我们想的不一致。
3.1.1.代码错误
常见的有4种代码错误:
- Syntax Error:语法错误
最好的错误也是最坏的错误 - ReferenceError:引用错误
最常见的错误;代码执行后才发现 - RangeError:数值超出范围
定义数组时,长度为负值 - TypeError:类型错误
在需要函数的地方不给函数
3.1.1.1.语法错误
代码写错了,不符合js的要求。
Uncaught: 没有捕获的
Unexpected:没有期待的,不需要,多余的
Token: 符号。
不需要符号)
解决错误的方法:把代码改对。
3.1.1.2.ReferenceError:引用错误
一般是使用了没有定义的变量
解决方法:先定义变量,再使用。
3.1.1.3.RangeError:数值超出范围
Range:范围,区间。
定义数组时,长度为负值
Invalid :无效的
3.1.1.4.TypeError:类型错误
原因:类型不对。
3.1.1.5.相同的错误,在不同的浏览器中提示不一样
3.1.2.4种常见的代码错误的区别
语法错误一旦发生,则整个代码段都不会执行。
其它的3种错误一旦发生,则其后的代码都不会执行。
3.1.3.错误的建议
在此处收集后面学习过程中遇到的各种js错误:
序号 错误关键字 原因 解决方案
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
3.1.4.逻辑错误
浏览器能够正确地执行你的代码,但,执行的结果不是我们想要的。
我们通过设置一些输出,来监听变量的值。
3.1.4.1.技能点: 加断点,调试程序(掌握*****)
在js代码的任何地方加
Debugger;
3.1.4.2.四个操作
3.1.4.3.watch面板
3.1.4.4.添加删除断点
作业,对如下代码进行调试
var week=“星期一”;
if( week == “星期一”){
console.info(“黑色星期一”);
}
if( week == “星期二”){
console.info(“明天是周末”);
}
if( week == “星期三”){
console.info(“今天是小周末”);
}
if( week == “星期四”){
console.info(“再坚持一下”);
}
if( week == “星期五”){
console.info(“明天是大周末”);
}
else{
console.info(“放假k…”);
} 结果是:
分析这里是为什么?
3.2.错误 小结
形成正确的错误观。
(1)错误是必不可少的。
(2)有错误,要珍惜!
(3)要会用调试工具!
4.javascript解析与执行
4.1.代码块的定义
代码块是指由
格式如下:
4.1.1.代码块是相互独立的
一个代码块中的错误,不会影响其它的代码块.
4.1.2. 前面的代码块中定义的变量,在后面的代码块中可以访问
4.2.js预编译
js源代码 --> 预编译 -->执行
js代码在执行之前,会先预编译
预编译过程是不可见的,它不会产生额外的文件
在预编译期间,js做了如下事情:
- 按扫描代码块,发现语法错误:停止一切工作,报错!
- 没有语法错误,做提升操作:
(1)变量提升。把所有的var变量,提升到代码块的开始,并设定初值为undefined。
(2)函数提升。把所有的用声明式定义的函数,提升到代码块的开始
4.3.变量提升(掌握)
把所有的var变量,提升到代码块的开始,并设定初值为undefined
变量提升。
4.3.1.只提升加var的变量
定义变量有两种方式:
1.显示声明。加了var
2.遗漏声明。没有加var .
在预编译期间,只有加了var声明的变量,才会被提升。
下面的变量a没有加var,所以不提升。
4.3.2.if结构中的变量提升
只要有var就会提升,提升过程与if没有关系 。
if有关系的是代码执行的过程。
4.3.3.for循环中的变量提升
4.3.4.函数内部的变量提升
4.4.函数提升(掌握)
4.4.1.只提升声明式定义的函数
定义函数有两种方式:
(1)函数表达式
var f = function(){ }
(2)函数声明式
Function f(){}
下面的代码:
之所以能够正确运行,就是因为有函数的提升。
下面看一个反面教材:函数表达式的方式去定义函数,看它是否有提升。
4.4.2.函数与变量同时提升
函数是一等公民。
4.5.提升不能跨script
4.6.通过调试器去观察变量和函数提升的过程
4.7.代码执行的过程
4.8.事件会改变代码的执行的顺序
5.数据的存储与赋值
5.1.数据类型
js中变量是没有类型的,只有数据才有类型。
由于数据的类型不同,在存储和赋值上就有不同。
5.2.检测题(掌握)
5.3.基本数据类型的存储(掌握)
只需要用到栈区
Var a = false
Var b = 1;
Var c = “a”
数据结构:栈,队列
5.4.引用数据类型的存储(掌握)
要同时用到栈和堆。
5.5.基本数据类型和引用数据类型的区别
5.6.引用数据的赋值和基本类型数据的赋值(掌握)
Var a = 1;
Var b = a
Var a =[1]
Var b = a;
js中的赋值都是把栈区中的数据复制一份,给左边的变量。这一点,无论是基本数据类型,还是引用数据类型都是一致的。
赋值操作对于基本数据类型和引用数据类型是一样的。
5.7.检测题讲解
5.7.1.1
5.7.2.2
5.7.3.3
5.7.4.4
5.8.理解 ==
判断两个变量是否相等,要检查它们在栈区中的值是否相等(不是看堆区中的真实数据是否相等)。
画图表示a,b在计算机中的存储情况:
结论:
用=来代替。不要用==,忘记了有==这回事。
6.变量
变量的作用是用来保存数据。
定义变量有两种方式:
遗漏声明
不加var
显示声明
加var
6.1.var与不加var的区别
1)区别一:在函数内部,加var是一个局部变量,不加var就是一个全局变量。
上面的代码中:var a 是一个局部变量,出了function就不能再使用。
下面的代码中,a = 1,没有加var,就变成一个全局变量。
出了function之外,还可能使用。
2)区别二:加var 是不可删除的,不加var是可以删除的
3)区别三:加var会提升,不加var不被提升
补充一下:
在es5中,有一种写代码的模式,叫严格模式,这种模式下,写代码的要求被提高了,更严格了。这种模式默认是关闭的,我们可以手动启用这种模式。
在代码最前面加”use strict”;
在严格模式下,遗漏声明是不允许的!!
在es6中,严格模式默认是打开的。换句话,在es6是不允许遗漏声明的。
6.2.变量的作用域
在es5中,变量作用域有两种:
1)全局作用域 – global – 全局变量
2)局部作用域 – local – 局部变量
在很多其它的语言中,都有第三种作用域,块级作用域,但在es5中,没有块级作用域。
好消息:
在es6中,已经有了块级作用域。
3)块级作用域 – block
6.2.1.全局作用域 – global
判断一个变量是否是全局变量:看变量的定义的外部,有没有一个function包含它。
在其后面的代码中,都可以正常使用它的值。
6.2.2.局部作用域 – local
一个变量的定义被function包含,此称它为一个局部变量,拥有局部作用域。
6.2.3.块级作用域 – block
通过 { }来划分作用域。
上面的代码中:
现在很明显, var a它不是块级作用域,而是全局使用域。所以, 我们说,在es5中没有块级作用域这个说法。只有一个东西可以用来划分作用域:function。
但在es6,为了跟其它的语言保持一致,它引入块级作用域的概念。 是通过 let关键字来实现的。
6.3.局部变量与全局变量同名的问题
在函数的内部,优先使用局部变量。
在函数的外部,只能使用全局。因为局部变量在函数调用结束后,被回收了。
分析
F()还没有被调用
准备去执行f:
由于在f中有局部变量(就是f自己的变量),所以f函数会向内存去申请一个空间来保存它自己的变量。
理解为,这里多出一个f栈。其中有一个变量a,由于变量提升的关系,它的初始值就是undefined.
所以 ,第3行代码会输出 undefined. 这里有一个问题:在确定一个变量的值的时候,有一个就近原则:先在局部变量中找,如果找不到,再到全局变量中找。
第7行代码,执行结束后,相当函数f调用完成,则它申请的空间(理解为函数栈,就回收了),则局部变量a就消失了。
所以第8行中,访问的是全局变量a.
6.4.练习(掌握)
6.4.1.1
6.4.2.2
6.4.3.3
6.4.4.4
6.4.5.5
6.4.6.6
6.4.7.7
6.4.8.8
6.5.全局变量的坏处 – 不要使用全局变量(理解)
变量污染。
6.5.1.污染系统函数
如果我也定义一个名为alert的变量,就会污染这个全局的函数。
现在的 alert就不能使用了。
6.5.2.相互污染
在我们写js的过程 , 我一般是写好的额外的js文件,然后引入到当前的html文件中。
如果是多人协作,有另一个程序员加入:
解决方法:类似于jquery代码的写法:
更具体的内容,见IIFE。
由于全局变量有如上的坏处,所以,我们一方法要尽量避免使用全局变量,另一方面要小心有些代码,会悄悄地产生全局变量。
6.6.悄悄地产生全局变量
1.函数内部少写var
2.定义多个变量时,使用连=号
6.7.变量的最佳实践
6.7.1.把所有在代码用变量,全部写在代码的开始处。
先定义,再使用。
6.7.2.多个变量,使用单var定义。
6.7.3.尽量不使用全局变量
如何从全局转成局部变量? 方法只有一个,加function包含起来。
7.函数的参数
7.1.创建一个函数有三种方式
7.1.1.函数声明式 推荐
Function f() {}
7.1.2.函数表达式 推荐
Var f = function(){
}
表达式和声明式的区别在于:
(1)它们都会被提升(一个有function , 一个有var关键字)
(2)函数声明式定义的函数,会整体提升。
(3)函数表达式定义的函数,只提升函数的名字,函数体不会提升。
7.1.3.使用Function构造器 不推荐
格式:
Var 函数名 = New Function(“形参1”,“形参2”,…“形参n”, “函数体”) ;
最后一个参数是函数体。
这种写法很少用。
7.2.参数
7.2.1.分两种
1)形参:定义函数时,使用的参数;
2)实参:调用函数时,传递的参数;
参数传递:把实参传递给形参。
7.2.2.传参的本质
按值传递,把实参的值赋值给形参。
值是栈区中的值,不是堆区中的值。
形参 = 实参; 就是一个赋值语句:从右边到左边。
理解形参。
形参就是函数内部定义的局部变量。
7.2.3.传递基本数据类型的数据的例子(掌握)
7.2.3.1.1
7.2.3.2.2
下面,开始执行f(a);
由于f函数中, 有一个形参,相当于它有一个局部变量,所以它要去申请一个空间来保存这个局部变量。
下面是参数的传递
从实参到形参。具体是:把实参的在栈区中的值,复制一份,填入形参的对应的栈区。
在函数的内部,对a进行赋值,由于a是局部变量,所以效果如下:
接下来,函数调用完成,系统要回收函数栈
通过调试:
7.2.3.3.3
由于在函数f 中,有局部变量b,所以 ,它会向系统申请一个空间,来保存b的值。
接下来,开始进行参数的传递。 f(a);相当于要把实参a的值,赋值给形参b。
再执行a = 3;
这里的a就是全局变量.
b = 1;
这个b就是局部变量
再接下来,函数f执行完成,系统要回收空间:
7.2.3.4.4
由于f中有局部变量a,所以它要向系统申请一块空间来保存a.
下面开始,参数传递。
把实参a的值传递给形参a。
下面执行a = 3;
这里的a就是局部变量。
接下来是:b = 1 ;
由于局部变量没有b,全局变量中也没有b。所以此时的 b = 1,相当于是遗漏声明,就是悄悄地定义了一个全局变量。
再接下来,函数f调用结束,它申请的空间被回收。
此时,访问的a,b就是全局变量。
7.2.4.传递引用数据类型的数据的例子(掌握)
7.2.4.1.1
修改一下函数的参数:
对结果 没有影响 。
准备调用函数f。
由于f有一个形参a,相当于它有一个局部变量a,所以它要申请一个空间。
下面进行参数传递:
参数传递结束后:
进行a [100] = 3;
这里的a是局部变量。a[100]就是给堆区中的数据(是一个数组)加一个3.
A[100] = 3;结束后,如下。
接下来,整个f调用结束 ,回收空间:
7.2.4.2.2
准备执行f.
由于f中有一个局部变量a,所以要向内存申请一块空间:
接下来,进行参数的传递:
传递之后:
接下来:
a = [1,2,3]
A = [1,2,3] 是把一个引用数据类型的值保存在变量中:
(1)先在堆区划出空间来,保存真实的数据。
(2)把堆区的地址保存在变量a的栈区。
这一句,完成后:
接下来。f调用结束 ,空间回收。
7.2.4.3.3
准备执行f
参数传递
A[100] = 3;
接下来:
A = [1,2,3]
接下来, 空间回收:
作业:如下的结果,自己画图分析。
7.2.5.总结,参数传值
从实参到形参,传递的过程就是赋值的过程。把一个变量的栈区中的数据,传递给另一个变量的栈区。
形参相当于是函数的局部变量
7.3.一个现象—函数的实参是函数(理解)
由于函数也是一种类型,它和数组还有对象一样都是引用数据类型,函数也可以当作实参进行传递。
在前面的学习中,有两个地方,我们这样使用过:
7.3.1.1.setInterval中的回调函数
在调用setInterval时,需要给两个参数,第一个是函数,第2个是时间间隔。
由于是调用函数,所以这里的参数称为实参,所以:
换一种写法:
把函数当作一个实参进行传递。
7.3.2.2.array中的sort函数之自定义的排序
Sort()默认是字典排序。
如果要把元素当作数值,进行升序排序,则要给sort 一个参数。
说明 :Sort()它可以接收一个自定义的排序规则函数,也可以不接收(使用字典顺序)。如你要给它一个排序规则函数,则,这个函数有一定的要求:
(1)这个函数必须要有两个形参,分别表示数组中的两个元素。
(2)这个函数必须要有返回值,并且:
a)如果返回值为正,则表示要交换两个元素在数组中的位置
b)如果返回不为正,则表示不需要交换。
问题:对数组进行排序,排序的依据是:数组元素谁与5的绝对值小,则数组元素就在前面。
var arr = [2,3,1,12,123,4];
[4,3,2,1,12,123]
7.3.3.例子:统计一个函数运行的时间
给一个函数f , 我现在想知道,f在当前的电脑上运行结束,需要花多长时间?
思路:
开始:记下当前的时间start;
//运行函数
结束:记下当前的时间end,则花的时间用end - start。
7.4.arguments对象(掌握)
Arguments :参数
7.4.1.它是在函数内部使用的一个对象
(1)它是一个对象 。
(2)它只能在函数的内部使用。
7.4.2.它的作用
在函数被调用时,它用来保存当前调用时,所有的实参信息(无论当前是否有对应的形参来接收实参的值)。
7.4.3.arguments长的像数组,但不是数组
我们可以像数组一样,通过下标去访问arguments对象的值。
还有length属性,进一步,还可以使用for循环访问元素。
但它不是真正的数组:
用Array.isArray()来判断:
另外,数组都可以sort(),下面我们来看一看arguments能不能sort()?
它没有sort()方法,它不是数组。
补充一下:像arguments这种,长的像数组(用length,可以用for循环),但不是数组这一类对象,我们称为 类数组对象。
arguments有一个伙伴,也是长的像,但本质不是数组。这个小伙伴就是:document.getElementsByTagName(“li”);的返回值。
但上面的lis也不是数组。
7.4.4.arguments的应用
由于arguments中包含的所有的实参的信息。所以它就可以用来实现 函数重载 的功能。
7.4.4.1.函数重载
js中没有函数重载,但在c语言中有。
函数重载就是说:允许你定义多个函数名相同,但形参列表不同的函数。然后在调用函数时,根据实参列表来选择执行某一个函数。
在c中,如下定义是定义了3个不同的函数,
Function getMax( a, b ) { }
Function getMax(a, b ,c){ }
Function getMax(a,b,c,d){ }
在调用时,根据参数的个数去选择不同的函数去执行:
getMax(1,2) ; // 调用第一个函数
getMax(1,2,10,20 ) ; // 调用第三个函数
如上描述的情况,就是说,这个语言支持函数重载。函数重载的好处在于:共用一个函数名。
但,js是不支持的。
如上的代码,等价于:
所以,在js 没有函数重载。但它有arguments.
7.4.4.2.示例:求最值
7.4.5.是否所有的形参都不需要了呢?
从理论上:是的。
实际中,我们不会这样做:虽然我们可以通过arguments对象去获取原形参的值,但,我们还是会保持必要的形参。
原因只有一个:方便别人读懂程序。
arguments不好读。不如r直观。
7.4.6.arguments的应用:不知道参数有几个的情况下。
$(“#div”).css(“color”) ;//获取id为div元素的颜色
$(“#div”).css(“color”,”red”) ;//设置id为div元素的颜色为red
如何在一个函数 css中实现两种不同的功能?如何去判断当前用户到底是要设置,还是要获取?
可以在css函数内部,根据 arguments.length来判断。
如果 arguments.length = 1 . 说明是获取
如果 arguments.length = 2 . 说明是设置
7.4.7.arguments和形参之间的关系 (了解)
下面的结论, 只在es5中有效。
7.4.7.1.当形参列表与arguments存在一一对应关系时。具备联动的特性。
由于a和arguments[0] 之间有一一对应关系,所以更改a时,arguments[0]也受影响。
对b,同理:
改了arugments[1],b也受影响。
注意:这里说的一一对应关系是由参数的传递(实参到形参)来决定的。
7.4.7.2.当形参列表与arguments不存在一一对应关系时。不具备联动的特性。
现在,补上一个实参,建立一一对应关系:
7.4.8.小结
它是一个类数组对象 ;
只能在函数内部使用;
作用是用来保存实参的值;
虽然它可以完全代替形参,但我们还是会保留必要的形参;
注意与形参列表的对应关系
8.函数的返回值
8.1.所有的函数,只要被调用了,就一定会有一个返回值
默认情况下,这个返回值是undefined.
8.2.函数的返回值可以是一个函数(理解)
上面的函数f执行结束后,返回值是一个函数 f1,把这个结果保存到rs中,所以rs就是一个函数,rs就可以正常调用.
8.2.1.示例1
对于一个数组:arr = [19,18,16,30,34,60,25];
请对数组进行排序:
(1)按元素与20之间的距离进行排序。
(2)按元素与35之间的距离进行排序。
观察一下f20和f35之间的区别在于?它们的区别在于排序的基准不一样,一个是20,一个是35,我们可以进一步地,提炼出一个函数来解决这个问题。
getSortFun就是一个用来产生排序函数的函数,它的返回值就是函数。
8.2.2.示例2
(1)你相当于是写的四个全局变量:变量名分别为add,sub,div,mul。
定义全局变量,会增加变量污染的可能性。不建议使用全局变量。
(2)这个四个函数,暴露在用户的面前,没有隐私!
你想想,我们是如何去调用这些函数的?
对上面的代码进行改进:
好处在于:
(1)add,sub,div,mul 不是全局变量,减少被污染的可能性(原来有四个全局变量,现在只有一个了getComputer)。
(2)add,sub,div,mul被封装起来了,外界无法直接访问。
从形式上来看:现在就是一个函数 getComputer,它的返回值也是函数。
8.3.调用一个函数返回多个值
给一个全数字的数组,写一个函数求数组的
(1)平均值
(2)最大值
(3)最小值
如果要求把这三个功能,写到一个函数中去(不要写三个小函数)。
8.3.1.不能写多个return
8.3.2.可以返回一个数组
如何取出数组中的元素?
上面的这种通过下标去取值的方式不好,它不方便记忆:下标0,1,2它没有语义!!!
换一个媒介来保存多个数据 — 对象!
8.3.3.可以返回一个对象
9.执行上下文(了解)
9.1.一道很难的面试题
9.2.什么是执行上下文
理解执行上下文
(1)执行上下文是一个对象,它保存了执行代码时所需要的各类数据(最常见的:变量的来源)。
(2) 代码的运行会产生执行上下文。如果代码不运行,则没有执行上下文。
(3) 代码分成两种:
全局代码
函数代码
(3)两种代码在执行的过程中会产生两个执行上下文。
a)全局代码产生全局执行上下文
b)函数代码产生函数执行上下文
(4)函数嵌套调用(一个函数中调用另一个函数)形成执行上下文栈
a)栈是一种线性的数据结构。(洗盘子,放盘子)
b)入栈操作:发生在栈顶。
c)出栈操作:发生在栈顶。
( 5) 执行上下文保存了执行代码时所需要的各类数据,每一个执行上下文由两部分的组成: 第一部分:自己的定义的资源;
第二部分:父级函数中定义资源;
9.3.通过代码调试的方式 看到 执行上下文
观察call stack面板
9.3.1.全局执行上下文有几个?
只有一个。
必须它是最先入栈的。(它是你洗到第一个盘子)
9.3.2.如何查看执行上下文中保存的供代码运行的资源?
在scope面板中可看到。
操作是:点击call stack中的某个 执行上下文,对应地,在scope面板中就可以看到。
9.3.3.如何查看函数执行上下文?
让代码运行到函数的调用处,你观察call stack面板的变化。
调用函数时,函数的执行上下文会入栈。
9.3.4.查看函数执行上下文中的资源?
9.3.5.函数的嵌套调用会产生执行上下文栈
9.3.6.函数调用结束它对应的执行上下文会从执行上下栈出栈。
出栈和入栈一样,都是发生在栈顶:后入栈会先出栈。
函数调用结束:出栈
F, f1,全局执行上下文,会依次出栈,最后call stack为空,表示所有的代码都运行结束了。
9.4.应用1
解释这个问题。
9.5.应用2
从当的执行上下文出发,先找自己的,再找自己的父级的…直到全局执行上下文。
10.函数的嵌套定义
10.1.格式
这是js特有的特点:函数内部再定义函数
Function f(){
Function f1() {}
}
10.2.作用
在es5中,只有function才能划分作用域。函数内部套函数有两个好处:
(1)切分作用域。
(2)封装代码。外部的代码无法访问函数内部定义的函数
10.3.作用域链
在js中有两条链:
作用域链:帮助我们去确定,在函数中如何去确定变量的值。
原型链:帮助我们去确定,在对象是中如何去确定属性的值。
只有function才能划分作用域,而function的嵌套定义,就会产生一个作用域链。
10.4.在函数的内部定义一个函数,并返回这个函数
接下来,执行r = f();
由于f()的内部有一个局部变量f1,它会申请一个空间去放f1。
由于f1的真正的内容是一个函数(引用数据类型),所以要在堆区划分空间,保存代码 。
接下来,执行 return f1;
结果 如下:
注意一点:
在f内部创建的一个函数体,这个代码在堆区是不是被回收的(如果它被回收了,则r就指向不明)。只有在栈区的f1被回收了。 这时,由于f1已经通过return 把自己的值(地址)成功传递 给了r,所以它的“身体死,灵魂还在”!!
10.4.1.简写1
10.4.2.简写2
11.立即执行的函数表达式IIFE
Immediately-Invoked Function Expression
11.1.需求
有一段代码,这段代码中定义了一些变量及函数。 我们希望去执行这段代码:
1)只需要执行一次,不需去重复调用。
2)不会对这段代码前后的其它代码产生任何影响,更不要污染全局变量,也不能受其它代码的变量的污染。
11.2.怎么办? 加一个函数
上面的这个方案解决前面的问题,但它带来新的问题:
(1)这里用到一个函数名,abc。一个函数名就是一个变量,abc也有可能被污染。
(2)这个函数名是多余的,我们这一段代码并不需要重复调用。
上面的问题,用IIFE解决。
11.3.立即执行的函数表达式
理解:
立即执行。不要等,不要有中间过程,不要有中间变量,立即就做。
函数表达式。定义函数的三种方式之一。
11.3.1.立即执行
以对数组排序为例:
第一步:先定义一个数组,给数组值,这里会定义一个变量。
第二步:调用数组sort方法。
var a = [3,50,1,2];
a.sort();
上面的代码就不是“立即执行”
改一下:拿掉中间变量
[3,50,1,2].sort();
上面的代码执行是ok的。
以定义函数再调用函数为例:
这就不是立即执行。有中间变量f。
去掉中间变量f。格式如上。
坏消息:不能执行
11.3.2.解释一下上面的错误是为什么?
原因是:Functoin(){} 它不是一个函数表达式,它是一个语句。所以它的后面不能加()。
要解决上面的问题,我们要把function(){} 变成一个函数表达式。
11.3.3.解决方法
11.3.3.1.function(){} 前后加()
( function(){} )() [jquery是这么做的]
11.3.3.2.整体加()
( function(){} () )
11.3.3.3.在前面加单目运算符,如:
+function(){}() [bootstrap.js就是这么做的]
-function(){}()
!function(){}()
11.3.3.4.标准的函数表达式写法:
这里的a中的值就是function()的返回值,由于没有明确写return,所以返回值就是undefined
11.4.最终的方案 iife
这里的函数一定义就执行,没有名字,就不会被别人覆盖。
11.5.带参数的iife
11.5.1.从格式上来讲
11.5.2.从应用的角度来讲,为什么要给它加一个参数?
由于 iife有一个独立的function包裹,会保持它其中的代码不会去污染别人的代码,也不会让别人的代码去污染它自己。
(function(){
} ) ()
但有一个问题。如果你在这代码中设计一些工具函数,这些工具函数可以达到特殊的目标,你是从保持代码的角度把它们写在iife中,但同时,也会导致另一个问题:你的工具将无法被别人使用!
这里不能在iife外部去使用工具函数。为了解决这个问题,我们可以给iife传递一个参数,把这个工具函数作为对象的一个属性加在这个参数上。
看jquery.
11.6.iife的一个小细节 在前面加一个 ;
一个小错误
错误原因:
解决方法:
加;隔开。加多个; 效果是一样的。
我们的建议是在你写的每一个iife的前面加 ;
在实际的开发过程, 一个iife我们会单独地放在一个js文件中,然后一并在html中引入。
你不能保证别人的iife都会在结束的时加; 但你可以在你自己开始的时候加;.
11.7.小结
iife
形成一个封闭的作用域,防止变量污染。
加一个全局对象window,把在封闭作用域写的工具暴露出去。
12.闭包 closure(掌握)
12.1.定义
在A函数中定义一个B函数(函数的嵌套定义),在B函数中使用了A函数中的变量,就会产生闭包。具体来说,就是B就是一个闭包。
注意:
1.嵌套定义
2.引用变量
12.2.用debuuger去观察闭包
如果没有变量引用,则也不会看到闭包。
12.3.闭包的作用是什么?
延长变量生命周期。
上面的代码中,闭包B会延长变量i的生命周期,它有能力可以让i活的更久一些。这里的i 是A的局部变量,它正常的生命周期是函数A的调用过程。
在调用A的过程中,由于i是它的局部变量,所以A会向内存申请一个空间来放i,但当A调用结束后,这个空间会回收,即i就死了。
闭包B的能力就是可以让函数A()执行完成之后,i仍然活着!!!!
实现这个能力还需要有一个帮手:return !
见如下代码:
12.4.示例:延长i的生命周期
可以看到,每次调用r(),都可以对i的值进行++,再输出来。这就说明,A()调用之后,i 并没有死掉了,它还活着。
原因如下:
还没有开始调用A()
接下来,开始调用A()
A函数内部,有两个局部变量:
i,
B 它还是引用类型的。要用到堆区
所以A会去申请空间
接下来,执行return B
结果如下:
由于在B的函数体中用到变量i ,所以i的空间不能被回收。即i的生命周期被延长了。
再次观察调试面板:
12.5.理解延长变量的生命周期
由于在函数B的内部它用到它外部的变量i,在这个函数B没有死掉之前,i是不会消失的。
12.6.闭包-经典应用
12.6.1.经典应用1:实现节流函数
在mui的buffer()函数中,我们提到了节流函数。节流函数就是一个函数在指定的时间间隔内,只能被调用一次。
例如,f函数,在1000ms内,只能调用一次。换句话,此时你调用f,则下一次再调用要等1000ms。本质上就降低函数执行的频率。
要求:1000ms内只能执行一次f.
改进如下:
上面的代码起到了降频的效果。
分析:
Var f = A():执行完成,
下面执行
Div.addEventListenter(“mousemove” ,f)
这一句执行之后,则如果鼠标在div上移动就会去调用f,
分析一下调用f的过程。
下面的代码就是f的函数体:
比较当前的时间与第一次保存的now的值之间是否>500( 表示时间过了500ms ).如果成立,则执行核心语句: Console.info(“鼠标移动中…”)
同时更新最近一次执行f的时间。Now = Date.now(); 由于now没有回收,所以它还可以正常访问的。
如果不成立(说明时间没有过500ms)
12.6.1.1.提炼一个节流函数
12.6.1.2.用节流函数改一下鼠标移动的问题
12.6.2.经典应用2:让一个函数只能被调用指定次数
12.6.3.经典应用3
上面点击每一个li都会输出3
12.6.3.1.解决办法 1:使用自定义属性
12.6.3.2.解决办法 2:使用闭包
(1)iife。在function的前面有=号。
lis[i].onclick = function(index){}(i)
(2)闭包结构。
a)function的嵌套
b)在子函数中,用到了index,而index是父函数的形参,就相当于是父函数的局部变量。所以 ,也就是在子函数中使用了父函数中定义的变量。
c)Return 子函数。
12.7.闭包与内存泄漏
一般来说,在函数内部定义的变量,会随着函数的调用结束而被系统回收,如下:
Function f(){
Var i ;
}
F();
在F()执行完成后,i就不能再访问到了 。
由于闭包结构的存在,如下:
Function f(){
Var i ;
Return function(){
Console.info( i):
}
}
Var r = f();
R();
上面的代码中,由于r()在执行时,还需要用到变量i,这个变量i并不会被系统回收。
这种情况,我们不需要手动去回收变量(如在 c中,内存空间是可以手动回收的),js中内部有垃圾回收机制去做这件事,我们不需要管。
12.8.闭包小结
还有很多其它的应用都是以闭包为基础的,如设计模式中的单例模式,面向对象的中,创建对象时,不使用new。
闭包比较难,大家要正确地使用。
13.递归(了解)
计算机术语。是指 函数自己直接或者是间接地调用自己。
13.1.格式
从格式上:
function f(){
//其它代码
f();
}
13.2.死循环
递归一定要注意:在什么时候结束对自己的调用。如果缺少了结束调用,则一定会有一个死循环。
13.3.求1-10的和
13.3.1.方案1 :直接使用循环
13.3.2.方案2 :用递归
分析问题:
s10 = 1+ 2 + 3… + 10;
假设:老板让你求s10。
想:
我不会求s10,我安排一个人( 这个人叫p9),让他去求s9 = 1+2…+ 9。我自己求s9+10.
p9:我不会,我安排一个人(这个人叫p8),让他去求s8 = 1+2…+ 8。我自己求s8+9.
P8:我不会,我安排一个人(这个人叫p7),让他去求s7 = 1+2…+ 7。我自己求s7+8.
…
P3:我不会,我安排一个人(这个人叫p2),让他去求s2 = 1+2。我自己求s2+3.
p2:我会啊,1+2 = 3,所以,他直接告诉p3,我的任务完成了:返回3。
递:递近。把一个大问题,分解成性质相同的小问题。
(1)性质是相同的。例如,如上,都是求1-n的和。
(2)问题的规模是不断变小。小到最后,可以直接给出答案。
归:回归。把小问题的答案,一层一层地向上汇总,得到大问题的答案。
13.3.2.1.代码
调试代码,观察递归的过程。
13.4.求一个多维数组中元素的个数
Var arr= [1,2,[“a”,b”,”c”]]
上面的arr 是一个二维数组,它的第三个元素arr[2]又是一个数组。我们说,这个数组中一共有5个元素:1,2,”a”,”b”,”c”.
现在的要求是,你要写一个函数,用来求一个多维数组中元素的个数。
function getamount(arr){
}
Getamount(arr);//5;
13.5.多维数组的克隆
要求,你写一个函数,现在有一个多维数组,你用这函数把这个数组一模一样克隆一份。
13.5.1.对于一维数组,可以直接使用循环
上面的代码,如果不适于多维数组。
受影响的原因在于:
arr中的第4个元素又是一个数组,而数组有别于前三个元素(4,5,6),它是引用类型的,你直接push(arr[3])相当于是让arr和arr2共用一个地址,而这个地址才是指向数据7,8的。
解决方法 :对于多维数组,还是要根据情况,使用递归。
13.5.2.代码
13.6.汉诺塔
目标:
把n个盘子,从a —> c,借助 b。
分析三步走:
(1)把n-1个盘子,从a–>b,借助c。
(2)把一个盘子, 从a–>c
(3)把n-1个盘子,从b–>c,借助a。
约定:
function hnt(a,c,b,n){}
它的功能就是把n个盘子,从a-- > c,借助b
代码如下:
Function hnt(a,c,b,n){
//(1)把n-1个盘子,从a–>b,借助c。
hnt(a,b,c,n-1)
//(2)从把一个盘子, 从a–>c
Console.info( a +”—>” +c);
//(3)把n-1个盘子,从b–>c,借助a。
hnt(b,c,a,n-1)
}
13.7.小结
递归一般用在:
求文件夹的大小(文件夹中还有文件夹),求路径(保存在数据库),深拷贝。
适用用于:
问题性质相同,但可以分解
分解到最后,是可以直接求解
14.Try catch finally
异常处理机制。
格式:
try{
//这里的代码可能有错误
}
Catch(e){
//如果有错误,就到这里来进行错误处理。这里的e是一个错误对象。它包含了出错的信息。与事件对象一样理解。
}
Finally{
//无论对错,这里的代码一定会执行。
}
finally是可以不要的。
好处在于,当try中的代码发生错误时,不会出现在浏览器出现红色的错误!而是根据情况,进行适应错误处理:
(1)更友好的用户提示。
(2)错误收集
14.1.基本使用
14.2.主动抛出错误
在某个情况下,我们认为是错误,就主动抛出。
throw new Error()
代码就会进入 catch分支中去。
补充:
柯里化
函数式编程