030-js高级特性

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种代码错误:

  1. Syntax Error:语法错误
    最好的错误也是最坏的错误
  2. ReferenceError:引用错误
    最常见的错误;代码执行后才发现
  3. RangeError:数值超出范围
    定义数组时,长度为负值
  4. 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. 按扫描代码块,发现语法错误:停止一切工作,报错!
  2. 没有语法错误,做提升操作:
    (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分支中去。

补充:
柯里化
函数式编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值