javascrip

 

# javascript基础

## 一、javascript的介绍

### (一)javascript的诞生

>1994 年,网景公司(NetScape)发布了Navigator浏览器0.9版, 这是历史上第一个比较成熟的浏览器,引起了广泛关注。但是,这个版本的浏览器只能用来浏览,不具备与访问者互动的能力。。。。网景公司急需要一门网页脚本语言,使得浏览器可以与网页进行互动。

1995年4月,网景公司录用了34岁的系统程序员Brendan Eich, 他只用10天时间就把Javascript设计出来。布兰登·艾奇


### (二)javascript的特点

+ javascript是一个轻量级的语言
+ javascript是可以插入HTML页面的编程代码
+ javascript支持目前所有的浏览器
+ 解释执行不需要编译
+ 基于对象,内置大量的现成对象,编写少量的程序就可以完成目标

### (三)javascript的组成

+ ECMAScript javascript的语法标准
+ DOM(Document Object Model)文档对象模型: javascript 操作网页元素的API
+ BOM(Browser Object Model) javascript 操作浏览器部分功能的API

### (四)javascript 和 Html css 的关系

+ Html 用来编写网页的结构
+ css 美化网页添加样式
+ javacript 实现网页和客户之间的沟通,让网页有活力

## 二、javascript的书写位置

### 1、在html标签中使用 **行内式**

```
    
```

### 2、在HTML内部使用

``标签,可以放在任意的地方 **页内式**

   + head标签中使用

    ```
        
    ```

   + body标签中使用

### 3、外部调用

javascript在script标签的src属性中设置 ``在外部的脚本中不能包括script标签。 **外链式**

### 4、**js代码书写需要注意的问题:**

```
1. 在一对script标签中,出现错误,那么这对script标签中的后面的代码则不会再往下执行;
2. 如果有一对的script标签中出现错误,不会影响其他script标签对的执行;
3. script中可以写什么内容:type="text/javascript"是标准的写法,也可以写language="javascript",但是在目前的html中,type和language则可以省略,因为html是遵循h5的标准。
4. 有时兼容性的问题,type和language会同时出现;
5. script可以在页面中出现多对;
6. 如果script是引入外部的js文件,则标签内不能再写js代码,要写则需要重新写一行script包裹;
```

## 三、javascript显示数据的方式

### 1、使用 window.alert() 弹出警告框

```



    
    text


    

我的第一个页面

我的第一个段落

``` ### 2、使用document.write() 方法将内容写到html文档中 ``` text

第一个页面

第一个段落

- ``` ``` text ``` ### 3、使用innerHTML写入到HTML元素。 ``` text

我的第一个页面

我的第一个段落

``` ### 4、使用console.log()写入到浏览器的控制台。 F12 启用调试模式, 在调试窗口中点击 "Console" 菜单。`console.warn("警告输出")`,`console.error('这是一个错误')` ``` test ``` ### 5、用户输入 prompt()语句 prompt()语句就是专门用来弹出能够让用户输入的对话框。用一个变量来接收值,且不论用户输入的是什么都是一个字符串。 ``` test ``` ### 6、confirm 在网页中弹出提示框,显示信息,一般和if 判断来配合使用,相比alert多了一个取消的按钮。 ``` ``` ### 7、javascript显示方式的说明和对比 #### (1) console.log()和alert相比 ``` console.log()的用处主要是方便你调式javascript用的, 你可以看到你在页面中输出的内容。 相比alert他的优点是:他能看到结构化的东西,如果是alert,弹出一个对象就是[object object],但是console能看到对象的内容。console不会打断你页面的操作,如果用alert弹出来内容,那么页面就死了,但是console输出内容后你页面还可以正常操作。 ``` #### (2) document.write 和 innerHTML ``` 1、document.write是直接写入到页面的内容流,如果在写之前没有调用document.open, 浏览器会自动调用open。每次写完关闭之后重新调用该函数,会导致页面被重写。 2、innerHTML则是DOM页面元素的一个属性,代表该元素的html内容。你可以精确到某一个具体的元素来进行更改。如果想修改document的内容,则需要修改document.documentElement.innerElement。 3、innerHTML很多情况下都优于document.write,其原因在于其允许更精确的控制要刷新页面的那一个部分。 ``` #### (3) alert和prompt的区别 ``` alert("我很帅"); 直接使用不需要变量 var a = prompt(“请输入一个数字”) 需要用一个变量来接收用户的输入,且这个值一定是一个字符串 ``` ## 四、javascript的语法 ### **js 基本的代码规范:** + javascript对换行、缩进、空格不敏感。 + 每条语句的末尾都要加上分号,除了if、for、function语句结尾处不加分号,如果不加分号,压缩之后将不能运行。 + 所有的符号都是英文的。 + js中的字符串可以使用单引号,也可以使用双引号,一般使用双引号; + 区分大小写 + 变量是弱类型的,定义变量时要用var运算符声明变量,可以将它初始化为任意值。因此,可以随时改变变量所存数据的类型(尽量避免这样做)。 + javascript的注释 + 单行 // 单行注释可以嵌套单行、多行注释 ``` // 单行注释 var name; ``` + 多行 /* 内容 */ 多行注释可以嵌套单行注释,但是 **不能嵌套多行注释** ``` /*多行注释*/ var name; ``` + 括号表示代码块,代码块表示一系列应该按顺序执行的语句,这些语句被封装在左括号({)和右括号(})之间。 ## 五、javascript的字面量 “直接量”即常量,就是一些不可改变的值,也称为“字面量”,看见什么就是什么。 字面量的种类 + 数字(number)字面量,可以是整数或者是小数,或者是科学计数(e)。 ``` 3.14 1001 123e5 #12300000 123e-5 #0.00123
``` + 字符串(String)字面量 可以使用单引号或双引号 ``` "John Doe" 'John Doe' ``` + 表达式字面量 用于计算 ``` 5 + 6 5 * 10 ``` + 数组(Array)字面量 定义一个数组 ``` [40, 100, 1, 5, 25, 10] ``` + 对象(Object)字面量 定义一个对象 ``` {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"} ``` + 函数(Function)字面量 定义一个函数 ``` function myFunction(a, b) { return a * b;} ``` ## 六、变量 变量表示一些可以变化的数据,当一个数据的值经常变化或是不确定时,就应该用变量来表示。 变量的作用:用来操作数据的(可以读取,可以存储) ### (一)声明变量 + 变量使用前必须用var运算符进行声明,且不需要声明变量的类型。 ``` var test = "hi"; var test1 = 1; ``` + 可以用一个var声明多个变量,变量之间用逗号分隔。 ``` var test1 = 1, test2 = "string"; ``` + 可以用同一个 var 语句声明的变量不必具有相同的类型。 ``` var test = "hi", age = 25; ``` + var声明的变量并不一定要初始化一个值。 ``` var test; // 这样也是有效的 ``` + 变量可以存放不同类型的值。这是弱类型变量的优势,如可以把变量初始化为字符串类型的值,之后把它设置为数字值。 ``` var test = "hi"; alert(test); test = 55; alert(test); ``` + 变量声明不是必须的。 ``` var sTest = "hello "; sTest2 = sTest + "world"; alert(sTest2); ``` 首先,sTest 被声明为字符串类型的值 "hello"。接下来的一行,用变量 sTest2 把 sTest 与字符串 "world" 连在一起。变量 sTest2并没有用var运算符定义,这里只是插入了它,就像已经声明过它一样。 ECMAScript的解释程序遇到未声明过的标识符时,用该变量名创建一个**全局变量**,并将其初始化为指定的值。这是该语言的便利之处,不过如果不能紧密跟踪变量,这样做也很危险。最好的习惯是像使用其他程序设计语言一样,总是声明所有变量。 ### (二)变量的命名规范 变量名需要遵守两条简单的规则: + 第一个字符必须是字母、下划线(_)或美元符号($),不能以数字开头。 + 余下的字符可以是下划线、美元符号或任何字母或数字字符; + 变量名一般都是小写; 下面的变量都是合法的: ``` var test; var $test; var $1; var _$te$t2; ``` + 保留字不能用作变量名 ``` abstract、boolean、byte、char、class、const、debugger、double、enum、export、extends、final、float、goto implements、import、int、interface、long、native、package、private、protected、public、short、static、super、synchronized、throws、transient、volatile ``` + 几种常见的命名方式: 1、Camel 标记法:首字母是小写的,接下来的字母都以大写字符开头。例如: ``` var myTestValue = 0, mySecondValue = "hi"; ``` 2、Pascal 标记法 首字母是大写的,接下来的字母都以大写字符开头。例如: ``` var MyTestValue = 0, MySecondValue = "hi"; ``` 3、匈牙利类型标记法: 在以Pascal标记法命名的变量前附加一个小写字母(或小写字母序列),说明该变量的类型。例如,i 表示整数,s 表示字符串,如下所示“ ``` var iMyTestValue = 0, sMySecondValue = "hi"; ``` ### (三)变量值交换 #### 1、第一种方法:利用临时变量进行交换 ``` ``` #### 2、第二种方法: 一般用于数字交换 ``` ``` #### 3、第三种方法: 位运算交换 ``` ``` ### (四)变量值的分类 在 ECMAScript 中,变量可以存在两种类型的值,即原始值和引用值。又可以称为基本数据类型(简单类型和值类型)和复杂类型, 原始数据类型有:number string null undefined boolean object 原始值(基本数据类型):number string null undefined boolean 引用值(复杂数据类型):object **原始值(基本数据类型)** 存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。 **引用值(复杂数据类型)** 存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。 为变量赋值时,ECMAScript 的解释程序必须判断该值是原始类型,还是引用类型。要实现这一点,解释程序则需尝试判断该值是否为 ECMAScript 的原始类型之一,即 Undefined、Null、Boolean、Number 和 String 型。由于这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 - 栈中。这样存储便于迅速查寻变量的值。 在许多语言中,字符串都被看作引用类型,而非原始类型,因为字符串的长度是可变的。ECMAScript 打破了这一传统。 如果一个值是引用类型的,那么它的存储空间将从堆中分配。由于引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。如下图所示: ![栈堆](media/6.png) #### 原始类型(基本数据类型) ECMAScript 有 5 种原始类型(primitive type),即 Undefined、Null、Boolean、Number 和 String。 #### typeof运算符查看变量值的类型 typeof 运算符有一个参数,即要检查的变量或值。例如: ``` var sTemp = "test string"; alert (typeof sTemp); //输出 "string" ``` --- ``` ``` 输出结果: ![输出结果](media/1.png) **对变量或值调用 typeof 运算符将返回下列值之一:** + undefined - 如果变量是 Undefined 类型的 + boolean - 如果变量是 Boolean 类型的 + number - 如果变量是 Number 类型的 + string - 如果变量是 String 类型的 + object - 如果变量是一种引用类型或 Null 类型的 + function-函数 **typeof 运算符对于 null 值会返回 "Object"。这实际上是 JavaScript 最初实现中的一个错误,然后被 ECMAScript 沿用了。现在,null 被认为是对象的占位符,从而解释了这一矛盾,但从技术上来说,它仍然是原始值。** #### 各个原始类型(基本数据类型)详细介绍 ##### 1、Undefined 类型 Undefined 类型只有一个值,即 undefined。当声明的变量未初始化时,该变量的默认值是 undefined。 ``` var oTemp; ``` **值 undefined 并不同于未定义的值。但是,typeof 运算符并不真正区分这两种值。** ``` console.log(typeof e); //e 被定义了,但是没有给值进行初始化。也是输出undefined console.log(typeof bb); //bb没有被定义过 也是输出undefined ``` 如果对bb使用除typeof之外的其他运算符的话,会引起错误,因为其他运算符只能用于已声明的变量上。 **当函数无明确返回值时,返回的也是值 "undefined"** ``` function testFunc() { } alert(testFunc() == undefined); //输出 "true" ``` **调用函数时,应该提供的参数没有提供,该参数等于 undefined** ```javascript function f(x) { return x; } f() // undefined ``` **对象没有赋值的属性** ```javascript // 对象没有赋值的属性 var o = new Object(); o.p // undefined ``` ##### 2、Null 类型 它只有一个专用值 null,即它的字面量。值 undefined 实际上是从值 null 派生来的,因此 ECMAScript 把它们定义为相等的。 ``` alert(null == undefined); //输出 "true" ``` 尽管这两个值相等,但它们的含义不同。undefined 是声明了变量但未对其初始化时赋予该变量的值,null则用于表示尚未存在的对象。如果函数或方法要返回的是对象,那么找不到该对象时,返回的通常是 null。想让一个变量的值是Null时,必须手动指定 `var a = Null;` ##### 3、Boolean 类型 它有两个值 true 和 false (即两个 Boolean 字面量)。即使 false 不等于 0,0 也可以在必要时被转换成 false,这样在 Boolean 语句中使用两者都是安全的 > 任何的非0数值都是true,包括正负无穷,只有0和NaN是false > 任何的非空字符串都是true,只有空字符串是false > 任何的对象都是true,除了Null和 undefined是false,空数组[],和空对象{}是true ##### 4、Number 类型 ECMA-262 中定义的最特殊的类型是 Number 类型。这种类型既可以表示 32 位的整数,还可以表示 64位的浮点数。直接输入的(而不是从另一个变量访问的)任何数字都被看做 Number 类型的字面量。 * javascript 可正常计算的范围:小数点前16位,后16位。 + 八进制数和十六进制数 js 可以表示不同进制的数字,得看浏览器的支持 整数也可以被表示为八进制(以8为底)或十六进制(以16为底)的字面量。八进制字面量的首数字必须是 0,其后的数字可以是任何八进制数字(0-7), ``` var iNum = 070; //070 等于十进制的 56 ``` 要创建十六进制的字面量,首位数字必须为 0,后面接字母 x,然后是任意的十六进制数字(0 到 9 和 A 到 F)。这些字母可以是大写的,也可以是小写的。 ``` var iNum = 0x1f; //0x1f 等于十进制的 31 var iNum = 0xAB; //0xAB 等于十进制的 171 ``` 尽管所有整数都可以表示为八进制或十六进制的字面量,但所有数学运算返回的都是十进制结果。 + 浮点数 要定义浮点值,必须包括小数点和小数点后的一位数字(例如,用 1.0 而不是 1)。这被看作浮点数字面量。 ``` var fNum = 5.0; ``` 对于浮点字面量的有趣之处在于,用它进行计算前,真正存储的是字符串 + 科学计数法 对于非常大或非常小的数,可以用科学计数法表示浮点数,可以把一个数表示为数字(包括十进制数字)加 e(或 E),后面加乘以 10 的倍数。 ``` var fNum = 5.618e7 ``` 该符号表示的是数 56180000。把科学计数法转化成计算式就可以得到该值:5.618 x 107。 + 特殊的 Number 值 前两个是 Number.MAX_VALUE 和 Number.MIN_VALUE,它们定义了 Number 值集合的外边界。所有ECMAScript数都必须在这两个值之间。不过计算生成的数值结果可以不落在这两个值之间。 最后一个特殊值是 NaN,表示非数(NotaNumber)。NaN是个奇怪的特殊值。一般说来,这 种情况发生在类型(String、Boolean 等)转换失败时。例如,要把单词 blue 转换成数值就会失败,因为没有与之等价的数值。与无穷大一样,NaN也不能用于算术计算, 是一个非法的数字,当对数值进行计算,没有结果返回,则返回NaN,NaN 的另一个奇特之处在于,它与自身不相等,这意味着NaN也不等于NaN。 * NaN 属性是代表非数字值的特殊值。 含义:表示非数字, 主要用在将字符串解析成数字出错的场合。 ```javascript 5 - 'x' // NaN ``` 一些数学函数的运算结果也会出现NAN ```javascript Math.acos(2) // NaN Math.log(-1) // NaN Math.sqrt(-1) // NaN ``` 0除0也会是NaN 1/0是Infinity 运算规则: NaN不等于任何值,包括它本身`NaN === NaN // false` Nan在布尔运算时被当作是false `Boolean(NaN) // false` NaN与任何数(包括它自己)的运算,得到的都是NaN ```javascript NaN + 32 // NaN NaN - 32 // NaN NaN * 32 // NaN NaN / 32 // NaN ``` ###### **小结:** (1)无论是小数还是整数都是数字类型 (2)不要用小数去验证小数 ``` var x=0.1; var y=0.2; var sum=x+y; console.log(sum==0.3); // false ``` (3)不要用NaN判断是不是Nan,而是用isNan(值或者是变量句) ``` 判断结果不是一个数字可以使用isNaN(变量名) var sum=num+10;//NaN console.log(sum); console.log(isNaN(sum));//不是数字为true,是数字结果为false ``` (4)想表示十进制就是正常的数字 (5)想表示八进制以0开头 (6)想表示十六进制以0x开头 (7)像010这样的字符串,有些浏览器会当成是8进制,有些则会当成是10进制,为了保证准确性,在进行转换时,parseInt()时,用第二个参数明确具体的进制数。 ##### 5、String 类型 String 类型的独特之处在于,它是唯一没有固定大小的原始类型。可以用字符串存储 0 或更多的 Unicode 字符, 字符串字面量是由双引号(")或单引号(')声明的。而Java则是用双引号声明字符串,用单引号声明字符。但是由于 ECMAScript 没有字符类型,所以可使用这两种表示法中的任何一种。 ``` var sColor1 = "red"; var sColor2 = 'red'; ``` **只要有一个是字符串,其他的是数字,那么结果也是拼接,不是相加** ``` console.log("我" + "爱" + "你"); //连字符,把三个独立的汉字,连接在一起了 console.log("我+爱+你"); //原样输出 console.log(1+2+3); //输出6 ``` **字符串 - 或 * 数值 = 数值 如果有一个是字符串,另一个不是字符串,使用 - 或 * 号,此时会发生计算** ``` test ``` 输出结果: ![输出结果](media/2.png) 如果把a改为字母 var a = 'e'; 则输出结果为: ![输出结果](media/3.png) 如果把-换成*乘号,var c = b * a; 则输出结果为: ![输出结果](media/4.png) #### 类型转换 ##### 1、 转换成字符串 + toString()方法 ``` ``` **需要注意的地方:** > null 和 undefined 没有toString()的方法,强行调用会报错 > toString 不会改变原变量的值,它只会将转化的结果返回 + String() 函数 有时候有些值没有toString()的方法,比如null和undefined > 如果Number 和 Boolean 用String函数进行字符串的转换,实际上是调用了toString的方法 > 对于null和undefined来说,没有toString的方法,会在内部产生一个新的字符串 ``` ``` + 任何数据和+连接在一起都会转为字符串,其内部的原理和String一样 ``` ``` ##### 2、转换成数字 parseInt() parseFloat() 和 Number()。 + 注意的地方 + parseInt()把值转换成整数,parseFloat()把值转换成浮点数。只有对String类型调用这些方法,它们才能正确运行;对其他类型都是先转化成字符串(string()方法)后再进行转换。他的返回值只有两种,一种是十进制的整数,一种是NaN。 + Number()函数中无论混合字符串中是否存在有效的整数,都会返回NaN,利用parseInt()和parseFloat()可以提取字符串中的有效整数。 + parseInt()和parseFloat()的区别是,前者可以提取有效的整数,后者可以提取有效的小数。 + 对非String使用parseInt()或者parseFloat(),会将其转换成String然后再操作 ``` var str11 = true; var res13 = parseInt(str11); // 这里相当于parseInt("true"); console.log(res13); // NaN var res14 = Number(str11); console.log(res14); // 1 ``` + Number()函数 1、字符串转数字 **如果是纯数字字符串,则直接将其转换为数字** ``` ``` **如果字符串中含有非数字的内容,则转换为NaN** ``` ``` **如果字符串是一个空串,或是一个全部是空格的字符串,则转换为0 ** ``` ``` 2、undefined 转数字 NaN ``` ``` 3、null转数字 0 ``` ``` 4、布尔转数字 true转成1 false转成0 ``` ``` + parseInt() 把值转换成整数 1. 带有自动净化的功能;只保留字符串最开头的数字,后面的中文自动消失 parseInt()方法首先查看位置0处的字符,判断它是否是个有效数字;如果不是,该方法将返回 NaN,不再继续执行其他操作。但如果该字符是有效数字,该方法将查看位置 1 处的字符,进行同样的测试。这一过程将持续到发现非有效数字的字符为止,此时 parseInt() 将把该字符之前的字符串转换成数字。 2. 自动带有截断小数的功能:取整,不四舍五入 字符串中包含的数字字面量会被正确转换为数字,比如 "0xA" 会被正确转换为数字 10。不过,字符串 "22.5" 将被转换成 22,因为对于整数来说,小数点是无效字符。 3. parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。 ``` test ``` 输出结果: ![输出结果](media/5.png) + parseFloat() 把值转换成浮点数 + 会解析第一个,遇到第二个或者非数字结束; + 如果第一位不是有效数字,什么也提取不到; + 不支持第二个参数,只能解析10进制数; + 如果解析内容里只有整数,解析成整数; ``` var fNum1 = parseFloat("12345red"); //返回 12345 var fNum2 = parseFloat("0xA"); //返回 NaN var fNum3 = parseFloat("11.2"); //返回 11.2 var fNum4 = parseFloat("11.22.33"); //返回 11.22 var fNum5 = parseFloat("0102"); //返回 102 var fNum1 = parseFloat("red"); //返回 NaN ``` ## 七、运算符 + **作用:** 运算符是告诉程序执行特定算术或逻辑操作的符号, 例如告诉程序, 某两个数相加, 相减等 + 分类 + 按照功能分:算术、位、关系、逻辑 + 按照操作个数分: + 单目运算 只有一个操作数:如i++ + 双目运算 有两个操作数:如a+b + 三目运算 也称为问号表达式 如:a > b ? 1:0 + 结合性 JavaScript中各种运算符的结合性分为两种: 左结合性(自左至右) 和 右结合性(自右至左) + 自左至右,即先左后右 例如表达式: x - y + z; 则y 应先与“-”号结合,执行 x-y 运算,然后再执行+z 的运算。 这种自左至右的结合 方向就称为“左结合性”。 + 自右至左,即先右后左 例如:如x = y = z = 10 由于“=”的 右结合性,应先执行z = 10; 再执行y = z 再执行x = y运算。 ### (一)加法运算符 + + 非Number类型的值,除字符串外,都会先转为Number类型的值后再进行计算 ``` var sun = true + 2; console.log(sun, typeof sun); // 3 "number" var sun1 = true + false; console.log(sun1, typeof sun1); // 1 "number" var sun3 = 3 + null; // null 转为0 console.log(sun3, typeof sun3); // 3 “number" ``` + 任何值和NaN进行运算时,结果都是NaN ``` var sun2 = NaN + 3; console.log(sun2, typeof sun2); // NaN "number" var sun4 = undefined + 3; console.log(sun4, typeof sun4); // NaN "number" ``` + 任何值和字符串进行加法运算,都会先转为字符串,然后和字符串进行拼接 ``` var sun5 = 3 + "3"; console.log(sun5, typeof sun5); // 33 string var sun6 = null + "3"; console.log(sun6, typeof sun6); // null3 string var sun7 = undefined + "3"; console.log(sun7, typeof sun7); // undefined3 string var sun8 = true + '3'; console.log(sun8, typeof sun8); // true3 string ``` ### (二)减法运算符 - + 所有的非Number类型(包括字符串)的数据类型,都会将非Number数据类型先转为Number后再进行计算 ``` var difference = 10 - 2; console.log(difference, typeof difference); // 8 "number" var difference1 = 10 - '2'; console.log(difference1, typeof difference1); // 8 "number" var difference2 = 10 - true; console.log(difference2, typeof difference2); // 9 "number" var difference3 = 10 - null; console.log(difference3, typeof difference3); // 10 "number" ``` + 任何数据类型和NaN进行减法运算时,结果都是NaN ``` var difference4 = 10 - undefined; console.log(difference4, typeof difference4); // NaN "number" var difference5 = 10 - NaN; console.log(difference5, typeof difference5); // NaN "number" ``` ### (三) 乘法运算 * 规律和减法一样 ### (四)除法运算符 / 规律和减法一样 ### (五)取余运算符 % > m % n 求余 相当于 m / n 取余数 + m 等于0 返回 0 ``` var remainder = 0 % '4'; console.log(remainder, typeof remainder); // 0 "number" ``` + n 等于0 返回 NaN ``` // 当n为0时 var remainder1 = '4' % 0; console.log(remainder1, typeof remainder1); // NaN "number" ``` + m > n 正常取余 ``` // 当m>n时 var remainder2 = 5 % 2; console.log(remainder2, typeof remainder2); // 1 "number" ``` + m < n 结果是m ``` // 当m '0'); // ture ``` + 如果两侧都是字符串,不会转换为数字进行比较,而是分别比较字符串的Unicode编码的大小 + 比较编码时是从左到右一位一位进行比较 + 如果两位相同,则会比较下一位,可以用来做英文的排序 + 比较中文没有意义 ``` console.log('a' < 'b'); // true console.log('abc' < 'abd'); // true console.log('你' > "我"); // false ``` + null、 undefined、 NaN比较 ``` console.log(null == 0); // false console.log(undefined == 0); // false // 永远不要判断两个NaN是否相等 console.log(NaN == NaN); // false /* * 可以通过isNaN()函数来判断一个值是否是NaN * 如果该值是NaN(不是一个数字)则返回true,否则返回false */ var num = NaN; console.log(isNaN(num)); // true console.log(isNaN(1)); // false console.log(isNaN(null)); // false console.log(isNaN(undefined)); // true console.log(isNaN('abc')); // true // undefined 衍生自 null, 所以返回true console.log(null == undefined); // true; console.log(null === undefined); // false; // == 判断值是否相等 // == 会进行数据类型转换 console.log("123" == 123); // true // === 判断值和类型时候同时相等 // === 不会进行数据类型转换 console.log("123" === 123); // false ``` + 注意的地方 比较两个字符串类型的数字,结果可能有偏差,所以需要提前转型; ``` console.log("1111123" < "124" ); // true console.log("1111123" < 124 ); // false ``` ### (十)逻辑运算符 #### 1、&& (与运算) + 运行结果 条件A && 条件B 先去判断条件A成不成立,如果A成立了才去判断B是否成立,如果A不成立则不会去判断B 两个条件为真才为真,有一个为假就为假 + 短路测试 条件 && 表达式 条件为真时,后面的表达式执行 ``` console.log(7 > 3 && console.log('执行了')) // 执行了 ``` + 注意的地方 + 对于非Boolean类型的数值,逻辑与会自动将其转换为Boolean类型来判断; + 如果条件A成立,不管条件B成立与不成立都会返回条件B; + 如果条件A不成立,则返回条件A本身; ``` // 如果条件A不成立,则返回a的本身 var result = null && 7 > 3; console.log(result); // null // 如果条件A成立,不管B成不成立,都将返回B var result1 = 123 && 'abc'; console.log(result1) // abc var result2 = 123 && null; console.log(result2); // null ``` #### 2、 || (或运算) + 运行结果 条件A || 条件B 有一个为真就为真,两个为假才为假 如果A成立了,则不会去判断条件B,只有在A不成立的时候才会去判断条件B + 短路测试 条件 && 表达式 条件为假时,后面的表达式执行 ``` console.log(7 < 3 && console.log('执行了')) // 执行了 ``` + 注意的地方 + 对于非Boolean类型的值,逻辑或会将其自动转为布尔类型的值来判断; + 如果条件A不成立,则不管条件B成不成立都会返回条件B本身的数值; + 如果条件A成立,则返回条件A本身的数值; #### 3、 !(非运算) + 运行结果 !条件A 对条件A进行取反 比如:条件A成立true,取反后为false + 注意的地方 + 对一个值进行两次取反,它不会发生变化; + 对非布尔值进行操作,则先将其转换为Boolean,然后再进行取反,所以将一个数值转换为Boolean,除了Boolean()函数外,还可以使用 !!数值, 实现的原理和Boolean(数值)是一样的; ### (十一)逗号运算符 逗号表达式就是把多个表达式连接起来,组成一个表达式。 + 结果 从左往右计算,一直到最后一个,整个表达式的值也是最后一个表达式的值。 ``` var a, b; b = (a=3, --a, a*5); console.log(b) // 10 ``` + 使用注意事项 + 程序中使用逗号表达式,通常是求表达式内各个表达式的值,并不一定要求,整个逗号表达式的值; + 并不是所有出现逗号的地方都组成逗号表达式,例如在变量说明中,函数参数表中的逗号只是分隔作用; ``` var a, b, c; //这里的逗号只是分隔符 function sendMessage(num, content) { //这里的逗号只是分隔符 console.log(num, content); } ``` ### (十二)三目运算符 + 格式: 条件表达式 ?语句1:语句2; + 求值规则 条件表达式为真时,执行语句1;为假时执行语句2. ``` // 如果条件表达式为真,则执行语句1,为假则执行语句2 true?alert("语句1"):alert("语句2"); // 语句1 false?alert('语句1'):alert('语句2') // 语句2 ``` 一个案例: ``` ``` + 注意的地方 + 条件运算符?:是一对运算符,不能分开单独使用 + 如果条件表达式的求值结果不是一个Boolean,则会转换为Boolean值后再运算 ### (十三)运算符的优先级 + 运算符的优先级共分为15级,1最高,15最低; + 计算规则 + 先计算优先级高的; + 如果优先级一样,则谁在左边先算谁 + 可以使用()来改变优先级 + 优先级图示 ![优先级](media/优先级.png) ## 八、流程控制 ### (一)if 语句 一个分支,要么执行,要么不执行 + 语法 ``` if (条件){ 代码块; } ``` + 执行过程 如果条件是true,则执行代码块,如果是false,不会执行代码块 ### (二)if else 语句 两个分支,只能执行一个分支,像这种情况也可以使用三元表达式 + 语法 ``` if(表达式){ 代码块1; }else{ 代码块2; } ``` + 执行过程 如果表达式的结果是true,执行代码块1,如果结果是false,则执行代码块2 ### (三)if else if else if .. else 语句 多个分支,最终也执行一个 + 语法 ``` if(表达式1){ 代码块1; }else if(表达式2){ 代码块2; ..... }else{ 代码块3; } else if 可以写多个,具体看需要 else 可以不用写,具体看需求 ``` + 执行过程 先判断表达式1的结果,如果是true,就执行代码块1,后面的不会执行,如果结果是false,则会判断表达式2的结果,如果是true,就执行代码块2,后面的不执行,如果是false,则会继续判断下一个表达式,依此类推,最终,如果else if的表达式结果都是false,则执行最后的else代码块的语句。 ### (四) switch case 语句 多个分支,最终也执行一个(最终执行几个由break决定) + 语法 ``` switch(表达式){ case 值1:代码1; break; case 值2:代码2; break; case 值3:代码3; break; case 值4:代码4; break; ..... default:代码5; break; } default 后的break可以省略 default 也可以省略 case 值和表达式的值进行比较的时候是严格模式,相当是===的比较; break,如果不写,会从case匹配到的值开始,一直执行代码块,直到遇到break跳出 ``` + 执行过程 获取表达式的值,和值1进行比较,如果相同,则执行代码1,遇到break跳出,后面的代码不执行;如果和值1不相同,则和值2进行比较,依此类推,如果表达式的值和case的值都不一样,就执行default代码5的,跳出。 break的使用 ``` ``` ## 九、循环 ### (一)while 循环 + 语法 ``` while(循环条件){ 循环体; 计数器++; } ``` + 执行过程 先判断循环条件是true还是false,如果条件是true,则执行循环体和计数器,再次判断循环条件,如果条件成立,就执行循环体,否则就退出循环。 ### (二)do while 循环 + 语法 ``` do { 循环体; } while(条件); ``` + 执行过程 先执行一次循环体,然后判断条件是true还是false,如果是false,则跳出循环,如果是true,则继续执行循环体,至到条件不满足,跳出循环体, + while 和 do while的区别 + while 是先判断,后循环,有可能一次循环都不执行; + do-while 是先循环,后判断,至少执行一次循环; ### (三)for 循环 + 语法 ``` for(表达式1;表达式2;表达式3){ 循环体; } ``` + 执行过程 先执行一次表达式1,然后判断表达式2的条件成不成立。 + 如果成立,则执行一次循环体,然后执行表达式3,再判断表达式2成不成立,依此类推,至到表达式2不成立。跳出循环; + 如果不成立,则跳出循环; ``` // 斐波那契数列 var num1=1, num2=1, sun=0; for (var i=3; i<=12; i++) { sun = num1 + num2; num1 = num2; num2 = sun; } console.log(sun); ``` ### (四) break 语句 如果在循环中,遇到了break,则立即跳出当前所在的循环(注意是当前的循环,如果还有外层循环,是不会跳出的) ``` ``` ``` // break 找100-200以内被7整除的第2个数 for (var i=100, j=0; i<=200; i++) { if (i%7==0){ if(j==1){ console.log(i); break; } j++; } } ``` ### (五)continue 跳出本次循环 在循环中遇到continue关键字,直接跳出本次循环,进入一下次循环。 ``` // 求100-200之间所有的奇数的和 continue for (var i=100, sun=0; i<=200; i++) { if (i%2==0) { continue; } sun += i; } console.log(sun); ``` ``` // 求整数100-200的累加值,要求跳过所有个位数为3的数 // 个数数为3,要取出来,就是10取余后,余数是3的 for (var sun=0, i=100; i<=200; i++) { if (i%10==3) { /* 说明个位数是3,不要 */ continue; } sun += i; } console.log(sun); ``` ## 十、数组 + 数组:变量只能存储一个值,所谓的数组就是将多个元素(通常是同一类型)按一定顺序排列放到一个集合中,那么这个集合我们就称为数组。 + 数组的作用:可以一次性存储多个数据; + 数组的所有方法来源于:`Array.prototype` ```javascript // 自己写数组的push方法 Array.prototype.push = function () { for (var i=0; i数组的长度(数组元素的个数); + 如果在Array(多个值),这个数组中就有数据了,数组的长度就是这些数据的个数; + 数组的索引和数组长度的关系:就是长度减1,就是最大的索引; ``` // for 循环遍历数组 var array = new Array(1, 2, 'mjc', undefined, true, null, new Object); for (var i=0; i=0; i--) { console.log(array[i]); } ``` ``` // 把数组中的每个元素用|拼接到一起产生一个字符串输出 var array = new Array(1, 2, 3, 4, 5, 6); for (var i=0, str=''; i一坨重复的代码 } 2、函数的调用:函数名(); 3、函数的参数:在函数定义的时候,函数名子后面的小括号里的变量就是参数,目的是函数在调用的时候,用户传进来的值操作;此时函数定义的时候后面的小括号里的变量叫参数;在函数调用的时候,按照提示的方式,给变量赋值-->就叫传值,把这个值就传到了变量(参数)中; 4、形参:函数在定义的时候,小括号里的变量叫形参; 5、实参:函数在调用的时候,小括号里传入的值叫实参,实参可以是变量也可以是值; 函数的返回值:在函数内部有return关键字,并且在关键字后面有内容,这个内容被返回了,当函数调用之后,需要这个返回值,那么就定义变量接收,即可; 6、命名函数:函数如果有名子,就是命名函数,是一种弱类型的声明方式,在预解析完,执行的时候,会被var声明的覆盖。 alert(a) 如果是在预解析的时候,函数名会被提升 var a = 1; function a() { alert(1) } alert(a) 会弹出1 ========================================================================== 7、匿名函数:函数如果没有名子,就是匿名函数; 8、函数的另一种定义方式:函数表达式,把一个匿名函数或者把一个函数给一个变量;强类型的声明,先声明,后调用 var f1 = function () { console.log("我是一个函数"); }; console.log(f1.name); // f1 var f1 = function abc () { // 注意这里的abc充当了一个表达式就不能充当一个函数体了 console.log("我是一个函数"); } abc(); // 报错 console.log(f1.name) // abc 如果是函数表达式,那么此时前面的变量中存储的就是一个函数,而这个变量就相当于是一个函数,就可以直接加小括号调用了。 9、函数的自调用,没有名字,调用--声明的同时,加小括号直接用, 一次性的,一般用于初始化数据 (function(){console.log("自调用");})(); 声明函数不能自调用,但是在声明函数前加上-/+/!/(括号)就变成了函数表达式了,能够自调用了,但是函数名就不起作用了; 10、回调函数:函数作为参数使用,如果一个函数作为参数,那么我们说这个参数(函数)可以叫做回调函数,只要是看到了一个函数作为参数使用了,那么就是回调函数; 11、块级作用域:一对大括号就可以看作是一块,在这块区域中定义的变量,只能在这个区域中使用,但是在js中在这个块级作用域中定义的变量,外面也能使用。说明,js中没有块级作用域; 12、全局变量:声明的变量,是使用var声明的,那么这个变量就是全局变量,全局变量可以在页面的任何位置使用,如果页面不关闭,那么就不会释放,就会占空间,消耗内存;一切声明的全局变量,都是window的属性。如 var a = 123; console.log(window.a) // 123 13、局部变量:在函数内部定义的变量,是局部变量,外面不能使用,目前除了函数内部定义的变量是局部变量,其他地方定义的变量是全局变量。 14、全局作用域:全局变量的使用范围; 15、局部作用域:局部变量的使用范围; 16、在一个函数中使用一个变量,先在该函数中搜索这个变量,找到了则使用,找不到则继续向外找这个变量,一直找到全局使用域,找不到则是undefined; 17、隐式全局变量(暗式全局变量):声明的变量没有var,就叫隐式全局变量;函数内的隐式全局变量,在函数外也可以访问,但是隐式全局变量不会预解析进行变量的提升。 18、js的执行过过程:1、语法分析,先过一遍看有没有语法错误;2、预编译;3、解释执行代码; 预编译也叫变量提升,不管函数在那定义的,都会把函数整体(整个函数,包括声明,但是不是函数表达式 )提升到逻辑的最前面,变量声明提升,只提升声明,不会赋值。 注意点: 1、函数要先定义,才能使用; 2、函数的名字和变量一样,要遵循命名法; 3、函数声明重名,后面的会把前面的覆盖,不管在那个位置;函数表达式 4、形参的个数和实参的个数可以不一到(这一点和python不一样,python里必须要求一致,否则会报错); 5、函数没有返回值(没有return)或者是没有明确的返回值(return后面没有内容)但是在调用的时候接收了,那么结果就是undefined(变量声明了,没有赋值也是undefined); 6、return就是结束函数,后面的代码不会被执行; 7、函数名不加括号,打印出来是函数块的代码(python打印出来的是函数的内存地址); 8、函数之间可以相互调用; 9、函数表达式后面,赋值结束后,必须要加分号; 10、函数是有数据类型的,是function类型的; 11、函数可以作为参数(回调函数),也可以作为返回值; 12、全局变量是不能被delete删除的,隐式全局变量是可以被删除的,也就是说使用var是不会被删除的,没有var是可以删除的; 13、预解析中,变量声名的提升,只会在当前的作用域中提升,提升到当前的作用域的最上面,函数中的变量只会提前到局部作用域的最前面,不会成为全局的; 14、预解析会分段(多对的script标签中函数重名,预解析的时候不会冲突) ``` ### arguments.callee 指向的是函数的引用,也就是函数本身的代码,在那个函数里就指代那个函数本身 ```javascript function test() { console.log(arguments.callee); // ƒ test() {console.log(arguments.callee);} } test() ============================ var num = (function (n) { if (n == 1) { return 1 } return n * arguments.callee(n-1) // return n * 函数(n -1) }(5)) console.log(num); // 120 ``` ### `function.caller` 函数自己的属性,在那个环境里调用,就是谁 ```javascript function f1() { f2() } function f2() { console.log(f2.caller); // 在f1里被调用了,那么就是f1本身,ƒ f1() {f2()} } f1() ``` ![作用域](media/作用域.jpg) ``` // 三个数中最大的值 function maxNum(x, y, z) { return x>y?(x>z?x:z):(y>z?y:z); } console.log(maxNum(3, 5, 9)); ``` ``` // 判断一个数是不是素数 function primeNumber(x) { for (var i=2; i2) { days ++; } return days; } console.log(getDays(2000, 3, 2)) ``` ``` /* arguments 的应用 计算N个数字的和 定义一个函数,如果不确定用户是否传入了参数,或者说不知道用户传了几个参数,没办法计算,但是如果在函数中知道了参数的个数,也知道了每个参数的值,可以 */ function f1() { var sun = 0; for (var i=0; i 布尔类型 ``` 4、字面量的方式创建对象; ``` // 3、字面量创建对象 // 注意里面的属性方法与值之间用冒号,相互之间用逗号隔开,最后一个不加 // 缺陷:一次性的对象,不对传值 var pers2 = { name:"小明", age:18, eat:function () { console.log(this.name + this.age + "爱吃辣条"); } }; pers2.eat(); ``` ### 访问对象属性方法的另一种写法 obj.prop与obj["prop"] 当执行obj.prop的时候,内部会隐式的转化成obj["prop"]的方式来执行,obj[]的这种形式,方括号里的必须是字符串 ```javascript // 访问对象属性的另一种方法["名子"] 注意双引号,是以字符串的形式 function Dog (name, age) { this.name = name; this.age = age; this.play = function () { console.log(this.name + "愉快的玩要"); }; } var dog = new Dog("阿黄", 3); dog["play"](); // 调用方法 console.log(dog["name"]); // 调用属性 console.log(dog["age"]); ``` 属性名是字符串拼接的情况 ```javascript // 属性的字符串拼接 var den = { wife1: {name:'xiaoliu'}, wife2: {name:'xiaozhang'}, wife3: {name:'xiaowang'}, sayWife: function (num) { return this['wife'+num] } } console.log(den.sayWife(1)); // {name: "xiaoliu"} =================================================== var obj = { name:'www', age:12, sex: 'wrwr' } for (var key in obj){ console.log(obj.key) // 打印3次undefined 为什么呢?因为执行obj.key相当于是==>obj["key"],这个字符串的key属性是对象没有的,所以是undefined console.log(obj[key]) // 这样才是对的 } ``` ### 知识点 + 对象是一组无序属性的集合,属性的值可以是任意的数据类型 ``` function Person(name, age, bool) { this.name = name; // 字符串 this.age = age; // 数字 this.dog = {name:"阿花", age:18}; // 对象 this.sex = bool; // 布尔值 this.play = function () { // 函数 console.log("喜欢玩游戏") }; } var xiaoMing = new Person("小明", 18, true); console.log(xiaoMing.sex?"男":"女"); // 男 console.log(xiaoMing.dog.name); // 阿花 ============================================ ``` + json格式的数据:一般都是成对的,是键值对,json也是一个对象,数据都是成对的,一般json格式的数据无论是键还是值都是用双引号括起来的。 JSON.parse():string —> json JSON.stringify():json —>string ``` var json = { "name":"小明", "age":18, "sex":"男" } // 遍历对象,不单是json,所有的对象都适用 for (var key in json) { // console.log(json.key) // 这种是错误的,key 是个变量,不是值,在属性里没有,会是undefined,点语法,如果没有属性值,会是undefind console.log(key + "====" + json[key]); } ``` + 判断一个对象里有没有指定属性 ``` // 判断一个对象里有没有指定的属性 var obj = { sex:"男", age:18 }; if (obj["sex"]) { console.log("有"); }else { console.log("没有"); } // 有 ``` ``` // 判断一个对象里没有指定的属性 var obj = { sex:"男", age:18 }; function check(key, obj) { return obj[key]?true:false; } console.log(check("sex", obj)); // true ``` + 在对象中一个方法调用另一个方法 ``` // 在对象中一个方法调用另一个方法 obj = { sayHi:function () { console.log("sayhi方法"); this.eat(); // 此时的this就是obj }, eat:function () { console.log("eat方法"); } }; ``` ### 对象的枚举 #### `for in ` 会延伸到原型链上的属性,但是不会打印Object上的东西。 hasOwnProperty(属性名) ,判断一个属性是自己的还是原型链上的,是自己的为true ```javascript var obj = { name:'www', age:12, sex: 'wrwr', __proto__: { lastName:'邓' } } for (var key in obj){ // console.log(obj.key) // 打印3次undefined 为什么呢?因为执行obj.key相当于是==>obj["key"],这个字符串的key属性是对象没有的,所以是undefined if (obj.hasOwnProperty(key)) { // 判断obj自身有没有这循环出来的这些属性,有了才打印,for in 会延展到原型链上,如果不加判断则会打印出邓 console.log(obj[key]) // 这样才是对的 } } ``` #### `in` 判断整个原型链上有没有这个属性,有就是true, `"lastName" in obj`则为true #### `instanceof` `A instanceof B` A对象是不是B构造函数构造出来的,实际上是看A的原型链上有没有B的原型 ```javascript person instanceof Person // true person instanceof Object // true [] instanceof Array // true [] instanceof Object // true ``` #### 判断数据的类型 ```javascript // 第一种判断数组和对象的方法 if ([].constructor === Array){ console.log('是一个数组'); } var obj = {}; if (obj.constructor === Object) { console.log('是一个对象'); } // 第二种区分数组和对象的方法 console.log([] instanceof Array ? '是数组':'是对象'); console.log(obj instanceof Array ? '是数组':'是对象'); // toString, 因为函数重写了toString方法 // Object.prototype.toString = function () { // // 识别this // // 返回相应的结果 // } console.log(Object.prototype.toString.call([])); // [object Array] console.log(Object.prototype.toString.call(123)); // [object Number] console.log(Object.prototype.toString.call({})); // [object Object] 一般用的时候用toString,因为在页面中有iframe子页面的时候,比如子域里的数组到父域[] instanceof Array 会是false ``` ### 对象的深浅拷贝 #### 浅拷贝 总结起来,浅拷贝就是只能拷贝原始值,只是拷贝了引用值的指向,所以引用值一个改变也会影响另一个的改变。 ```javascript obj = { name:'abc', age:123, sex:"female", card:['visa', 'unionpay'] }; function clone(origin, target) { var target = target || {}; for (var key in origin) { target[key] = origin[key]; } return target } var obj1 = {}; clone(obj, obj1); console.log(obj1); obj1.card.push('mm') // 修改引用值 console.log(obj); // 另一个对象中的引用值也会跟着改变 obj1.name = 'bb'; // 修改原始值 console.log(obj) // 另一个对象中的原始值不会发生改变。 ``` #### 深拷贝 ```javascript var obj = { name: "abc", age: 123, card: ["visa", "master"], wife: { name: "bcd", son: { name: "aaa" } } }; // 1.判断是不是原始值 // 2.判断引用值是数组还是对象 // 3. 建立相应的数组或对象 // 4.递归 function deepClone (origin, target) { var target = target || {}, toStr = Object.prototype.toString, arrStr = '[object Array]'; for (var key in origin) { if (origin.hasOwnProperty(key)) { // 只拷贝自身的,而不是原型链上的 if (origin[key] !== "null" && typeof(origin[key]) == "object") { // 开始判断是数组还是对象 target[key] = toStr.call(origin[key]) == arrStr ? []:{}; deepClone(origin[key], target[key]) }else { // 是原始值就直接拷贝 target[key] = origin[key]; } } } return target; } var obj1 = {}; deepClone(obj, obj1) ``` ## 十三、内置对象 对象分为三种:内置对象、自定义对象、浏览器对象 内置对象:系统提供的 自定义对象:自己写的对象 浏览器对象:浏览器的 实例对象,通过构造函数创建出来的,实例化的对象; 静态对象,不需要创建,直接就是一个对象,方法(静态方法),直接通过这个对象名子调用。 实例方法必须通过实例化后的对象调用的;对象.方法(); 静态方法必须通过大写的构造函数这样的调用的方式调用的;构造函数.方法() ### (一)Math Math 是一个内置对象,具有数学常数和函数的属性和方法,不是一个构造函数,所有的属性和方法都是静态的。 #### Math 对象属性 | 属性 | 描述 | | :----------------------------------------------------------- | :------------------------------------------------ | | [E](http://www.w3school.com.cn/jsref/jsref_e.asp) | 返回算术常量 e,即自然对数的底数(约等于2.718)。 | | [LN2](http://www.w3school.com.cn/jsref/jsref_ln2.asp) | 返回 2 的自然对数(约等于0.693)。 | | [LN10](http://www.w3school.com.cn/jsref/jsref_ln10.asp) | 返回 10 的自然对数(约等于2.302)。 | | [LOG2E](http://www.w3school.com.cn/jsref/jsref_log2e.asp) | 返回以 2 为底的 e 的对数(约等于 1.414)。 | | [LOG10E](http://www.w3school.com.cn/jsref/jsref_log10e.asp) | 返回以 10 为底的 e 的对数(约等于0.434)。 | | [PI](http://www.w3school.com.cn/jsref/jsref_pi.asp) | 返回圆周率(约等于3.14159)。 | | [SQRT1_2](http://www.w3school.com.cn/jsref/jsref_sqrt1_2.asp) | 返回返回 2 的平方根的倒数(约等于 0.707)。 | | [SQRT2](http://www.w3school.com.cn/jsref/jsref_sqrt2.asp) | 返回 2 的平方根(约等于 1.414)。 | #### Math 对象方法 | 方法 | 描述 | | :----------------------------------------------------------- | :----------------------------------------------------------- | | [abs(x)](http://www.w3school.com.cn/jsref/jsref_abs.asp) | 返回数的绝对值。 | | [acos(x)](http://www.w3school.com.cn/jsref/jsref_acos.asp) | 返回数的反余弦值。 | | [asin(x)](http://www.w3school.com.cn/jsref/jsref_asin.asp) | 返回数的反正弦值。 | | [atan(x)](http://www.w3school.com.cn/jsref/jsref_atan.asp) | 以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。 | | [atan2(y,x)](http://www.w3school.com.cn/jsref/jsref_atan2.asp) | 返回从 x 轴到点 (x,y) 的角度(介于 -PI/2 与 PI/2 弧度之间)。 | | [ceil(x)](http://www.w3school.com.cn/jsref/jsref_ceil.asp) | 对数进行上舍入。 | | [cos(x)](http://www.w3school.com.cn/jsref/jsref_cos.asp) | 返回数的余弦。 | | [exp(x)](http://www.w3school.com.cn/jsref/jsref_exp.asp) | 返回 e 的指数。 | | [floor(x)](http://www.w3school.com.cn/jsref/jsref_floor.asp) | 对数进行下舍入。 | | [log(x)](http://www.w3school.com.cn/jsref/jsref_log.asp) | 返回数的自然对数(底为e)。 | | [max(x,y)](http://www.w3school.com.cn/jsref/jsref_max.asp) | 返回 x 和 y 中的最高值。 | | [min(x,y)](http://www.w3school.com.cn/jsref/jsref_min.asp) | 返回 x 和 y 中的最低值。 | | [pow(x,y)](http://www.w3school.com.cn/jsref/jsref_pow.asp) | 返回 x 的 y 次幂。 | | [random()](http://www.w3school.com.cn/jsref/jsref_random.asp) | 返回 0 ~ 1 之间的随机数。 | | [round(x)](http://www.w3school.com.cn/jsref/jsref_round.asp) | 把数四舍五入为最接近的整数。 | | [sin(x)](http://www.w3school.com.cn/jsref/jsref_sin.asp) | 返回数的正弦。 | | [sqrt(x)](http://www.w3school.com.cn/jsref/jsref_sqrt.asp) | 返回数的平方根。 | | [tan(x)](http://www.w3school.com.cn/jsref/jsref_tan.asp) | 返回角的正切。 | | [toSource()](http://www.w3school.com.cn/jsref/jsref_tosource_math.asp) | 返回该对象的源代码。 | | [valueOf()](http://www.w3school.com.cn/jsref/jsref_valueof_math.asp) | 返回 Math 对象的原始值。 | ### (二)Date ``` // 创建一个Date的实例化对象(不传参) var date = new Date(); console.log(date); // 获取时间戳 console.log(date.getTime()) // 传入参数 var date1 = new Date("2000-5-4") console.log(date1); // 获取年份 console.log(date.getFullYear()); // 获取月份 console.log(date.getMonth() + 1);//是从0开始的,真实的需要加1 // 获取日期 console.log(date.getDate()); // 获取时 console.log(date.getHours()); // 获取分 console.log(date.getMinutes()); // 获取秒 console.log(date.getSeconds()); // 获取星期 console.log(date.getDay()); // 周日是0,依此类推 // 英文的日期的字符串 console.log(date.toDateString()); // 数字的日期的字符串 console.log(date.toLocaleDateString()); // 英文的时间的字符串 console.log(date.toTimeString()); // 中文的时间的字符串 console.log(date.toLocaleTimeString()); ``` ``` // 格式化日期输出“2018年11月15日 11:23:23” /** 检查月、日、时、分、秒是不是大于0 * @param num 日期的数字 * @returns {string} 返回的是清洗后的数字 */ function checkDate(num) { return num>10?num:"0"+num; } /** 返回指定格式的日期字符串 * @param date 日期对象 * @returns {string} 返回的是字符串 */ function formatDate(date) { // var date = new Date(); // 可以直接向函数传一个对象的参数 //年 var y = date.getFullYear(); //月 var m = checkDate(date.getMonth() + 1); // 月份是从0开始的,所以要加1 //日 var d = checkDate(date.getDate()); //时 var h = checkDate(date.getHours()); //分 var M = checkDate(date.getMinutes()); //秒 var s = checkDate(date.getSeconds()); var str = y+"年"+ m +"月"+d+"日" + h +":"+M+":"+ s; return str; } console.log(formatDate(new Date())); // 可以直接向函数传一个对象的参数 ``` ``` // 毫秒值转成日期对象 var date = new Date(); console.log(date.valueOf()) // 得到毫秒数 1546436676284 var dt = new Date(date.valueOf()); console.log(dt); ``` ### (三)String String是一个对象,字符串可以看成是字符组成的数组,但是JS中没有字符类型,字符是一个一个的,在别的语言中是用单引号括起来的,在JS中字符串可以使用单引号或者双引号,因为字符串可以看成是数组,所以能用for循环进行遍历。 字符串的特性:不可变性。单独写是基本的数据类型(栈)通过new构造函数的方式创建的是引用类型(堆 栈)javascript会将基本的数据类型的字符串转化为字符串对象,来使用字符串对象的方法。 字符串的重新赋值,看起来是字符串发生改变了,实际上是指向改变了,并不是值改变了,原来的值还在内存中。 ``` // 创建一个字符串的对象,两种方法:一是字面量,第二种系统构造函数创建对象 var str = "小明好帅abc"; var str1 = new String('今天是个好天气'); // 字符串对象的属性 var len = str.length; console.log(len); // 7 // **** 1、字符方法 // charAt() 返回指定位置的字符 console.log(str.charAt(2)); // 好 // charCodeAT() 返回指定位置的unicode编码 console.log(str.charCodeAt(4)); // **** 2、字符串的操作方法 // concat 和字符串的拼接+ 一样,比+更常用 console.log(str.concat(str1, "哈哈")); // 小明好帅abc今天是个好天气哈哈 // slice 从strat位置截取字符串的片断,直到end end 截取不到,一个参数一直截取到最后,不支持反截取 console.log(str.slice(2)); // 好帅abc console.log(str.slice(2, 4)); // 好帅 console.log(str.slice(2, -2)); // 好帅a console.log(str.slice(4, 2)); // 空 截取不到 // substring() 功能和slice一样,不一样的是不支持负数索引值,但是支持反截取 console.log(str.substring(2, -2)); // 小明 不支持负数索引,会从0位置截取 console.log(str.substring(2, 4)); // 好帅 console.log(str.substring(4, 2)); // 好帅 // substr() 从开始的位置截取长度为几个的字符串 第一个参数是开始的位置,第二个参数为截取的长度,只写一个参数,从开始一直到结束 console.log(str.substr(3, 1)); // 帅 // **** 3、位置方法 // indexOf() 返回指定内容在字符串中的位置,找不到则返回-1,第二个参数是从第几个索引开始找 console.log(str.indexOf("帅")); // 3 console.log(str.indexOf("帅", 5)); // -1 // lasttIndexOf() 从右往左找返回指定内容在字符串中的位置,找不到返回-1,第二个参数无意义 console.log(str.lastIndexOf("帅")); // 3 console.log(str.lastIndexOf("帅", 5)); // 3 // **** 4、去除空白 // trim() 只能是去除字符串两端的空格,不能去除中间的 console.log(" abess ".trim()); // **** 5、大小写转换方法 // 小写转大写 console.log(str.toUpperCase()); // 小明好帅ABC console.log(str.toLocaleUpperCase()); // 小明好帅ABC // 大写转小写 console.log("SFSFS".toLocaleLowerCase()); // sfsfs console.log("SAADAD".toLowerCase()); // saadad // **** 6、与正则相关的 // split() 把一个字符串分隔成字符串数组 第一个参数,正则表达式或者是分隔符,第二个参数分隔后数组的长度 console.log("wsf|sfsw|sfwrr|wwrw|www".split('|')); // ["wsf", "sfsw", "sfwrr", "wwrw", "www"] // replace 替换 第一个参数是被替换的内容或是regexp 正则对象, 第二个参数是新的内容 console.log(str.replace("帅", "丑")); // 小明好丑abc console.log("123,321,456".replace(',', '')); // 123321,456 只会替换第一个不是全局的 console.log("123,321,456".replace(/,/g, '')); // 123321456 // search() 查找指定内容或正则表达式的索引,找不到返回-1, console.log(str.search("帅")); // 3 ``` 案例: ``` // 截取指定的字符串 var str = "我喜欢村里的小芳"; var key = "小芳"; var index = str.indexOf(key); str = str.slice(index, index+key.length); console.log(str); ``` ``` // 找到字符串中所有o的位置 var str = "wrwrowrowrsfsfowrsfsfwosfsfosfwosfwwowrwo"; var key = "o"; var index = 0; while ((index = str.indexOf(key, index))!= -1) { /* 先算等号右边的,如果index不等于-1, 就循环查找,本次查找到的索引值作为下一次查找的开始位置 */ console.log(index); index += key.length; // 不加上查找字符串的长度,会成为死循环 }; // 将上述代码封装成函数 function finedStr(str, key) { var index = 0; while ((index = str.indexOf(key, index)) != -1) { console.log(index); index += key.length; } } var str1 = "wsfpsfwpsfwpsfwpsfsp" console.log(finedStr(str1, "p")); ``` ``` // 找出字符串的每个字母出现了多少次 var str = "ooouwrosiQWiadWooiuQWWueyrREwq"; // 第一步先将字符串全部变为大写或小写 str = str.toLowerCase(); // 创建一个空对象,目的为了存储每个字母的次数 var obj = {}; // 循环遍历字符串,获取每个字母 for (var i=0; i split),toString, slice ```javascript // 数组的方法 var arr1 = [1, 2, 3, 45, 6]; // 1、Array.from()将一个可迭代对象,转换为数组, 不改变原有的,返回一个新的数组 console.log(Array.from(arr1)); // [1, 2, 3, 45, 6] console.log(Array.from("abcedf")); // ["a", "b", "c", "e", "d", "f"] // 2、concat() 合并多个数组,不修改原来的,返回一个新的数组,不会去重 console.log(arr1.concat([7, 8, 9])); // [1, 2, 3, 45, 6, 7, 8, 9] console.log(arr1.concat([1, 2, 3, 7])); //  [1, 2, 3, 45, 6, 1, 2, 3, 7] // 2、every(callback 回调函数) 测试数组中的所有的元素是否通过了指定函数的测试 // 函数作为参数使用,整体返回boolean值是and的关系,函数中有三个参数,第一个是元素的值,第二个 // 参数是元素的索引值,第三个参数是调用方法的数组(一般没用) 不用可以不写 var flage = arr1.every(function (ele, index) { return ele > 2; }); console.log(flage); // false // 3、filter(函数) 参数是一个回调函数,通过回调函数,过滤数组中的每个元素,把符合条件的元素,组成一个新的数组,返回 // 函数有三个参数,第一个是元素的值,第二个是元素的索引,第三个是调用方法的数组,不用不以不写 var newArry = arr1.filter(function (ele) { return ele > 5; }); console.log(newArry); // [45, 6] // 4、forEach() 遍历数组,相当于for 循环,数组的每一个元素都执行一次回调函数 // 函数有三个参数,第一个是元素的值,第二个是元素的索引,第三个是调用方法的数组,不用不以不写 var arr1 = [1, 2, 3, 45, 6]; arr1.forEach(function (ele, index) { console.log(ele + "====" + index); }); /* 1 ====0 2 ====1 3 ====2 45 ====3 6 ====4 */ // 5、map(函数) 数据中的每个元素都要执行一次回调函数,把新的值组成一个新的数组,并返回 // 函数有三个参数,第一个是元素的值,第二个是元素的索引,第三个是调用方法的数组,不用不以不写 var arr1 = [1, 2, 3, 45, 6]; var newArr = arr1.map(function (ele, index) { return ele += 3; }); console.log(newArr); // [4, 5, 6, 48, 9] // 6、push() 追加元素 原地修改 返回新数组长度,不去重 var arr2 = [1, 2, 3]; console.log(arr2.push(3)); // 4 console.log(arr2); //  [1, 2, 3, 3] // 7、unshift() 向数组的第一个元素前面插入一个新的元素 原地修改,返回新数组长度,不去重 var arr2 = [1, 2, 3]; console.log(arr2.unshift(3)); // 4 console.log(arr2); //  [3, 1, 2, 3] // 8、pop() 删除最后一个元素或者指定的元素, 原地修改,返回被删除的元素, var arr2 = [1, 2, 3, 'aa']; console.log(arr2.pop("aa")); // aa console.log(arr2); // [1, 2, 3] console.log(arr2.pop()); // 3 console.log(arr2); // [1, 2] // 9、shift() 删除数组中的第一个元素,不能删除指定的元素,原地修改,返回被删除的元素 var arr2 = [1, 2, 3, 'aa']; console.log(arr2.shift("aa")); // 1 删除指定的元素,失败 console.log(arr2); // [2, 3, "aa"] console.log(arr2.shift()); // 2 console.log(arr2); // [3, "aa"] // 10、reverse 翻转数组,原地修改,返回排序后的数据 var arr2 = [1, 2, 3, 'aa']; console.log(arr2.reverse()); // ["aa", 3, 2, 1] console.log(arr2); // ["aa", 3, 2, 1] // 11、sort(排序函数) 数组排序 原地修改,返回排序后的数据 // 不写函数不稳定,回调函数的第一个参数相当于arr[j], 第二个参数相当于arr[j+1] // 规则: 1、必须写两个参数 2、看返回值:(1)当返回值为负数时,那么前面的数放在前面;(2)为正数,那么后面的数 // 在前;(3)为0,不动。 var arr2 = [3, 4, 2, 8, 9, 10, -1]; console.log(arr2.sort(function (a, b) { if (a > b) { return 1; } else if (a == b) { return 0; } else { return -1; } // 简化版的 // return a-b })); // 不写回调函数结果是[-1, 10, 2, 3, 4, 8, 9] ,写了回调函数 [-1, 2, 3, 4, 8, 9, 10] console.log(arr2); // 不写回调函数结果是[-1, 10, 2, 3, 4, 8, 9] 写了回调函数 [-1, 2, 3, 4, 8, 9, 10] // 12、indexOf() 返回指定元素的索引,第二参数,查找的起始位置,找不到返回-1 var arr = [1, 2, 2, 2, 3, 4]; console.log(arr.indexOf(2)); // 1 console.log(arr.indexOf(2, 3)); // 3 console.log(arr.indexOf(500)); // -1 // 13、lastIndexOf 从右往左,返回指定元素的索引,找不到返回-1 var arr = [1, 2, 2, 2, 3, 4]; console.log(arr.lastIndexOf(2)); // 3 console.log(arr.lastIndexOf(2, 2)); // 2 console.log(arr.lastIndexOf(500)); // -1 // 14、join 将数组按指定的元素组成一个新的字符串,返回 var arr = [1, 2, 3, 4, 5]; console.log(arr.join(":")); // 1:2:3:4:5 // 15、selice() 浅拷贝从参数(开始索引到结束索引之间的元素,不包括结束索引),组成一个新的数据 返回,原数组不变,不传参数就是拷贝整个数组 var arr = [1, 2, 3, 4, 5]; console.log(arr.slice(1, 3)); // [2, 3] console.log(arr.slice(3)); // [4, 5] console.log(arr); // [1, 2, 3, 4, 5] // 16、splice(开始的位置,要删除的个数, 替换的元素值) 一般用于删除数组中的元素,或者是替换元素,或者是插入元素 // 替换返回的是[]空列表,删除返回的是删除的元素组成的列表 var arr = [1, 2, 3, 4, 5]; console.log(arr.splice(3, 0, 'deue')); // [] 在任意位置插入元素 console.log(arr); // [1, 2, 3, "deue", 4, 5] var arr = [1, 2, 3, 4, 5]; console.log(arr.splice(2, 2)); // [3, 4] console.log(arr); // [1, 2, 5] ``` 技巧: ``` // 清空一个数组 var arr = [1, 2, 3, 4, 5]; arr.length = 0; console.log(arr); // [] ``` ### (五)包装类 基本包装类型 + 普通变量不能直接调用属性或者方法; + 对象可以直接调用属性和方法; **基本包装类型:本身是基本类型,但是在执行代码的过程中,如果这种类型的变量调用了属性或者是方法,那么这种类型就不再是基本类型了,而是基本包装类型,这个变量也不再是普通的变量了,而是基本包装类型对象 ** string number boolean 是基本类型,也是基本包装类型 ```javascript var str = "hello"; str = str.replace("ll", "HH"); // 基本数据类型调用了方法 console.log(str); // heHHo 这个时候,str就再是基本数据类型了,而是基本包装类型对象 var num = 10; // 原来是基本数据类型 console.log(num.toString()); // 10 调用了方法,那么现在就是基本包装类型了 规律: 如果是一个对象&&true,那么结果是true; 如果 是一个true&&对象,那么结果是对象; var flag = new Boolean(false); var result = flag && true; console.log(result); // true var flag = new Boolean(false); var result = true && flag; console.log(result); // Boolean var num = 10; var num2 = Number("10") // 类型转换,还是基本数据类型,没有new var num3 = new Number("10"); // 基本包装类型 var str = "hello"; str.li = "ee"; console.log(str.li); // undefined; 因为str在调用属性的时候,相当于是调用了new String("hello") 包装成了引用类型,然后添加了属性,当执行log的时候,又相当是再次调用了new String("hello")的方法,而这个新的对象里是没有li的属性的。 ``` # Web API ## 一、API相关的概念 ** 什么是API ** API是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。 + 任何开发语言都有自己的api + API的特征输入和输出 + API的使用方法(console.log()) ** 什么是WEB API ** 浏览器提供一套操作浏览器功能和页面的API(BOM和DOM) ## 二、DOM DOM 就是把页面看成是一个文档,学习DOM就是学习操作文档里的元素。DOM又称为文档树模型 DOM树:把html页面或者是xml文件看成是一个文档,文档就是一个对象,这个文档中所有的标签都是元素,元素也可以看成是对象,标签(元素、对象)有很多,还有嵌套关系组成的这种层次结构,可以模拟成树形结构图,简称:树状图,就是dom树 DOM对象:通过DOM方式获取的元素得到的对象 文档 一个网页可以称为文档 节点(node):页面中所有的内容都是节点(包括标签、属性、文本(文字、换行、空格、回车)、注释等) 元素(elementt):页面中所有的标签都是元素,元素可以看成是对象。 根元素:html标签 页面中的顶级对象:document 属性 标签的属性 HTML文件是用来展示信息,展示数据的 XML:侧重于存储数据 HTML 文件看成是一个文档(document),把这个文档也可以看成是一个对象,文档中所有的标签都可以看成是一个对象 ### DOM结构树(继承关系) ![dom结构树](/Users/mengjuncheng/Documents/notes/前端/javascript笔记/media/dom结构树.png) DOM结构树上相关联的有这个继承的关系,例如在`HTMLElement.abc=123`,那么在它的分支下的任何一个拥有继承关系的分支上,都会有这个属性。比如在页面中选中一个元素,那么这个元素身上也有abc=123这个属性。 `document.__proto__` ==> `HTMLDocument` `HTMLDocument.__proto__` ==>`Document` `Document.__proto__` ==> `Node` `Node.__proto__`==> `EventTarget` `EventTarget.__proto__` ==>`Object` ## 三、DOM 常见的操作 + 获取元素 + 动态创建元素 + 对元素进行操作(设置其属性或调用其方法) + 事件(什么时机做相应的操作) 1、`getElementByid`方法定义在`Document.prototype`上,即`Element`节点上不能使用。 2、`getElementsByName`方法定义在`HTMLDocument.prototype`上,即非`html`中的`document`不能使用(`xml documentElement`) 3、`getElementsByTagName`方法定义在`Document.prototype`和`Element.prototype`上,换句话说就是获取到的元素可以继续调用`getElementsByTagName`这个方法。 4、`HTMLDocument.prototype`定义了一些常用的属性,`body.head`分别指代`HTML`文档中的``标签 5、`Document.prototype`上定义了`documentElement`属性,指代文档的根元素,在`HTML`文档中,他总是指代 ``元素。 6、`getELementsByClassName`、`querySelectorAll`、`querySelector`在`Docuement.prototype`,`Element.prototype`类中均有定义。 ### 获取页面元素的方法 除了id,其他的选择器取出来的都是一组的,基本上全部是类数组 1、根据id属性获取元素 返回的是一个元素对象 getElementById("id") 2、根据标签名获取元素 返回的是元素对象组成的伪数组 getElementsByTagName("标签的名子") 下面的几个,有的浏览器不支持 3、根据表单的name属性获取元素,返回的是元素对象组成的一个伪数组 getElementsByName("name属性") ```




``` 4、根据类样式的名子来获取元素,返回的是元素对象组成的伪数组 getElementsByClassName("类名子") ``` Title
这是第一个div
这是第二个div
span ``` 5、根据css选择器获取元素,返回来的是一个元素对象 querySelector("选择器名字"),并不常用,选择出来的东西不是时事的 6、根据css选择器获取元素,返回的是元素对象组成的伪数组 querySelectorAll("选择器的名字") ## 四、事件 事件的三要素 + 事件源:被触发事件的元素 + 事件类型:事件的触发方法(如鼠标经过、鼠标点击) * 鼠标事件 click(点击)、mousedown(鼠标按下),mouseup(鼠标抬起),一个鼠标的click点击事件分开来看就是是先down,再up,形成一个click事件。 mousemove:鼠标移动事件 contextmenu:鼠标右键产生菜单事件。 mouseover进入,mouseout离开,mouseenter、mouseleave - mouseover和mouseout在父元素和其子元素都可以触发,当鼠标穿过一个元素时,触发次数得依子元素数量而言。 - mouseenter和mouseleave只在父元素触发,当鼠标穿过一个元素时,只会触发一次。 - mouseover和mouseout比mouseenter和mouseleave先触发 因此一般mouseover和mouseout一起使用,mouseenter和mouseleave一起使用, 必须是事件mousedown和mouseup事件来判断,用事件参数对象button的属性值来区分鼠标的左右键:0/1/2,左/中键/右键,click事件只能监听左键。 * 键盘事件 * 和鼠标的click事件不一样,keydown+ keyup != keypress, * keydown>keypress>keyup * keydown和keypress的区别 * keydown的事件参数参数对象的charCode属性值为0,而keypress的charCode是每个字母的ASCII码,而两个的which都有值,对应的是每个键的值,但是大小写也是一样的,所以字符用keypress,而操作类的用keydown. * keydown可以响应任意键盘按键,keypress只可响应字符类键盘按键 * keypress返回ASCII码,可以转换成相应字符。 * 文本操作事件 * input(输入一次触发一次),change(鼠标聚焦,与失去聚点,两次input的value值发生改变时才触发) * focus获得焦点触发, blur失去焦点后触发, * 窗休操作类(window上的事件) * scroll 页面滚动条发生滚动触发 * load 整个文档解析完毕后才触发,效率低。 + 事件的外理程序:事件触发后要执行的代码(函数形式) 点击操作: 就是一个事件。 事件:就是一件事,比如 按键被点击,弹出对话框,点击就是事件的名子 触发:被点了,就是触发了 响应:弹框了就是响应 事件源:按键就是事件源 事件绑定的特性 一但绑定 这个元素 就一直有这个事件了,即使事件中修改了这个元素的id,事件也会继续有这个事件。 事件源对象:event.target 火狐只有这个,event.srcElement ie只有这个,这两个chrome都有 `var target = event.target || event.srcElement;` 事件参数对象:window.event和e都是事件参数对象,一个是Ie的标准,一个是火狐的标准,事件参数e在ie8的浏览器中是不存在,此时用window.event来代替。`var event = e ||window.event;` 事件委托:通过事件的冒泡把子元素的事件传传递给父元素,父元素再通过事件源对象操作每个子元素 优点:1、性能不需要循环所有的元素一个个绑定事件 ​ 2、灵活,当有新的子元素时不需要重新绑定事件。 ```javascript
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
``` (一)点击事件 onclick ``` ``` (二)鼠标进入和移出事件 onmouseover进入 onmouseout 移出 案例一:鼠标进入离开列表高亮显示 ``` Title
  • 五菱神车
  • 五菱神车
  • 五菱神车
  • 五菱神车
  • 五菱神车
  • 五菱神车
  • 五菱神车
  • 五菱神车
``` 案例二:鼠标进入和移出显示二维码 事件响应代码中添加和移除类名的方式,显示或隐藏,注意多个类名的时候,要把之前的也要重新写上 ```
``` 案例三:鼠标经过高亮显示边框 注意,由于border会增加盒子的大小,鼠标经过时会显示跳,影响下面的布局,所以先在css里设置好边框大小,颜色和背景一样,鼠标经过时只改边框颜色就可以了 ``` Title
``` (三)焦点事件 进入焦点:onfocus 失去焦点:onblur 案例一:模拟搜索框焦点事件 ``` Title ``` 案例二:验证密码长度 ``` Title ``` ## 五、操作属性 能对属性进行赋值,也可以通过属性拿对对应的值 ### (一)非表单元素的属性 href、 title 、id、 src、 className 案例一:点击按钮显示图片 ``` ``` 案例二:点击修改a标签的地址和热点文字 ``` ``` 案例三 点击修改P标签的显示内容 ```

这是一个p标签

``` 案例四 点击修改多个P标签的文字 ```

王者归来

王者归来

王者归来

王者归来

王者归来

王者归来

哈哈,变帅了

哈哈,变帅了

哈哈,变帅了

哈哈,变帅了

哈哈,变帅了

啦啦,变丑了

啦啦,变丑了

啦啦,变丑了

啦啦,变丑了

啦啦,变丑了

``` 案例5 点击修改图片的alt title ``` ``` 案例6:点击修改所有文本框的值 ```










``` 案例7 为每一张图片添加点击事件 ``` ``` 案例8:点击按键修改按钮本身的value属性 ``` ``` 案例9:点击图片修改自身的宽高 ``` body> ``` 案例10 按钮的排他功能 ``` ``` 案例11 点击超连接切换图片 ``` ``` 案例12 点击按钮切换图片-通过封装document.getElementById()的方式 ``` // common.js /** * 根据id值,返回对应的标签元素。 * @param id id 属性的值,string类型 * @returns {HTMLElement} 元素对象 */ function my$ (id) { return document.getElementById(id); } ``` ``` ``` ### (二)表单元素属性 + value 用于大部分表单元素的内容获取(option除外) + type 可以获取input标签的类型(输入框或复选框等) + disabled 禁用属性 + checked 复选框选中属性 + selected 下拉菜单选中属性 + name + readonly 只读 案例1:点击修改性别和兴趣 ```
保密

吃饭 睡觉 打豆豆 保蓝球 踢足球
``` 案例2:点击按钮选择菜单 ``` ``` 案例3:点击修改文本域 ``` ``` ### (三)样式操作 #### 1、使用style方式设置的样式显示在标签行内 对象.style.属性 = 值;设置标签的 对象.style.classText = 值 获取:getComputedStyle,因为对象.style.属性只能拿到行间样式,拿不到css类里的属性。 window.getComputedStyle(ele, null).属性。计算样式只读,返回计算样式的值都是绝对值,没有相对单位,ie8及ie8以下不兼容。第二个参数是用来获取伪元素的,把伪元素名子放到第二个参数上。 在ie8及ie8以下,使用ele.currentStyle[属性]来获取只读属性,返回的计算样式的值不是经过转换的绝对值, 案例1:点击修改div的宽高背景颜色 ```
``` 案例2 点击隐藏或显示div ``` Title
``` 案例3 一个按钮解决div的隐藏与显示 ``` Title
``` #### 2、类名操作 修改标签的className属性相当于直接修改标签的类名 对象.className = 值 案例1 通过类样式,点击设置div样式 ``` Title
``` 案例2 通过类样式显示隐藏div ``` Title
``` 案例3 关灯效果 ``` ``` ### (四)阻止a链接浏览器默认跳转和其他的默认事件 + return false; + 事件参数对象.preventDefault()方法; + href="javacript:void(0);" ```javascript 百度一下 百度一下 百度一下 //还有一种写法 function cancelHandler(evernt){ if (event.preventDefault) { event.preventDefault(); }else { event.returnValue = false; } } ``` 案例一:点击小图切换大图 ``` ``` 案例二:美女相册 ```

美女画廊

选择一个图片

``` ### (五) innerText和textContent兼容性 + 设置标签中的文本内容,应该使用textContent属性,谷歌、火狐支持,ie8不支持 + 设置标签中的文本内容,应该使用innerText属性,谷歌、ie8支持,高版本的火狐支持,低版本的不支持 + 如果这个属性在浏览器中不支持,那么这个属性的类型是undefined,判断这个属性的类型是不是undefined,就知道浏览器支不支持。 ```
螺蛳粉就算了
``` ### (六)innerText和innerHTML区别 + innerText主要是设置文本的,设置标签内容,是没有标签的效果的,同时会把标签内的所有内容(包括包含的标签)都替换为新的内容 文本的形式 + innerHTML 可以设置文本内容,主要作用是在标签中设置新的html标签内容,是有标签的效果的,同时把标签内的所有内容(包括包含的标签)都替换成 + 新的内容 解析成html代码的形式 + innerText 获取的时候,会把标签内的所有的文本内容到取到。包括嵌套的标签里的内容,文本的形式 + innerHTML 获取的时候,会把标签内的所有内容,原样获取到,html代码的形式 ``` Title
酸辣粉叫我 哈哈哈
``` ### (七)自定义属性 + 自定义属性:也叫隐式属性,本身html标签中没有这个属性,是自己添加的,用来存储一些数据。setAttribute(属性名,属性值) + 在html标签中添加的自定义属性,是无法通过对象.属性获取的,因为自定义属性在标签中,并不在DOM对象中,所以需要使用getAttribute("自定义属性名子")来获取的,也可以拿到显示属性。 + 在html标签中设置自定义属性,是无法通过对象.属性=值来设置的,因为这种方法自定义属性在DOM对象上,不在标签中 + 删除自定义属性:removeAttribute("属性的名子") 用于移除自定义属性和标签的属性 + 判断有没有这个属性 hasAttribute(属性名) 返回布尔值。 案例一:自定义属性的获取 ``` Title
  • 小明的成绩
  • 小花的成绩
  • 小李的成绩
``` 案例二:自定义属性的设置与获取 ``` Title
  • 小明的成绩
  • 小明的成绩
  • 小明的成绩
  • 小明的成绩
``` 案例三:删除自定义属性和标签属性 ``` Title
``` 案例四:tab切换案例的实现 排他思想 排他的步骤:1、先删除所有(恢复默认)2、再重新定义 ```
体育 娱乐 新闻 综合
  • 我是体育模块
  • 我是娱乐模块
  • 我是新闻模块
  • 我是综合模块
``` ## 六、节点 ### (一)节点的介绍 页面中的所有内容都是节点,包括标签、文本(文字、空格、换行等)、注释、属性 ### (三)节点的属性 可以使用标签.点出来,可以使用属性节点.出来,文本节点.点出来 + nodeType: 节点的类型 1 -- 标签,2--属性,3—文本,注释-8 , document-9 + nodeName:节点的名子:标签节点--大写的标签名子,属性节点--小写的属性名子,文本节点--#text + nodeValue: 节点的值:标签节点--null, 属性节点--属性值,文本节点--文本内容 + attributes:元素节点的属性集合 ``` Title
这是div中的第一个span标签

这是div中的第二个元素,第一个P标签

  • 这是ul中的第一个元素li
  • 这是ul中的第二个元素li
  • 这是ul中的第三个元素li
``` ### (四)节点的层级 在页面中只有标签能做为父节点(或者叫父元素) 父节点obj.parentNode=(父元素)obj.parentElement; 元素层级中,只有children兼容ie9以下的浏览器 ``` Title
这是div中的第一个span标签

这是div中的第二个元素,第一个P标签

  • 这是ul中的第一个元素li
  • 这是ul中的第二个元素li
  • 这是ul中的第三个元素li
``` #### 节点层级的十二行代码 ``` Title
哈哈 这是div中的第一个span标签

这是div中的第二个元素,第一个P标签

  • 这是ul中的第一个元素li
  • 这是ul中的第二个元素li
  • 这是ul中的第三个元素li
啧啧
å ``` 案例一:利用节点 点击更改p标签的背景颜色 ``` Title
这是span

这是p

这是span

这是p

这是span

这是p

这是span

这是p

这是span

这是p

百度
``` 案例二:隔行变色 节点的方式 ``` Title
  • 五菱宏光
  • 五菱宏光
  • 五菱宏光
  • 五菱宏光
  • 五菱宏光
``` #### 节点的兼容代码 ``` Title
  • 第一个
  • 第二个
  • 第三个
  • 第四个
  • 第五个
``` 案例三:切换背景图片 背景图片变量的拼接 ``` 哈哈,我又变帅了
``` 案例四:全选和全不选逻辑 ```
菜名 饭店
红烧肉 田老师
西红柿鸡蛋 田老师
油炸榴莲 田老师
清蒸助教 田老师
``` ## 七、元素 为了提高用户的体验, 元素创建的三种方式: + document.write("标签的代码及内容"); + 对象.innerHTML="标签及代码"; + document.createElement("标签的名子"); ### (一)document.write() 创建元素有缺陷,如果是在页面**加载完毕后**,此时通过这种方式创建元素,那么页面上存在的所有的内容被干掉了。 ### (二)对象.innerHTML = "内容" innerHTML 返回dom的内容,如果 = 就是赋予dom内容 outerHTML 会返回 dom本身+自己的内容,如果 = 就是连自己也给替换了 innerText 返回dom的文本内容 如果=需要注意,这个dom内如果有其他的标签和内容,就会一同覆盖掉 ```
菜名 饭店
红烧肉 田老师
西红柿鸡蛋 田老师
油炸榴莲 田老师
清蒸助教 田老师
``` ### (三)创建元素 document.createElement('标签名') 创建元素节点 document.createTextNode('文本') 创建文本节点 document.createComment('注释内容') 创建注释节点 1、先创建对象 document.createElement("标签名子") 返回的是一个对象 2、追加到父元素 父元素.appendChild(对象) 追加到父元素的最后 父元素.insertBefore(对象,节点),在父元素内那个子元素前插入 案例一:点击动态创建列表 ``` Title
``` 案例二:动态创建表格 ``` Title
``` ### (四)元素相关的操作方法 + 追加子元素 父元素对象.appendChild("子元素"); + 插入子元素 父元素对象.insertBefore(新元素,在那个元素之前插入); + 替换子元素 父元素对象.replaceChild(新元素,被替换的元素); 返回被替换的元素 + 删除子元素 父元素对象.removeChild(删除的元素); 返回值就是删除的元素 + 删除自身:子元素对象.remove(); 没有返回值 ``` Title
``` ### (五)为同一个元素绑定多个相同的事件 由于 ele.onXXXX=function(){}一个元素上只能绑定一个事件,后面的会把前面的覆盖。这时this指向的是dom元素本身 #### 1、addEventListener() 程序this指向的是dom元素本身 ``` ``` #### 2、attachEvent() 程序指向是window ``` /* * 对象.attachEvent(参数1, 参数2) 只有ie8可以用 * 参数1:事件类型 事件的名子 有on * 参数2:事件处理函数 * */ my$("btn").attachEvent("onclick", function () { console.log("小明很帅"); }); my$("btn").attachEvent("onclick", function () { console.log("小明很帅"); }); my$("btn").attachEvent("onclick", function () { console.log("小明很帅"); }); ``` #### 3、兼容性代码 ``` Title ``` #### 4、绑定事件的区别 总结绑定事件的区别: addEventListener(); attachEvent(); + 相同点: 1、都可以为元素绑定多个事件; + 不同点 1、方法名不一样; 2、参数的个数不一样,addEventListener三个参数,attachEvent两个参数; 3、浏览器的兼容性不一样,addEventListener 谷歌、火狐、ie11支持,ie8不支持;attachEvent 谷歌、火狐不支持,ie11不支持,ie8不支持; 4、this不同,addEventListener中的this是当前绑定事件的对象,attachEvent中的this是window; 5、addEventListener中的事件的类型(事件的名子)没有On,attachEvent中的事件的类型(事件的名子)有on; ### (六)为元素解绑事件 1、对象.onclick = function ----> 对象.onclick = null; 2、对象.addEventListener("事件名子不带On", 命名函数,false)----> 对象.removeEventListener("事件名子不带On", 命名函数,false) 3、对象.attachEvent("on事件名子",命名函数) ----> 对象.detachEvent("on事件名子",命名函数) **注意点:** 1、用什么方式绑定事件,就应该用对应的方式解绑事件,不能互换。 对象.on事件名子 = 事件处理函数 (绑定事件) 对象.on事件名子 = null; 2、如果需要解绑,就要在绑定的时候使用命名函数,不能使用匿名函数; **解绑的兼容性代码 ** ``` Title ``` ## 八、事件冒泡 事件冒泡:多个元素嵌套,有层次关系,这些元素都注册了相同的事件,如果里面的元素的事件触发了,外面元素的该事件也自动触发了。 父级的事件子级都继承,取消冒泡防止父级的事件贯穿到子级 阻止事件冒泡 1、window.event.cancelBubble = true; ie特有的,谷歌支持,火狐不支持 ```javascript ``` 2、事件参数.stopPropagation(); 谷歌和火狐支持,ie8+; ```javascript ``` 兼容代码: 事件的三个阶段: 1、事件捕获阶段:从外向内 2、事件目标阶段:最开始选择的目标 3、事件冒泡阶段:从里向外 4、触发顺序:先捕获后冒泡 5、focus、blur、change、submit、reset、select等事件不冒泡 事件参数.eventPhase 查看事件阶段 为元素绑定事件: addEventListener("没有on的事件类型",事件处理函数,控制事件阶段的),事件触发的过程中,可能会出现 事件冒泡的效果,为了阻止事件冒泡: window.event.cancelBubble=true;谷歌、ie8支持,火狐不支持;window.event就是一个对象,是ie的标准。 e.stopPropagation();阻止事件冒泡 ==>谷歌和火狐支持 window.event和e都是事件参数对象,一个是Ie的标准,一个是火狐的标准,事件参数e在ie8的浏览器中是不存在,此时用window.event来代替。`var event = e ||window.event;` 事件的阶段有三个,通过e.eventPhase这个属性可以知道当前的事件是什么阶段的,如果 这个属性的值是:1--捕获阶段 2--目标阶段 3--冒泡阶段 一般默认都是冒泡阶段,很少用捕获阶段 ``` Title =============取消冒泡兼容代码=============== function stopBubble(event){ if (event.stopPropagation){ event.stopPropagation(); }else { event.cancelBubble = true; } } ``` ## BOM 相关操作 ### 一、BOM介绍 浏览器的顶级对象:window 页面中的顶级对象:document 页面中所有的内容都是属于浏览器的,页面中的内容也是window。因为页面中所有的内容都是window的,window可以省略。 ### 二、BOM的对话框 window.alert("内容") 弹框 每个浏览器的样式不一样,只是在测试的时候用 window.prompt("请输入帐号") 让用户输入 每个浏览器的样式不一样,只是在测试的时候用 var ret = window.confirm("你确定退出吗") 点确定的时候有返回值 true false 每个浏览器的样式不一样,只是在测试的时候用 ### 三、加载事件 只要页面加载完毕,这个事件就会触发----页面所有的内容,标签、属性、文本、包括外部引入的js文件。 ``` window.onload = function () { }; // window 可以省略,但是最好把js文件都放在最后 ``` 页面关闭后才触发的事件 谷歌不支持 ``` window.onunload = function () { }; ``` 页面关闭之前触发的 谷歌不支持 ``` window.onbeforeunload = function () { } ``` ### 四、window 中的对象 #### (一)location对象 ``` Title ``` #### (二)history对象 ``` document.getElementById("btn2").onclick = function () { window.history.forward(); // 相当于浏览器的前进 得有历史记录 }; document.getElementById("btn1").onclick = function () { window.history.back(); // 相当于浏览器上的后退 得有历史记录 }; ``` #### (三)navigator对象 ``` // navigator 对象 // 通过platform 可以判断浏览器所在的系统平台类型 console.log(window.navigator.platform); // MacIntel // 判断浏览器的类型 console.log(window.navigator.userAgent); ``` ### 五、定时器 #### (一)setInterval() 反复 setInterval(参数1, 参数2) 参数1:函数 参数2:时间 毫秒 1000毫秒=1秒 返回值:就是定时器的ID; 执行过程:页面加载完毕后,过了1秒,执行一次函数的代码,又过了1秒,再执行函数,注意的是页面加载完毕,过了间隔的秒后,才执行定时器。 停止定时器:window.clearInterval(参数) 参数:要清理定时器id的值,所以内部的this指向的是window ``` Title ``` 案例一:最简单的轮播图 ```
1 2 3 4 5 6
``` 案例二 完整的轮播图 ```
<>
``` 变速动画封装 ``` ``` #### (二)setTimeout()一次性 setTimeout(函数,时间);时间还是毫秒 返回值:还是该定时器的ID 执行过程:一次性的定时器 停止定时器:window.clearTimeoutl(参数) 参数:要清理定时器id的值,所以内部的this指向的是window ### (三)直接通过document获取属性 ``` //获取body console.log(document.body);//获取的是元素--标签 //获取title console.log(document.title);//标签中的值 document.title="嘎嘎去"; // 设置 //获取html console.log(document.documentElement); ``` ## 九、一些属性和方法 ### (一)offset系列中的属性 值都是数字类型,元素的样式属性是无法直接通过:对象.style,属性来获取的(因为样式在style标签中设置的,只有在style属性中设置的才能获取) + offsetLeft:距离左边位置的值,忽略自身是不是定位元素,如果父级是定位元素,那么距离是到父级的距离,如果不是定位元素,则是到外边框的距离。 + offsetTop:距离上边位置的值 规则同上 + offsetWidth:元素本身的宽,不管内容 元素的尺寸 + offfsetheight:元素的高,不管内容 元素的尺寸 + `offsetParent`返回最近的有定位的父级,如果没有,返回body,body.offsetParent返回null #### 1、没有脱离标准的文档流 offsetLeft:父级元素margin+父级元素padding+父级元素的border+自己的margin #### 2、脱离了标准文档流 主要是自己的left和自己的margin ### (二)scroll系列:卷曲--滚出去 + scrollWidth 元素中内容的实际宽度,如果没有内容,就是元素的宽度 + scrollHeight 元素中内容的实际的高,如果没有内容,就是元素的高 + scrollTop:向上卷曲出去的距离,有了滚动条之后,从可视区无法看到部分的高度,也就是超出部分的高度 + scrollLeft:向左卷曲出去的距离,有了滚动条之后,从可视区无法看到部分的宽度,也就是超出部分的高度 + 兼容性的问题:(都是查看滚动条的滚动距离) 1. window.pageXOffset或者window.pageYOffset IE8及IE8以下不兼容; 2. document.body.scrollLeft/Top document.documentElement.scrollLeft/Top (ie8和ie8以下的浏览器兼容,但是具体那个好使是没有定论的,有可能ie7上面的好使,也有可能下面的好使,有一个规律,在一个浏览器上只要上面的有值,那么下面的一定为零,反之下面的有值,上面的就没值,相加之后就可以兼容ie8和ie8以下的浏览器了) ``` 获取向上卷曲出去的距离的兼容代码 var scrollTop = window.pageYoffset || document.documentElement.scrollTop ||document.body.scrollTop || 0; 也可以这样: var scrollTop = window.pageYoffset || document.documentElement.scrollTop + document.body.scrollTop || 0; 获取向左卷曲出去的距离的兼容代码 var scrollLeft = window.pageXoffset || document.documentElement.scrollLeft ||document.body.scrollLeft || 0; 也可以这样: var scrollLeft = window.pageXoffset || document.documentElement.scrollLeft + document.body.scrollLeft || 0; ``` ### (三)client系列:可视区 + clientWidth:可视区域的宽(没有边框)边框内部的宽度 + clientHeight:可视区域的高(没有边框)边框内部的高度 + clientLeft:左边边框的宽度 + clientTop:顶部边框的宽度 + clientX:可视区域的橫坐标 + clientY:可视区域的纵坐标 查看可视区窗口的尺寸(可以看到的html代码,能看到的部分,不包括浏览器的导航栏,控制台等,页面的缩放会影响到可视区窗口的尺寸) 1. `window.innerWith/innerHeight`w3c标准的。 ie8及ie8以下不兼容 2. ie8及ie8以下兼容 `document.documentElement.clientWidth/clientHeight` 标准模式下(有``这行的就是标准模式,没有就是怪异模式/混杂模式 向后兼容),任意浏览器都兼容 `document.body.clientWidth/clientHeight` 适用于怪异模式下的浏览器 案例图片跟着鼠标 兼容代码的封装过程 ``` var imgObj = document.getElementById("im"); // 火狐 谷歌 document.onmousemove = function (e) { imgObj.style.left = e.clientX + "px"; imgObj.style.top = e.clientY + "px"; }; // 事件参数对象e只有谷歌和火狐有,ie8没有,ie8是window.event来代替e。但是在火狐里不支持window.event,谷歌支持 document.onmousemove = function () { imgObj.style.left = window.event.clientX + "px"; imgObj.style.top = window.event.clientY + "px"; }; // 当有了滚动条之后,鼠标超过了可视区,如:y坐标(pageX/pageY)=滚动出去的距离+clientY;图标就不会跟鼠标了,而会找clientY/X; document.onmousemove = function (e) { // 谷歌支持,火狐支持 ie8不支持window.enevt.pageX和window.event.pageY这两个属性 imgObj.style.left = e.pageX + "px"; imgObj.style.top = e.pageY + "px"; }; // ie8 支持 function getScroll () { // scroll兼容代码 return { left:window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft||0, top:window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop||0 } } document.onmousemove = function () { imgObj.style.left = window.event.clientX + getScroll().left + "px"; imgObj.style.top = window.event.clientY + getScroll().top + "px"; }; =============================================================================== // 终极兼容版 // 用到了对象的一个方法调用另一个方法 obj = { sayHi:function () { console.log("sayHi方法"); this.eat(); // 调用eat方法 }, eat:function () { console.log("eat方法"); } }; /* * 兼容了那些: * 1、window.event和事件参数对象e的兼容 * 2、 clientX和clientY单独的使用的兼容代码 * 3、scrollleft和scrolltop的兼容代码 * 4、pageX,pageY和clientX+scrollLeft和clientY+scrollTop * 封装在对象中 * */ var evt = { // window.event和e的兼容 getEvent:function (evt) { return window.event || evt; // 相当于var a = 0 || 1; a是1;如果有window.event,就用window.event,没有就用evt, }, // 可视区域横坐标的兼容代码 getClientX:function (evt) { return this.getEvent(evt).clientX; }, // 可视区域纵坐标的兼容代码 getClientY:function (evt) { return this.getEvent(evt).clientY; }, // scrollLeft和scrollTop的兼容代码 /*页面向左卷曲出去的横坐标*/ getScrollLeft:function () { return window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft||0; }, /*页面向上卷曲出去的纵坐标*/ getscrollTop:function () { return window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop||0; }, // 相对于页面的横坐标(pagex或者是clientX+scrollLeft) getPageX:function (evt) { return this.getEvent(evt).pageX?this.getEvent(evt).pageX:this.getScrollLeft()+this.getClientX(evt); }, // 相对于页面的纵坐标(pagey或者是clientY+scrollTop getPageY:function (evt) { return this.getEvent(evt).pageY?this.getEvent(evt).pageY:this.getscrollTop()+this.getClientY(evt); } }; // 测试 var imObj = document.getElementById("im"); document.onmousemove = function (e) { imObj.style.top = evt.getPageY(e) + "px"; imObj.style.left = evt.getPageX(e) + "px"; }; ``` ### (四)查看元素的几何尺寸 `dom元素.getBoundingClientRect()`得到一个对象包,里面有所选择元素的几乎一切信息,和offset系列功能一样,没offset系列好用。 1. 兼容性很好; 2. 该方法返回一个对象,对象里面有left, top, right, bottom等属性。left和top代表该元素左上角的X和Y坐标,right和bottom代表元素右下角的X和Y坐标。 3. height和width属性老版本IE并未实现。 4. 返回的结果不是实时的; ### (五)让滚动条滚动 1. `window.scroll()` 2. `window.scrollTo()` 3. window.scrollBy() 4. 三个方法功能类似,用法都是将x,y坐标传入,即实现让滚动轮滚动到当前位置。 5. 区别:scrollBy()会在之前的数据基础之上做累加 ## 十、闭包 ### (一) [[scope]] 每个javascript的函数都是一个对象,对象中有些属性可以访问,但是有些不可以,这些属性仅供javascript引擎存取,scope就是其中一个,scope就是我们所说的作用域,其中存储了运行期上下文的集合,就是ao和go ### (二) 作用域链 scope中存储的运行期上下文对象的集合,这个集合呈链式链接,把这个链式连接叫做作用域链 ### (三) 运行期上下文 当函数执行时,会创建一个称为执行期上下文的内部对象,(AO/GO),一个执行期上下文定义了一个函数的执行环境,函数执行时,对应的执行上下文都是独一无二的,多次调用函数,会创建多个执行上下文,当函数执行完毕,所产生的执行上下文被销毁 查找变量:从作用域链的顶端依次向下查找 当一个函数a被定义的时候 [[scope]]存储的第0位的是GO 当一个函数a被执行的时候 [[scope]]存储的第0位就变成了AO,第一位就变成了GO。 ![](media/scope1.png) ![](media/scope2.png) ### (四) 闭包 当内部函数被保存到外部时,将会生成闭包,闭包会导致原有作用域不被释放,造成内存泄露 两个函数嵌套,把里面的函数保存到了外部全局,就行成了装饰,它保存着父级函数的作用域AO 给一个标签绑定一个事件,相当于是将事件外理函数保存在了js之外的标签属性上,那么也就行成了闭包,如果正好这个事件外理函数在一个循环的函数中,那么他就会保存着父级函数的AO,AO对象存储着变量的值,这个值会随着循环更新,停在循环最后一个值上,如果想要拿到循环时候的每个值,就需要在事件函数的外面加一个立即执行函数,这样事件函数就会存储立即执行函数的AO对象,而这个AO里的变量属性是每次跟着循环得到的值,这个值是不会变的 ``` function createLi (n) { var divObj = document.createElement("div"); var div = document.body.appendChild(divObj); var ulObj = document.createElement("ul"); div.appendChild(ulObj); for (var i=0;i var name = 'bcd'; var init = (function () { var name = 'abc'; function callName () { console.log(name); } return function () { callName(); } }()) console.log(name); // bcd init() // abc ``` ## 十一、原型 定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先,通过该构造函数产生的对象,可以继承该原型的属性和方法,原型也是对象,没有属性和方法之前就是一个只有constructor属性值是函数本身的一个对象。 + 利用原型的特点和概念可以提取公共属性。 ```javascript ``` + 对象如何查看原型: 隐式属性`__proto__` + 对象如何查看对象的构造函数 constructor + 访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着`__proto__`这条链向上找,这就是原型链。 + 如何区分一个属性到底是基本的还是从原型中找到的呢?——hasOwnProperty,特别是在for…in…循环中,一定要注意 ![原型](media/原型.png) + 原型的修改和删除,不能通过对象.属性的方式修改和删除,必须用`构造函数.prototype.属性`的方式修改,删除也不能使用`delete 对象.属性`的方式删除,要使用`delete 构造函数.prototype.属性`的方式删除。 ```javascript ``` + 另一种写法, 这种方式和函数.prototype.属性 = 值 修改对象,一种是直接修改引用值。 ```javascript 构造函数.prototype = { name="kk", age=18 } ``` + 绝大多数的对象最终都会继承自object.prototype,但是有一个例外,object.create(原型/null),使用null参数创建的对象是个例外。 总结: - 任何函数都具有一个 `prototype` 属性,prototype里有`constructor`属性 - 构造函数的 `prototype` 对象默认都有一个 `constructor` 属性,指向 `prototype` 对象所在函数,可以修改 ```javascript ``` - 通过构造函数得到的实例对象内部会包含一个指向构造函数的 `prototype` 对象的指针 `__proto__` ```javascript ``` - 所有实例都直接或间接继承了原型对象的成员 - Object.create(原型) ```javascript ======================== var demo = { lastName: "小花" }; var obj = Object.create(demo); // 而这个obj对象是个空对象,但是他里面有个属性__proto__值是demo,他自身没有lastName属性的时候,就会去原型上去找。 // obj = {__proto__: demo} ``` - undefined,null没有原型 - call/apply 改变this指向 区别后面传的参数的形式不同。借用别人的函数,实现自己的功能 函数()=函数.call(参数1,参数2...) 参数1,改变this指向的对象,参数2以后正常的函数参数 ```javascript ``` 函数()=函数.apply(参数1,[参数2...]) 参数1,改变this指向的对象,函数的正常参数要放在一个数组中。 ```javascript function Person(name, age) { this.name = name; this.age = age; } obj = {}; Person.apply(obj, ['ww', 18]); console.log(obj); ``` 区别: call:需要把实参按形参的个数传进去 applay:需要传一个arguments 应用: ``` // call、apply function Father (name, age) { this.name = name; this.age = age; } function Son (name, age, phone, email) { Father.call(this, name, age); this.phone = phone; this.email = email; } var son = new Son("小花", 13, 1443322323, "[email protected]") ``` ## 十二、继承 ### 传统形式:原型链继承 过多的继承了没用的属性 ```javascript ``` ### 借用构造函数 不能继承借用构造函数的原型 每次构造函数都要多走一个函数 ```javascript function Person (name, age, sex) { this.name = name; this.age = age; this.sex = sex; } function Student (name, age, sex, grade) { Person.call(this, name, age, sex); this.grade = grade; } var student = new Student('ww', 12, '女', 'bb') ``` ### 共享原型 不能随便改动自己的原型 ```javascript Father.prototype.lastName = '张'; function Father () {} function Son () {} Son.prototype = Father.prototype var son = new Son() ``` 当Son的原型想增加东西的时候,势必造成Father的原型上也增加了东西 ### 圣杯继承 ```javascript // 圣杯模式 普通版 Father.prototype.lastname = "eee"; function Father (name) { this.name = name; } var father = new Father("mjc"); function Son (age) { this.age = age; } function Inherit (Target,Origin) { function F() {}; F.prototype = Target.prototype; Origin.prototype = new F(); // 注意这行不能在F的原型改变之前,一定要在后面。原因是new的时候的原型一定是要改变后的原型 Origin.prototype.constructor = Origin; // 改变原型的constructor指向自身的构造函数,不改变的话,构造函数会指向父类 Origin.prototype.uber = Origin.prototype; // 找到超类,究竟继承自谁,为了信息的一个存储 } Inherit(Father, Son); var son = new Son(18); // 圣杯模式 雅虎版 Father.prototype.lastname = "eee"; function Father (name) { this.name = name; } var father = new Father("mjc"); function Son (age) { this.age = age; } var inherit = (function () { var F = function () {}; // 闭包的属性私有化,不想让外界访问这个属性 return function (Target,Origin) { F.prototype = Target.prototype; Origin.prototype = new F(); Origin.prototype.constructor = Origin; Origin.prototype.uber = Origin.prototype; } }()); inherit(Father, Son); var son = new Son(18); ``` ## 十一、this指向 全局作用域里this指向window call/apply可以改变函数运行时的this指向 ```javascript function test() { console.log(this) } test.call({name:'小花'}); // 相当于是 test() -- > AO { arguments: {}, this: window --> {name:'小花'} } 1、预编译: this --> window 2、谁调用的this 指向谁 var name = 'window'; var obj = { name: 'obj', say: function (){ console.log(this.name) } } obj.say() // obj obj.say.call(window); // window var fun = obj.say fun() // window fun.call(obj) // obj 3、call apply 改变指向 4、全局this --> window ``` 普通函数中的this:window 相当于是window.函数(),(函数预编译过程中,this指向window) 对象方法中的this: 当前的实例对象; 定时器方法中的this:也是window 构造函数中的this:当前的实例对象 原型对象方法中的this:当前的实例对象; jquery为什么能连续的点方法或属性,因为return this。 当new一个函数时,相当于是在函数里创建了一条下面的语句 `var this = Object.create(函数.prototype)`准确的来说是在这个对象里添加了this对象,而这个this对象里添加了`__proto__:函数.prototype`。 ## 十二、try catch 不抛出错误,后续代码继续执行(try里的代码出错的那行以下不会再执行),把错误放到catch的参数e里。 ```javascript try{ console.log('a'); console.log(b); // 出错行 console.log('c'); // 不执行 } catch(e){ console.log(e.name); // 错误名称 console.log(e.message) } console.log('d') // 会执行 ``` e.name 的六种对应的信息: 1、EvalError:eval()的使用与定义不一致; 2、RangeError:数值越界 3、ReferenceError: 非法或者不能识别的引用数值 ,变量、函数 没声明就使用, 4、SyntaxError:发生语法解析错误; 5、TypeError:操作类型错误; 6、uRLError:URL处理函数使用不当; ## 十三、es5严格模式 不使用严格模式的情况下是:基于es3的+es5的新增方法,使用的,出现冲突,以es3的方法为主。 严格模式就是出现冲突的时候以es5的方法为主 "use strict" 写在逻辑的最顶端。全局的。局部的,写在函数代码的最顶端 不再兼容e3的一些不规范的语法,使用全新的es5规范 两种用法: 1、全局严格模式 2、局部函数内严格模式(推荐) 就是一行字符串,不会对不兼容严格模式的浏览器产生影响。 严格模式下,不支持with(改变作用域链,改变AO), arguments.callee, func.caller 变量赋值前必须声明,(隐式全局变量不能用了)局部this必须被赋值(Person.call(null/undefined,赋什么就是什么),拒绝重复属性和参数。 ```javascript var obj = { name: "obj" } var name = "window"; function test() { var age = 123; var name = 'scope'; with (obj){ console.log(name); // obj console.log(age); // 123 } } ================== "use strict" function Test() { console.log(this) // 也就是预编译的时候,不再指向window而是指向了空 } Test() // undefined new Test() // test{} Test.call({}) // {} ``` ## 十三、小技巧工具方法 ### 1、实现jquery连续调用方法 ```javascript ``` ## 2、自定义的typeof ```javascript function myType(target) { var ret = typeof(target); template = { "[object Array]": "arrery", "[object Object]": "object", "[object Number]": "number-object", "[object Boolean]": "boolean-object", "[object String]": "string-object" }; if (target === null) { return 'null'; }else if(ret == 'object') { var str = Object.prototype.toString.call(target); return template[str]; } else { return ret; } } ``` ### 自定义的数组去重方法 ```javascript // 数组的去重,思路:把数组的每一个元素放到对象中去,给一个随意的值,因为对象中的每个属性是不重复的,达到去重的效果 Array.prototype.unique = function () { var temp = {}, arr = [], len = this.length; for (var i=1;i` 2、async w3c标准,异步加载,加载完就行,async只能加载外部脚本,不能把js写在script标签里。 `script type="text/javascript" src="tools.js" aysnc="aysnc">` 3、执行时也不阻塞页面。 4、创建script,插入到dom中,加载完毕后callBack。 ```javascript demo.js function test(){ console.log(a) }

你可能感兴趣的:(javascrip)