Javascript基础

JS

简介

Javascript一种直译式脚本语言,是一种动态类型、弱类型、解释型或即时编译型的编程语言。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。

JavaScript 的标准是 ECMAScript 。截至 2012 年,所有的现代浏览器都完整的支持 ECMAScript 5.1,旧版本的浏览器至少支持 ECMAScript 3 标准。2015年6月17日,ECMA国际组织发布了 ECMAScript 的第六版,该版本正式名称为 ECMAScript 2015,但通常被称为 ECMAScript 6 或者 ES6

Javascript 版本说明

  1. 1998年6月,ECMAScript 2.0版发布。
  2. 1999年12月,ECMAScript 3.0版发布,成为JavaScript的通行标准,得到了广泛支持。
  3. 2007年10月,ECMAScript 4.0版草案发布,对3.0版做了大幅升级,预计次年8月发布正式版本。
  4. 2008年7月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激进,ECMA开会决定,中止ECMAScript 4.0的开发。
  5. 2009年12月,ECMAScript 5.0版正式发布。
  6. 2011年6月,ECMAscript 5.1版发布。
  7. 2013年3月,ECMAScript 6草案冻结,不再添加新功能。新的功能设想将被放到ECMAScript 7。
  8. 2013年12月,ECMAScript 6草案发布。然后是12个月的讨论期,听取各方反馈。
  9. 2015年6月17日,ECMAScript 6发布正式版本,即ECMAScript 2015。

HTML文档引入Javascript

  1. <script> 标签

<script> 标签可以放在 HTML文档的任意位置,为了避免不必要的错误建议放在 </body>结束标签之前。

  1. 引入外部.js文件

javascript 存储到外部文件中时文件后缀为 .js

javascript 调试技巧

第一个javascrit程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<button id='btn'>点我</button>
<div id="box" style="width: 100px; height: 100px; background: orange; display: block;"></div>
<script>
var btn = document.getElementById('btn')
var box = document.getElementById('box')
// 点击按钮显示/隐藏 #box
btn.onclick = function (){
if('block' == box.style.display){
box.style.display = 'none'
}else{
box.style.display = 'block'
}
}
</script>

在编写 javascript 代码时要严格区分字母的大小写,语法规则是否正确,否则程序将不能正确的执行。

标识符与关键字

  • 关键字
  • 保留字

命名规则

在编写代码时会用到一些命名,这些命名应该见名知意,见名知意是为了便于让程序人员阅读代码。
通常情况下,为了便于阅读,在起名时会用于两个及以上的单词作为名称。

在命名时需要注意不要能特殊字符及数字开头,也不要使用有二异性的单词,不要使用中文,长度也要适当不要过长。

  1. 下划线连接:单词与单词之间使用下划线 _ 进行连接,例如:var hello_world = 1

  2. 驼峰规则:单词之间首字母使用大写进行区分。

    • 变量:首单词首字母小写,后面单词首字母大写,例如:var helloWorld = 998
    • 函数:同变量命名规则。
    • 类名:所有单词首字母大写,例如:class HelloWorld {}

语句

语句是JavaScript 语句向浏览器发出的命令。用分号分隔 ;

分号;表示一条语句的结束,在js中分号是可选的,如果一行结尾没有使用分号,那么这一行中只能有一条语句。

注释

  • 多行注释 /* 多行注释 */
  • 单行注释 // 单行注释

注意:单行注释通常写在被注释语句的上方或右侧,而多行注释则写在被注释语句上方。

变量

变量 是存储值的容器。要声明一个变量,使用关键字var

1
var a = 199;

js是动态类型的语言,变量则可以被赋于不同类型的值
ES6引入两个新的关键字定义 let 定义变量,const定义常量。

数据类型

  • String 字符串(一串文本)。字符串的值必须将用引号(单双均可,必须成对)扩起来。
  • Number 数字。无需引号。
  • Boolean 布尔值(真 / 假)。 true/false 是 JS 里的特殊关键字,无需引号。
  • Array 数组,用于在单一引用中存储多个值的结构。
  • Object 对象,JavaScript 里一切皆对象,一切皆可储存在变量里。
  • Undefined 未定义或未赋值
  • Null 对象为空

转义符

使用反斜杠(转义符)来向文本字符串添加特殊字符。

代码 输出
\\' 单引号
\\" 双引号
\\& 和号
\\\ 反斜杠
\n 换行符
\r 回车符
\t 制表符
\b 退格符
\f 换页符

typeof

typeof 操作符返回一个字符串,表示未经计算的操作数的类型。

1
2
3
4
5
6
7
8
9
10
11
12
function fun1(){}

console.log( typeof 1 ) // number
console.log( typeof true ) // boolean
console.log( typeof 'num' ) // string
console.log( typeof null ) //object
console.log( typeof new Array(1) ) // object
console.log( typeof new Object() ) // object
function fun1(){} //定义一个函数
console.log( typeof fun1 ) // function
var num //定义一个变量
console.log( typeof num) // undefined

类型转换

  • 隐式类型转换(自动转换):在表达式中,参与运算的操作数据自动转换为相同类型并进行运算。

    1
    2
    console.log( 5 - '1' ) //结果为数字 4
    console.log( 5 + '1' + 1 + 1 - 1 ) //结果为数字 5110
  • 显式类型转换(强制转换): 在表达式中参与运算的操作数据需要调用方法到对象对数据进行转换类型,转换完成后再进行运算。

    1
    2
    3
    4
    5
    6
    7
    var a = ''
    a.Number() //转数字
    a.Boolean() //转布尔
    a.String() //转字符串
    a.parseInt() //转数字
    a.parseFloat() // 转数字
    a.toString() //转字符串

运算符

  • 算术运算符

    • + - * / % ++ --

    ++ --数据本身会自递增或自递减 1 ,在同一语句中 ++ --在操作数的右侧时,先使用操作数的值再运算,在左侧时先运算再使用操作数中的结果。
    + 两侧的操作数,如果有一方为字符串那么 + 就作为字符串连接符使用

  • 赋值运算符

  • = += -= *= /= %=

  • 比较运算符

    • > < == === >= <= != !==

    使用比较运算的表达式的值的结果为 布尔值

  • 逻辑运算符

    • && || !
  • 位运算符

    • & |
  • 三目运算

    • ? :

运算符优先级

下面的表将所有运算符按照优先级的不同从高(20)到低(1)排列。

参考文档

优先级 运算类型 关联性 运算符
20 圆括号 n/a(不相关) ( … )
19 成员访问 从左到右 … . …
19 需计算的成员访问 从左到右 … [ … ]
19 new (带参数列表) n/a new … ( … )
19 函数调用 从左到右 … ( … )
19 可选链(Optional chaining) 从左到右 ?.
18 new (无参数列表) 从右到左 new …
17 后置递增(运算符在后) n/a … ++
17 后置递减(运算符在后) n/a … —
16 逻辑非 从右到左 ! …
16 按位非 从右到左 ~ …
16 一元加法 从右到左 + …
16 一元减法 从右到左 - …
16 前置递增 从右到左 ++ …
16 前置递减 从右到左 — …
16 typeof 从右到左 typeof …
16 void 从右到左 void …
16 delete 从右到左 delete …
16 await 从右到左 await …
15 从右到左 … ** …
14 乘法 从左到右 … * …
14 除法 从左到右 … / …
14 取模 从左到右 … % …
13 加法 从左到右 … + …
13 减法 从左到右 … - …
12 按位左移 从左到右 … << …
12 按位右移 从左到右 … >> …
12 无符号右移 从左到右 … >>> …
11 小于 从左到右 … < …
11 小于等于 从左到右 … <= …
11 大于 从左到右 … > …
11 大于等于 从左到右 … >= …
11 in 从左到右 … in …
11 instanceof 从左到右 … instanceof …
10 等号 从左到右 … == …
10 非等号 从左到右 … != …
10 全等号 从左到右 … === …
10 非全等号 从左到右 … !== …
9 按位与 从左到右 … & …
8 按位异或 从左到右 … ^ …
7 按位或 从左到右 … ▏ …
6 逻辑与 从左到右 … && …
5 逻辑或 从左到右 … ‖ …
4 条件运算符 从右到左 … ? … : …
3 赋值 从右到左 … = …
3 赋值 从右到左 … += …
3 赋值 从右到左 … -= …
3 赋值 从右到左 … *= …
3 赋值 从右到左 … /= …
3 赋值 从右到左 … %= …
3 赋值 从右到左 … <<= …
3 赋值 从右到左 … >>= …
3 赋值 从右到左 … >>>= …
3 赋值 从右到左 … &= …
3 赋值 从右到左 … ^= …
3 赋值 从右到左 … ▕= …
2 yield 从右到左 yield …
2 yield* 从右到左 yield* …
1 展开运算符 n/a
0 逗号 从左到右 ,

从上表中可以看到运算符非常多并且各运算符优先级并无太多规律。建议在运算时使用 圆括号() 将需要的表达时括起来。这样便于读写。

流程控制

控制程序的执行顺序

条件判断

根据给定的条件决定代码块是否被执行。

  • if

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    if(true){
    //是否要被执行的代码
    }

    if( 2 == 1 ){
    //要执行的代码块
    }else{
    //要执行的代码块
    }

    if( 2 == 1 ){
    //要执行的代码块
    }else if( 2 > 1){
    //要执行的代码块
    }else{
    //要执行的代码块
    }

    if可以配置else ifelse 使用这样可以使用程序在执行时可以有更多的选择。

    if ( … )语句接收一个布尔值或者可以表示变布尔值的表达式。

  • switch

    1
    2
    3
    4
    5
    6
    7
    8
    switch (key) {
    case value:

    break;

    default:
    break;
    }

循环控制

  • while

    1
    2
    while (true) {
    }
  • do/while

    1
    2
    3
    do {

    } while (condition);
  • for

    1
    2
    3
    for (var i = 0; i < 10; i++) {

    }

break、continue

  1. break/continue都作用于循环语句

  2. continue结束本次循环继续下一次循环。

  3. break结束当前离它最近的循环。

  4. break可以在switch中使用。switch不是循环。

数组

数组,数据的聚集,在一段连续的空间里存储多个数据,javascript中的数组不同其它语言,数组的长度以及数组中存储的数据类型是可以改变的。

创建数组

创建数组使用 new Array()构造器或者使用 []

1
2
3
4
5
6
7
8
// 构造一个长度为10的数组
var arr = new Array(10)
// 构造并初始化数组
var arr1 = new Array(1, 2, 3, 4, '小明')
// 创建数组的简便写法
var arr2 = []
// 创建并初始化数组
var arr3 = [1,2,3,4,'99']

数组名称代表的是数组本身,如果使用 console.log(arr) 在控制台打印数组名称,则输出整个数组。

访问数组元素及赋值

  • 数组是一段连续的存储空间,要想访问数组中的数据需要使用数组名称加上数组的下标

  • 下标从 0 开始计数到数组的长度减 1

  • 数组中存储的数据叫作元素。元素可以看作是一个变量,可以存储任意类型的数据。

    1
    2
    3
    var arr = [1,2,3,4,'99']
    // 访问数组中第一个元素
    console.log( arr[0] )

数组中所存储的不同的数据类型,但是为以后期数据的管理建议存储相同类型的数据

  • 元素赋值

    1
    2
    3
    4
    var arr = new Array(10)
    arr[0] = 1
    arr[1] = 5
    arr[2] = 9

数组长度

  • 使用 array.length 属性可以获取当前数组的长度

    1
    2
    var arr = [1,2,3,4,'99']
    console.log( arr.length ) //5
  • 在 javascript 中数组的长度是可以变化的。

    1
    2
    3
    4
    var arr = new Array(10)
    console.log( arr.length ) //10
    arr[11] = 123
    console.log( arr.length ) //12
  • array.length 可以直接赋值。

    1
    2
    3
    4
    5
    var arr = new Array(10)
    console.log( arr.length ) //10
    //重新定义数组的长度
    arr.length = 5
    console.log( arr.length ) //5

注意:修改 length 的值可能会造成数组中数据的丢失。谨慎使用!

遍历数组

  • 使用普通for循环遍历

    1
    2
    3
    4
    var array = [2,3,4,5,6,7,8]
    for (var i = 0; i < array.length; i++) {
    console.log(array[i])
    }
  • for/in 遍历

    1
    2
    3
    for (const key in array) {
    console.log(array[key])
    }

    key中迭代出数组的下标

  • for/of 遍历

    1
    2
    3
    for (const value of array) {
    console.log(value)
    }

    value 迭代出数组中元素值

多维数组

  • 数组中元素如果存储的是具体的数据这类数组称为一维数组。如果数组中元素存储的不是具体数据值而是一个 一维数组 那么我们把这个数组称为 二维数组。同理多维数组就是在一个数组的元素中存储另外一个数组。

  • 创建二维数组:

    1
    2
    3
    4
    5
    6
    // 方式一:
    var array = [[9, 3, 4], [1,3,4]]
    // 方式二:
    var array1 = new Array()
    array1[0] = new Array()
    array1[1] = new Array()
  • 访问二维数组:

    1
    2
    3
    4
    var array = [[9, 3, 4], [1, 3, 4]]
    // 输入二维数组中数据
    console.log(array[0][0]) // 9
    console.log(array[1][0]) // 1

    二维数组在访问数据使用 array[][],左侧第一个 [] 中填入的是下标为第一维度下标, array[0] 时表示的是第二维度中第一个数组的名称。要想访问第二维度中具体元素值就需要在 第二个 [] 填入第二维度的下标。

  • for 循环遍历二维数组

    1
    2
    3
    4
    5
    6
    7
    8
    var array = [[9, 3, 4], [1, 3, 4]]
    // i 控制第一维度下标
    for(var i=0; i<array.length; i++){
    // j 控制第二维度下标
    for(var j=0; j<array[i].length; j++){
    console.log(array[i][j])
    }
    }

函数

函数可以将代码进行封装,以达到复用的效果。函数可以接收调用者输入的数据,并且也可以返回数据给调用者。使用 function 关键字定义函数。

  • 定义函数

    1
    2
    3
    4
    5
    6
    7
    // 定义函数
    function fun(){
    console.log('这是一个函数')
    }
    //调用函数
    fun()
    fun()

带参数的函数

1
2
3
4
5
6
7
// ()中定义调用者要传入的参数, 形参。
function fun(arg1, arg2, arg3) {
console.log(arg1, arg2, arg3)
}
// ()传入参数, 实参。
fun(1, 2, 3) //控制台打印 1 2 3
fun(98, 89, 66) //控制台打印 98 89 66
  • 形参:在定义函数时写在 ( ) 中,形参可以有多个。
  • 实参:调用函数时需要根据形参列表传入对应的参数。

注意:形参与实参需要一一对应。

函数的返回值

使用 return 关键字可以返回数据给调用者,调用带返回值函数时需要接收返回的数据。

1
2
3
4
5
6
function fun(arg1, arg2) {
// 返回数据给调用者
return arg1 + arg2
}
// 接收函数返回值
var result = fun(1,2)

arguments

arguments 是一个对应于传递给函数的参数的类数组对象。

1
2
3
4
5
function fun() {
// arguments对象接收调用函数时传入的参数列表。
console.log(arguments)
}
fun(12,3,3,5,6,7)

递归函数

  1. 程序直接或间接调用自身的编程技巧称为递归,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
  2. 递归有三个阶段 :边界条件、递归前进段和递归返回段。
  3. 当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

递归函数中代码尽可能的短小简练。如果函数过大,那么最好分解该函数再写递归函数。

1
2
3
4
5
6
7
8
var res = jieCheng(5)
// 求阶乘
function jieCheng(num) {
if(1==num){
return 1
}
return num * jieCheng(num -1)
}

闭包

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

1
2
3
4
5
6
7
8
9
10
11
function  out(params) {
let num = params
function inner(){
num++
return num
}
return inner
}
let inn = out(19)
console.log( inn() ) //20
console.log( inn() ) //21

自执行函数

  • 自执行函数也叫作立即调用函数

  • 为了避免解析上的歧义,JS引擎规定,如果function出现在行首,一律解析成语句。立即执行函数需要将函数体放在一个圆括号里

1
2
3
4
5
6
7
(function(){
//code
}())

(function (){
//code
})()

为了避免错误请在 请在自执行函数结尾加 分号 ;

回调函数

把函数当作一个参数传到另外一个函数中,当需要用这个函数是,再回调运行()这个函数.回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。(作为参数传递到另外一个函数中,这个作为参数的函数就是回调函数)

1
2
3
4
5
6
7
8
9
10
11
function add(num1, num2, callback) {
var sum = num1 + num2;
callback(sum);
}

function print(num) {
console.log(num);
}

add(1, 2, print); //3

分析:add(1, 2, print);中,函数print作为一个参数传入到add函数中,但并不是马上起作用,而是var sum = num1 + num2;运行完之后需要打印输出sum的时候才会调用这个函数。(这个作为参数传递到另外一个函数中,这个作为参数的函数就是回调函数.

匿名回调函数:

1
2
3
4
5
6
7
8
function add(num1, num2, callback) {
var sum = num1 + num2;
callback(sum);
}

add(1, 2, function (sum) {
console.log(sum); //=>3
});

回调函数有哪些特点

  • 1.不会立即执行

    回调函数作为参数传递给一个函数的时候,传递的只是函数的定义并不会立即执行。和普通的函数一样,回调函数在调用函数数中也要通过()运算符调用才会执行。

  • 2.回调函数是一个闭包

    回调函数是一个闭包,也就是说它能访问到其外层定义的变量。

  • 3.执行前类型判断

    在执行回调函数前最好确认其是一个函数。

    1
    2
    3
    4
    5
    6
    7
    8
    function add(num1, num2, callback) {
    var sum = num1 + num2;
    //判定callback接收到的数据是一个函数
    if (typeof callback === 'function') {
    //callback是一个函数,才能当回调函数使用
    callback(sum);
    }
    }

    回调函数中this的指向问题

    注意在回调函数调用时this的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
function createData(callback){
callback();
}
var obj ={
data:100,
tool:function(){
createData(function(n){
console.log(this,1111); //window 1111
})
}
}
obj.tool();

分析:this指向是 离它最近的或者嵌套级别的 function/方法的调用者,这里离它最近的function是function(n),会回到上面的callback()中,这时候调用者就不是obj而是window。

解决回调函数this指向的方法1:箭头函数
回调函数(若回调函数是普通函数时)当参数传入另外的函数时,若不知道这个函数内部怎么调用回调函数,就会出现回调函数中的this指向不明确的问题(就比如上面例子中this指向的不是obj而是window)。所以 把箭头函数当回调函数,然后作为参数传入另外的函数中就不会出现this指向不明的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

function createData(callback){
callback();
}
var obj ={
data:100,
tool:function(){
createData((n)=>{
this.data = n;
})
}
}
obj.tool();
console.log(obj.data);

分析:回调函数用箭头函数写之后,this指向很明确,就是 离它最近的或者嵌套级别的 function/方法的调用者,所以这里是 obj

解决回调函数this指向的方法2:var self = this;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createData(callback){
callback(999);
}
var obj ={
data:100,
tool:function(){
var self = this; //这里的this指向obj,然后当一个变量取用
createData(function(n){
self.data = n;
})
}
}
obj.tool();
console.log(obj.data);

为什么要用到回调函数

有一个非常重要的原因 —— JavaScript 是事件驱动的语言。这意味着,JavaScript 不会因为要等待一个响应而停止当前运行,而是在监听其他事件时继续执行。
来看一个基本的例子:

1
2
3
4
5
6
7
8
9
10
function first() {
console.log(1);
}

function second() {
console.log(2);
}

first();
second();

分析:正如你所料,first 函数首先被执行,随后 second 被执行 —— 控制台输出:1 2

但如果函数 first 包含某种不能立即执行的代码会如何呢?例如我们必须发送请求然后等待响应的 API 请求?为了模拟这种状况,我们将使用 setTimeout,它是一个在一段时间之后调用函数的 JavaScript 函数。我们将函数延迟 500 毫秒来模拟一个 API 请求,新代码长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
 function first() {
// 模拟代码延迟
setTimeout(function () { //所以function(){console.log(1)}是回调函数
console.log(1);
}, 500);
}

function second() {
console.log(2);
}

first();
second()

分析:这里 function(){console.log(1)}函数当作一个参数传入setTimeout函数中,因为setTimeout是官方提供得一个函数,里面有很多复杂的业务程序,所以函数 function(){console.log(1)}传入后,不一定马上运行,要setTimeout里面要运行到function(){console.log(1)}时才会运行该函数参数,那是不是整个程序就一直等setTimeout运行?不是的!!!

整个程序运行结果为: 2 1 ,并不是原先的1 2 .即使我们首先调用了 first() 函数,我们记录的输出结果却在 second() 函数之后。

这不是 JavaScript 没有按照我们想要的顺序执行函数的问题,而是 JavaScript 在继续向下执行 second() 之前没有等待 first() 响应的问题。回调正是确保一段代码执行完毕之后再执行另一段代码的方式。

回调函数和异步操作的关系

定义: 回调函数被认为是一种高级函数,一种被作为参数传递给另一个函数的高级函数。回调函数的本质是一种模式(一种解决常见问题的模式),因此回调函数也被称为回调模式。
简而言之: 一个函数在另一个函数中被调用。而且可以当参数传给其他函数。

定时器

通过设置定时器可以让代码延迟或循环执行。

  1. setTimeout() 只执行一次的定时器
  2. setInterval() 循环执行的定时器。

以上两种定时器都会返回一个正整数的定时器ID,这个ID表示定时器的编号。通过这个ID可以删除正在运行中的定时器。

  1. clearTimeout() 删除 setTimeout()对应的定时器,参数为定时器ID
  2. clearInterval() 删除 setInterval()对应的定时器,参数为定时器ID

需要注意的是setTimeout()和setInterval()共用一个编号池,技术上,clearTimeout()和 clearInterval() 可以互换。但是,为了避免混淆,不要混用取消定时函数。

对象

对象是一个复合数据类型,可以根据需要在对象中任意添加成员属性及方法。

创建/访问对象

  • new Object() 创建对象

    1
    2
    3
    4
    5
    6
    7
    8
    // 创建空对象
    var boy = new Object()
    // 添加对象成员到对象
    boy.name = "小明"
    boy.age = 19
    boy.work = function(){
    console.log('学生')
    }
  • 访问对象中属性和方法,

    • 访问对象中属性的方法需要使用到点运算符 .
    1
    2
    3
    boy.name
    boy.age
    boy.work()
  • JSON 格式创建

    1
    2
    3
    4
    5
    6
    7
    var man = {
    name:'小明',
    age:19,
    job:function(){
    console.log('的工作是学生')
    }
    }

面向对象

  • 工厂方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function createPerson(name, sex) {
    //实例化对象
    var person = new Object();
    person.name = name;
    person.age = sex;
    person.doSome = function () {
    console.log(">> " this.name);
    }
    //返回对象给调用者
    return person;
    }
  • 构造函数方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function Person(name, sex) {
    //属性
    this.name = name;
    this.sex = sex;
    //方法
    this.doSome = function () {
    console.log(">> " + this.name);
    }
    }
    var p1 = new Person('小白', 18);

    prototype 属性允许您向类中添加属性和方法
    添加原型属性时通过 类名 方式添加 ClassName.prototype.attrName = value
    对象中有属性和 prototype 中属性名称重名,会优先使用对象中的属性。

[] 数组方式访问对象成员

1
2
var p1 = new Person('小白', 18);
console.log(p1['name'])

delete

  • delete 删除对象成员属性

    1
    2
    // 删除对象中成员属性
    delete p1.name

instanceof

  • instanceof 判断对象是否是某个类的实例

    1
    2
    3
    4
    var p1 = new Person('小白', 18);

    console.log(p1 instanceof Person) // true
    console.log(p1 instanceof String) //false

in

  • in 检查对象中是否有该属性或方法

    如果该属性是通过继承得来的也会输出为true

    1
    console.log('toString' in p1)

hasOwnProperty()

  • hasOwnProperty()方法判断的是对象是否自身拥有的,该方法会忽略掉那些从原型链上继承到的属性。

    1
    2
    console.log(p1.hasOwnProperty('name'))
    console.log(p1.hasOwnProperty('sex'))

遍历对象

  • for/in

    • for/in 迭代出对象中属性名
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var p1 = new Person('小白', 18);

    for (const key in p1) {
    if (p1.hasOwnProperty(key)) {
    const element = p1[key];
    // 输出属性名
    console.log(key)
    // 输出属性值
    console.log(element)

    }
    }

this

  • 在函数中,this 表示全局对象。
  • 在方法中,this 该方法所属的对象。
  • 如果单独使用表示全局对象。
  • 在事件中,this 表示接收事件的元素。
  • setTimeout时this 指向全局对象
  • 使用箭头函数时this可以指向程序的上下文,(就是定义时所在的对象,而不是使用时所在的对象)

改变this指向

  • func.call(thisArg, arg1, arg2, ...)
  • func.apply(thisArg, [argsArray])
  • func.bind(thisArg[, arg1[, arg2[, ...]]])

继承

继承是面向对象三大特征之一:继承的特点就是让子类拥有父类对象的属性和方法,类似人类社会中的儿子具有父亲的特征是一样的。js中要实现两个对象间的父子关系比较麻烦,ES6之前没有继承语法。

  • js实现继承的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    //定义父类
    function Human(name,age){
    this.name = name;
    this.age = age;
    this.run = function(){
    console.log('run...');
    }
    }
    // 在 Human原型链上添加方法
    Human.prototype.say = function(){
    console.log("我的名字叫:"+this.name);
    }
    //定义子类
    function Man(name,age,job){
    // 让父类拥有子类的属性和方法(构造函数伪装)。
    Human.call(this,name,age);
    // 子类自己的属性
    this.job = job;
    }
    //子类继承父类原型中方法和属性(原型链)。
    Man.prototype = Human.prototype;
    //给子类添加方法
    Man.prototype.work = function(){
    console.log(this.job);
    }

    /**
    * 实例化
    */
    var man = new Man('小强',22,'工人');
    man.say(); //父类中定义方法 输出 我的名字叫:小强
    man.run(); //父类中定义方法 输出 run...
    man.work();//子类中定义方法 输出 工人
    • 在子类的构造函数中调用了 Human.call(this,name,sex) ,函数内部时会Human对象设置 this.namethis.age 属性和 this.run() 方法。但是在这里使用了call() 方法,并且第一个参数传入了 this ,我们知道 call() 方法的作用就是更改调用者内部的 this 指向的。Human.call(this,name,sex) 参数中的 this 所代表是的子类Man对象,所以调用调用 Human.call(this,name,sex) 就等同于直接在子类中写入与父类中的相同的属性和方法,这个过程就叫作构造函数的伪装。
    • 调用 Human.call() 可以直接将父类中的属性和方法继承到子类中使用但js原型中的方法并继承过来所以直接 Man.prototype = Human.prototype ;将原型中的属性和方法也赋值到子类中。
    • 在经过构造函数的伪装和原型链操作后就实现js版的继承了。

常用内置对象

标准的内置对象例如 Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String 以及其他对象.

全局属性

  • Infinity 代表正的无穷大的数值。
  • NaN 指示某个值是不是数字值。
  • undefined 指示未定义的值。

全局方法

  • eval() 计算 JavaScript 字符串,并把它作为脚本代码来执行。
  • isNaN() 检查某个值是否是数字。
  • Number() 把对象的值转换为数字。
  • parseFloat() 解析一个字符串并返回一个浮点数。
  • parseInt() 解析一个字符串并返回一个整数。
  • String() 把对象的值转换为字符串。
  • escape() 对字符串进行编码。
  • unescape() 对由 escape() 编码的字符串进行解码。
  • encodeURI() 把字符串编码为 URI。
  • decodeURI() 解码某个编码的 URI。

Array 常用属性及方法

  • array.length 设置或返回数组元素的个数。
  • Array.prototype 允许你向数组对象添加属性或方法。
  • array.indexOf() 搜索数组中的元素,并返回它首次出现的位置。
  • array.lastIndexOf()搜索数组中的元素,并返回它最后出现的位置。
  • array.concat() 连接两个或更多的数组,并返回结果。
  • array.copyWithin() 从数组的指定位置拷贝元素到数组的另一个指定位置中。
  • array.reverse() 反转数组的元素顺序。
  • array.every() 检测数值元素的每个元素是否都符合条件。
  • array.fill() 使用一个固定值来填充数组。
  • array.forEach() 数组每个元素都执行一次回调函数。
  • array.unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
  • array.push() 向数组的末尾添加一个或更多元素,并返回新的长度。
  • array.pop() 删除数组的最后一个元素并返回删除的元素。
  • array.shift() 删除并返回数组的第一个元素。
  • array.slice() 选取数组的的一部分,并返回一个新数组。
  • array.sort() 对数组的元素进行排序。
  • array.splice() 从数组中添加或删除元素。
  • array.toString() 把数组转换为字符串,并返回结果。

String 常用属性及方法

  • str.length 返回字符串的长度
  • String.prototype 允许您向对象添加属性和方法
  • str.charAt() 返回在指定位置的字符。
  • str.concat() 连接两个或更多字符串,并返回新的字符串。
  • str.indexOf() 返回某个指定的字符串值在字符串中首次出现的位置。
  • str.lastIndexOf() 返回某个指定的字符串值在字符串中最后出现的位置。
  • str.match() 查找找到一个或多个正则表达式的匹配。
  • str.replace() 在字符串中查找匹配的子串, 并替换与正则表达式匹配的子串。
  • str.search() 查找与正则表达式相匹配的值。
  • str.slice() 提取字符串的片断,并在新的字符串中返回被提取的部分。
  • str.split() 把字符串分割为字符串数组。
  • str.toLowerCase() 把字符串转换为小写。
  • str.toUpperCase() 把字符串转换为大写。
  • str.trim() 去除字符串两边的空白

Boolean

  • boo.prototype 使您有能力向对象添加属性和方法。
  • boo.toString() 把布尔值转换为字符串,并返回结果。

Number

  • Number.MAX_VALUE 可表示的最大的数。
  • Number.MIN_VALUE 可表示的最小的数。
  • Number.NaN 非数字值。
  • num.prototype 允许您可以向对象添加属性和方法。
  • num.toString() 把数字转换为字符串

Math

  • Math.PI 返回圆周率(约等于3.14159)。
  • Math.abs(x) 返回 x 的绝对值。
  • Math.ceil(x) 对数进行上舍入。
  • Math.floor(x) 对 x 进行下舍入。
  • Math.max(x,y,z,...,n) 返回 x,y,z,…,n 中的最高值。
  • Math.min(x,y,z,...,n) 返回 x,y,z,…,n中的最低值。
  • Math.round(x) 四舍五入。
  • Math.random() 返回 0 ~ 1 之间的随机数。。

Date

  • date.getTime() 返回 1970 年 1 月 1 日至今的毫秒数。
  • date.getFullYear() 以四位数字返回年份。
  • date.getMonth()返回月份 (0 ~ 11)。
  • date.getDate() 返回一个月中的某一天 (1 ~ 31)。
  • date.getDay() 返回一周中的某一天 (0 ~ 6)。
  • date.getHours() 返回小时 (0 ~ 23)。
  • date.getMinutes() 返回分钟 (0 ~ 59)。
  • date.getSeconds() 返回秒数 (0 ~ 59)。
  • date.getMilliseconds() 返回毫秒(0 ~ 999)。
  • date.setFullYear() 设置的年份(四位数字)。
  • date.setMonth() 设置月份 (0 ~ 11)。
  • date.setDate() 设置月的某一天 (1 ~ 31)。
  • date.setHours() 设置小时 (0 ~ 23)。
  • date.setMinutes() 设置分钟 (0 ~ 59)。
  • date.setSeconds() 设置秒钟 (0 ~ 59)。
  • date.setMilliseconds() 设置毫秒 (0 ~ 999)。
  • date.setTime() 以毫秒设置 Date 对象。

JSON

  • JSON.stringify() 将一个对象或者数组转换为一个 JSON 字符串。
  • JSON.parse() 将一个JSON 字符串转换为对象或数组。

深拷贝与浅拷贝

  • 深拷贝:数据 B 复制了数据 A 当修改数据 B 时不影响数据 A,叫作深拷贝。
  • 浅拷贝:数据 B 复制了数据 A 当修改数据 B 时数据A也随着一起变化叫作浅拷贝。
    在数组与对象的使用中会经常出现深浅拷贝的问题。

变量作用域

  • 变量作用域(生命周期)是指变量的有效范围。作用域分为两种,全局作用域和局部作用域。
  1. 全局作用域: 在当前文档中的任何位置都可以使用。
  2. 局部作用域: 只有在当前代码块中可用。
    • 代码块:使用 { } 包裹的代码。

作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链(作用域形成的链条)

  1. 作用域链的前端,始终都是当前执行的代码所在环境的变量对象
  2. 作用域链中的下一个对象来自于外部环境,而在下一个变量对象则来自下一个外部环境,一直到全局执行环境
  3. 全局执行环境的变量对象始终都是作用域链上的最后一个对象
    内部环境可以通过作用域链访问所有外部环境,但外部环境不能访问内部环境的任何变量和函数。
    由于变量的查找是沿着作用域链来实现的,所以也称作用域链为变量查找的机制.这个机制也说明了访问局部变量要比访问全局变量更快

var与let/const

let const ES6声明变量新语法。

  1. var 变量,函数作用域,能重复声明覆盖
  2. let 可以声明局部变量。
  3. let 声明的变量不支持变量提升。
  4. let 声明的变量不允许重复声明。
  5. const 声明常量,常量在声明时必须赋值。一旦赋值不能修改

    const 一般在 require 一个模块的时候用或者定义一些全局常量。而 let 是限制了变量的作用域,保证变量不会去污染全局变量。所以尽量将 var 改为用 let
    const 一旦定义,无法更改,let定义块级变量,他们都是无法重复声明的,会报错,而 var可以覆盖之前的声明
    赋值之后不会再做修改了就用 const,如果后边还会修改就用 let,不建议使用 var

命名空间

命名空间 namespace:解决程序命名冲突(命名空间污染)的问题。

命名空间namespace(某些语言中叫package),是一个在静态语言中常见的概念。它可以帮助我们更好地整理代码,并可避免命名冲突。

1
2
3
4
5
6
7
8
var MYNAMESPACE = {
person: function(name) {
this.name = name;
this.getName = function() {
return this.name;
}
}
};

person对象被完整包含到MYNAMESPACE这个命名空间中了,使用方法也很简单:
1
2
3
var p = new MYNAMESPACE.person("ifcode");

p.getName(); // ifcode

嵌套命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
var MYNAMESPACE = {
PEOPLE: {
person: function(name) {
this.name = name;
this.getName = function() {
return this.name;
}
}
},
PET: {
dog: function(petName) {
this.petName = petName;
this.getPetName = function() {
return this.petName;
}
}
}
};

看起来这个模拟的命名空间不错,但这里还是有一个问题。我们这里使用的是一个全局对象,在添加这个“命名空间”的时候,我们有可能覆盖全局空间中的同名对象。因此我们需要在声明命名空间前进行检查,保证全局空间的安全:

1
var MYNAMESPACE = MYNAMESPACE || {};

若全局空间中已有同名对象,则不覆盖该对象;否则创建一个新的命名空间。采用了这个安全的命名空间后,声明的方法也需要略作改动:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
var MYNAMESPACE = MYNAMESPACE || {};

MYNAMESPACE.person = function(name) {
this.name = name;
};

MYNAMESPACE.person.prototype.getName = function() {
return this.name;
};

// 使用方法
var p = new MYNAMESPACE.person("ifcode");
p.getName(); // ifcode

注意在定义命名空间构造函数时,需要将其定义在prototype上,否则新建的实例无法访问对象的方法。

错误处理

使用try...catch尝试捕获一个错误

1
2
3
4
5
6
7
try {
... //异常的抛出
} catch(e) {
... //异常的捕获与处理
} finally {
... //结束处理
}
  • try 块中内容为可能出现异常的代码。
  • catch:一旦 try 块中内容出现异常在 catch 块中就可以捕获到该异常。如果没有异常则 catch 块中内容不执行。
  • finally:无论是否出现异常 finally 块中代码都会执行

throw

throw 语句用来抛出一个用户自定义的异常。当前函数的执行将被停止(throw之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个catch块。如果调用者函数中没有catch块,程序将会终止。

1
2
3
4
throw "Error2"; // 抛出了一个值为字符串的异常
throw 42; // 抛出了一个值为整数42的异常
throw true; // 抛出了一个值为true的异常
throw new Error('这是一个错误'); //抛出一个错误对象

正则表达式

正则表达式: 正则的作用是用来匹配字符串中是否有符合正则表达式中规则的字符串,当然当我们匹配后就可以对字符串进行检索、替换等一系列操作。正则表达式让操作字符串变的更加简单。

获取正则对象

  • var patt=new RegExp(pattern,modifiers);

  • var patt=/pattern/modifiers;

    • pattern: 正则表达式的文本。

    • modifiers:匹配的模式。

      • i: 忽略大小写。
      • g: 全局匹配;找到所有匹配,而不是在第一个匹配后停止。
      • m: 多行匹配。
  • 正则的行首与行尾

    1
    var patt=/^匹配规则$/

    在写正则规则的时候需要注意,如果使用/匹配规则/的写法时,该规则会检索字符中是否包含该规则所匹配的字符串,简单点说只要有符合该规则的字符就能通过验证。而/^匹配规则$/则需要字符串中所有字符都必须符合该规则。

正则对象中方法:

  • test() 检索给定字符的值是否和正则表达式匹配返回布尔值。
  • exec() 检索字符串中指定的值。返回找到的值,并确定其位置。

String 字符串对支持正则表达式的方法:

  • search() 字符搜索
  • split() 分割字符串操作
  • replace() 替换匹配到的字符串
  • match() 匹配一个或多个规则

正则表达式 规则

  • 元字符:具有特殊含义的字符。大部分元字符前使用转义字符\。
表达式 描述
. 查找单个字符,除了换行和行结束符。
\w 查找单词字符。
\W 查找非单词字符。
\d 查找数字。
\D 查找非数字字符。
\s 查找空白字符。
\S 查找非空白字符。
\b 匹配单词边界。
\B 匹配非单词边界。
\0 查找 NULL 字符。
\n 查找换行符。
\f 查找换页符。
\r 查找回车符。
\t 查找制表符。
\v 查找垂直制表符。
\xxx 查找以八进制数 xxx 规定的字符。
\xdd 查找以十六进制数 dd 规定的字符。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。
  • 量词:用来描述匹配字符的数量。
表达式 描述
n+ 匹配任何包含至少一个 n 的字符串。
n* 匹配任何包含零个或多个 n 的字符串。
n? 匹配任何包含零个或一个 n 的字符串。
n{X} 匹配包含 X 个 n 的序列的字符串。
n{X,} X 是一个正整数。前面的模式 n 连续出现至少 X 次时匹配。
n{X,Y} X 和 Y 为正整数。前面的模式 n 连续出现至少 X 次,至多 Y 次时匹配。
n$ 匹配任何结尾为 n 的字符串。
^n 匹配任何开头为 n 的字符串。
?=n 匹配任何其后紧接指定字符串 n 的字符串。
?!n 匹配任何其后没有紧接指定字符串 n 的字符串。
  • 范围:描述匹配范围内字符。
表达式 描述
[abc] 查找方括号之间的任何字符。
[^abc] 查找任何不在方括号之间的字符。
[0-9] 查找任何从 0 至 9 的数字。
[a-z] 查找任何从小写 a 到小写 z 的字符。
[A-Z] 查找任何从大写 A 到大写 Z 的字符。
[A-z] 查找任何从大写 A 到小写 z 的字符。
`(red\ blue\ green)` 查找任何指定的选项。

正则练习

  • 从一段文本中选出可用的QQ号

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
      var str = "qq号大全,984033324,1452245123-22q2222ddddd,456123";
    // 匹配所有数字
    var re1 = /\d+/g; // /d+ 查找数字 /g全局匹配;
    // 匹配是否是QQ号
    var re2 = /^[1-9]\d{4,10}$/g; // ^[1-9] 查找数字 \d{4,10} 4到9位数 $结尾 /g全局匹配;
    // 挑出所有的数字
    var arrays = str.match(re1);
    // 检验数字是否是QQ号
    for (var i = 0; i < arrays.length; i++) {
    if (null != arrays[i].match(re2)) {
    alert(arrays[i] + "是QQ号");
    } else {
    alert(arrays[i] + "不是QQ号");
    }
    }
    /**
    984033324是QQ号
    1452245123是QQ号
    22不是QQ号
    2222不是QQ号
    456123是QQ号
    **/

  • 验证邮箱

    1
    2
    3
    var re = /^\w+@[a-z0-9]+\.[a-z]{2,4}$/;
    var email = 'li_chao@126.com.122222222225';
    alert(re.test(email));
  • 去除字符串首尾空格

    1
    2
    3
    var str = "    hello javascript  ";
    var re = /^\s+|\s+$/g;
    alert("(" + str.replace(re,'') + ")");
  • 验证是否包含中文

    中文转Unicode

    • 一 \u4e00
    • 龥 \u9fa5
1
2
3
var str='java中国';
var re = /[\u4e00-\u9fa5]/;
alert(re.test(str));
  • 验证字符串中是否包含指定的单词

    1
    2
    3
    4
    var str = 'welcome to,shengzhen!';
    var word = 'to';
    var re = new RegExp("\\b" + word + "\\b",'g');
    alert(re.test(str));

DOM

DOM

DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML文档交互的API。DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分(例如:页面元素、字符串或注释等等)。DOM允许运行在浏览器中的代码访问文件中的节点并与之交互。节点可以被创建,移动或修改。事件监听器可以被添加到节点上并在给定事件发生时触发。

获取HTML元素

  • document.getElementById() 根据元素的 id 属性值获取节点。
  • document.getElementsByClassName() 根据元素的 class 属性值获取节点列表集合。
  • document.getElementsByTagName() 根据元素名获取节点列表集合。
  • document.getElementsByName() 根据元素的 name 属性值获取节点列表集合。
  • document.querySelector(selectors) 返回文档中与指定选择器或选择器组匹配的第一个 html元素Element。 如果找不到匹配项,则返回null。H5新特性。
  • document.querySelectorAll(selectors) 返回与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。返回的对象是 NodeList 。H5新特性。

节点类型

element.nodeType 属性可以获取元素的类型。可用来区分不同类型的节点,比如 元素, 文本 和 注释,只读属性

节点类型 描述
1 元素
2 属性
3 文本
8 注释
9 整个文档(DOM树的根节点)

节点名称

element.nodeName返回当前节点的节点名称,只读属性

获取子节点及兄弟节点方式一

  1. element.childNodes 获取子节点。
  2. element.firstChild 获取第一个子节点。
  3. element.lastChild 获取最后一个子节点。
  4. element.nextSibling 获取当前节点的下一个兄弟节点。
  5. element.previousSibling 获取当前节点的上一个兄弟节点。

注意以上5个属性在标准浏览器会获取空白节点(空白节点其它也是文本节点)而在IE5/6/7/8下则不会获取空白节点,

获取子节点和兄弟节点方式二

  1. element.children 获取子节点。
  2. element.firstElementChild 获取第一个子节点。
  3. element.lastElementChild 获取最后一个子节点。
  4. element.nextElementSibling 获取当前节点的下一个兄弟节点。
  5. element.previousElementSibling 获取当前节点的上一个兄弟节点。

以上5个属性在使用的时候不会获取空白节点。但是要注意除了children支持所有浏览器外其属性是不支持IE5/6/7/8浏览器的。

  • IE5/6/7/8兼容性写法
1
2
3
4
5
6
7
8
9
10
11
12
function firstNode(obj) {
return obj.firstElementChild || obj.firstChild;
}
function lastNode(obj) {
return obj.lastElementChild || obj.lastChild;
}
function nextNode(obj) {
return obj.nextElementSibling || obj.nextSibling;
}
function prevNode(obj) {
return obj.previousElementSibling || obj.previousSibling;
}

父级节点

  • element.parentNode 当前节点的父节点。
  • element.offsetParent 获取当前节点的祖先节点。
    • 标准浏览器在获取祖先节点时有两种情况:1. 如果所有父级节点都没有定位,那么获取到的祖先节点就是。2. 如果父级节点中有定位(固定、相对、绝对),那么获取的的节点就是离他最近的定位节点。

offsetParent 兼容性问题。

  1. 在IE6/7下获取祖先节点时,如果在节点中有触发 haslayout 的css样式,那么 offsetParen 所获取的节点就是该触发offsetParen的节点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <div id="div1" style="position: absolute;">
    <div id="div2" style="width:1px;" >
    <div id="div3"></div>
    </div>
    </div>

    <script>
    var div3 = document.getElementById('div3');

    console.log(div3.offsetParent.id);
    </script>

    上面代码在IE6/7中运行输出结果是div2而在标准及IE高版本浏览器中运行则输出div1。

  2. 子节点中有定位而所有父级节点中没有定位的。那么获取到的祖先节点是 html 。并且不受 haslayout 的影响。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <div id="div1">
    <div id="div2" style="width:1px;" >
    <div id="div3" style="position: absolute;"></div>
    </div>
    </div>
    <script>
    var div3 = document.getElementById('div3');
    console.log(div3.offsetParent.nodeName);
    </script>

    上面代码在IE5/6中输出结果是 html 而在标准及IE高版本中输出 body。

  3. 子节点中有定位,并且父级节点中也有定位的,那么获取到的祖先节点离他最近的有定位样式的节点。同样不受 haslayout 影响。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     <div id="div1" style="position: absolute;">
    <div id="div2" style="width:1px;" >
    <div id="div3" style="position: absolute;"></div>
    </div>
    </div>

    <script>
    var div3 = document.getElementById('div3');
    console.log(div3.offsetParent.nodeName);
    console.log(div3.offsetParent.id);
    </script>

    上面代码在IE5/6中输出结果是 div1。同样在标准浏览器下也是输出 div1

元素操作

  • document.createElement() 创建元素节点。
  • document.createTextNode() 创建文本节点。
  • document.createAttribute() 创建一个属性节点
  • element.appendChild() 为元素添加一个新的子元素
  • element.insertBefore() 现有的子元素之前插入一个新的子元素
  • element.replaceChild() 替换一个子元素
  • element.removeChild() 删除一个子元素
  • element.innerHTML 设置或者返回元素的内容。
  • element.innerText 设置或者返回元素的内容。
  • element.setAttribute() 把指定属性设置或更改为指定值。
  • element.removeAttribute() 从元素中删除指定的属性
  • element.className 设置或返回元素的 class 属性。
  • element.id 设置或返回元素的 id。
  • element.style 设置或返回元素的 style 属性。
  • element.title 设置或返回元素的 title 属性。
  • element.tagName 返回元素的标签名。

元素克隆

  • element.cloneNode() 克隆元素。
    • 接收一个布尔值的参数默认为false:如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身。

批量添加元素

  • 普通添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!-- 为ul添加子元素 -->
    <ul id="box"></ul>
    <script>
    var box = document.getElementById("box");
    for(var i=1;i<=100;i ){
    var li = document.createElement("li");
    li.innerHTML = i;
    box.appendChild(li);
    }
    </script>

    频繁操作DOM树,效率低。

  • 使用文档碎片方式添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var box = document.getElementById("box"); 
    // 创建文档碎片
    var fragment = document.createDocumentFragment();
    // 将新元素添加到文档碎片中
    for(var i=1;i<=100;i ){
    var li = document.createElement("li");
    li.innerHTML = i;
    fragment.appendChild(li);
    }
    //将文档碎片添加到 ul 元素。
    box.appendChild(fragment);

    由于文档碎片 Fragment 的原因将创建的元素一次生添加到DOM树,效率更高。

事件

鼠标/键盘事件

  1. 常用键盘事件:onkeydown键盘按下、onkeyup键盘松开,onkeypress键盘按下松开时(类似数字电路中的上升沿触发或下降沿触发)。
  2. 常用的鼠标事件:onclick单击、ondblclick双击、onmousedown鼠标按下、onmouseup鼠标松开、oncontextmenu鼠标右键、onmousemove鼠标移动等…。

事件对象event

event对象:连接事件与事件细节的桥梁。其实就是鼠标事件中鼠标在屏幕的哪个位置触发的该事件,键盘事件中你按下的是哪个按钮触发的该事件等这些事件的细节。

获取 event 对象两种方法:

  1. window.event (firefox 低版本不支持)

    1
    2
    3
    document.onclick = function () {
    console.log(window.event);
    }
  2. 事件函数中的第一个参数(IE 5/6/7/8不支持)。

    1
    2
    3
    document.onclick = function (event) {
    console.log(event);
    }
  • event 兼容性写法

    1
    2
    3
    4
    document.onclick = function (event) {
    event = event || window.event;
    console.log(event);
    }

鼠标事件event对象中常用属性。

  • event.clientX :事件在触发时鼠标所在的X轴位置,需要注意这个位置是相对于整个窗口可视区域的,窗口可视区域最左侧为0坐标。

  • event.clientY :事件在触发时鼠标所在的Y轴位置

  • event.x / event.y :作用和 event.clientX | event.clientY 相同,但需要注意在IE5/6/7时event.x | y要比 event.clientX | clientY 少两像素。

  • event.pageX :鼠标相对于整个页面窗口的X轴位置。

  • event.pageY :鼠标相对于整个页面窗口的Y轴位置。

    event.pageX/event.pageY:IE5/6/7/8不支持 。

  • event.target:事件源,(触发的事件的元素)IE5/6/7/8不支持该属性。

  • event.srcElement:事件源,IE5/6/7/8支持该属性。

键盘事件中的 event 对象

事件对象event.keyCode 可以获取按下的按键是什么,注意event.keyCode返回的是一个数字。键盘上的每一个按键就有一个独一无二的数字编码。 键盘的CTRL、SHIFT、ALT,在event事件对象中分别对应event.ctrlKeyevent.shiftKeyevent.altKey,如果对应的按键按下该属性返回true否则返回false。当然也可以通过event.keyCode获取它们的数字编码。

阻止事件的默认行为

部分事件中会有默认的行为,这些并不是我们需要的,比如上下文菜单(oncontextmenu),表单提交(onsubmit),如果要阻止这些默认行为可以使用 event.preventDefault();

1
2
3
4
5
6
7
8
9
10
11
 document.querySelector('body')
.oncontextmenu = function (ev) {
// 阻止默认事件
ev.preventDefault()
}

document.querySelector('.testForm')
.onsubmit = function (ev) {
// 阻止表单默认事件
ev.preventDefault()
}

event.preventDefault(); 不支持IE浏览器 ,IE使用 return false;

绑定事件

绑定事件的特点可以让一个元素同时拥有相同事件类型的事件两个及以上事件,而普通事件添加方式是只能拥有一个相同的类型的事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
//添加普通事件
box1.onclick = aa;
box1.onclick = bb;
//以绑定事件的方式添加
box2.attachEvent('onclick',aa);
box2.attachEvent('onclick',bb);
function aa(){
alert('aaa');
}
function bb(){
alert('bb');
}

上面示例在 IE5/6/7/8/9/10 下运行。

绑定事件有两种方式:

  1. attachEvent(eventName,callback) : 支持 IE5/6/7/8/9/10。为一个元素绑定事件。

    • eventName 第一个参数是事件名称。
    • callback 第二个参数为事件回调函数。

    注意该方法不支持标准浏览器和IE11.

  2. addEventListener(type, listener, options): 支持标准浏览器和IE9/10/11,

    • type 参数一:事件名称,这里需要注意正常情况下JS中的事件就是以on开头的,如onclick、ondblclick,但在这个方法中要写成click或dblclick去掉on。
    • listener 参数二:事件回调函数。
    • options 参数三:boolean值,为true时捕获,false时冒泡。
  • 绑定事件兼容性写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /*
    * 参数1:要绑定事件的元素。
    * 参数2:要绑定的事件名称。
    * 参数3:要绑定事件回调。
    */
    function bindEvent(obj,eventName,callback){
    if(obj.addEventListener){
    obj.addEventListener(eventName,callback,false);
    }else{
    obj.attachEvent("on"+eventName,callback);
    }
    }

    注:attachEvent()绑定事件中如果内部使用了this指针那么需要注意this的指向问题,在IE5/6/7/8/9/10中this并不是指定元素本身,而是指向了window对象。

取消普通事件和绑定事件

  • 取消普通事件:xx.onclick = null;
  • IE5-10使用attachEvent()绑定事件那么就要使用 detachEvent()取消事件绑定。
  • 标准浏览器使用addEventListener()绑定事件,使用removeEventListener()取消事件绑定。
1
2
3
4
5
6
7
8
// 兼容性写法:
function delEvent(obj,eventName,callback){
if(obj.removeEventListener){
obj.removeEventListener(eventName,callback,false);
}else{
obj.detachEvent('on'+eventName,callback);
}
}

事件冒泡与事件捕获

事件流是js中事件的一种现象。事件流有两种:事件冒泡和事件捕获。

  • 事件冒泡是从内向外传递将事件层层传递,而捕获是从外向内传递事件。捕获与冒泡不能同时存在,只能二先一。IE5/6/7/8不支持事件捕获。普通事件只支持冒泡(如:onclickondblclick…)只有绑定事件才有捕获现象。
  • 阻止事件冒泡有两种方法
    • event.cancelBubble = true; 支持IE
    • event.stopPropagation(); IE5/6/7/8不支持这种写法

事件委托

事件委托:可以让后续添加的元素直接拥有事件的形为,也就是直接就拥有事件,而不需要再单独指定事件。简单来说,就是我们在父级元素上添加了事件,那么后续给在这个父级元素上添加的所有子级元素都会拥有这个事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<ul id="mul">
<li>苹果</li>
<li>香蕉</li>
<li>草莓</li>
<li>樱桃</li>
</ul>
<script>
var mUl = document.getElementById("mul");
var lis = document.getElementsByTagName("li");

mUl.onclick = function(ev){
var event = ev ||window.event;
//获取事件源
var target = event.target || event.srcElement;
// 必须是由li触发
if("li" == target.nodeName.toLowerCase()){
alert(target.innerHTML);
}
}
</script>

事件委托其实就是利用事件冒泡现象实现的,这样作有两个好出,

  1. 效率高:如果有大量的元素需要添加相同的事件时不必为每一个元素单独添加事件。
  2. 后续加入的元素也可以直接拥有事件,不需要再为新元素添加事件。而普通事件就不可以,必须为新元素再次指定事件。
  1. 要注意事件委托利用的是冒泡原理,那么就需要该事件必须支持冒泡。
  2. 由于使用事件委托时其所有的子元素及其本身都可以触发该事件方法,所在必须检查触发元素是否是想要的事件元素。

元素距离及可视距离的计算

元素距离

元素到浏览器窗口左侧、顶部的距离的计算方法及兼容性问题

  • element.offsetLeft 计算元素到左侧的距离,需要注意的是 offsetLeftoffsetParen 一样会受到父级节点中定位的影响,如果父级中有定位的,那么获取的距离是该元素到定位元素的距离。如果父级中没有定位样式,那么获取到的距离是该元素到 HTML 元素的距离。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style>
*{margin: 0;padding: 0;}
#div1,#div2,#div3{padding: 50px;}
#div1{background: red;}
#div2{background: green;}
#div3{background: blue;}
body{padding: 20px;}
html{padding: 10px;}
</style>

<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>

<script>
var div3 = document.getElementById('div3');
console.log(div3.offsetLeft);
</script>

在上面代码放在标标准浏览器及IE高版本浏览器中运行输出结果为: 130(div2 50 div1 50 body 20  html 10)。 而在 IE5/6/7 中运行结果为:120(div2 50 div1 50 body 20)。标准浏览器及IE高版本中父级无定位计算的距离到 html 标签,IE5/6/7父级无定位计算的距离到 body 标签。

  • offsetLeft 在IE5/6/7中还需要注意以下几个兼容性问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <style>
    *{margin: 0;padding: 0;}
    #div1,#div2,#div3{padding: 50px;}
    #div1{background: red;}
    #div2{background: green;}
    #div3{background: blue;}
    body{padding: 20px;}
    html{padding: 10px; }
    </style>

    <div id="div1" style="position: absolute">
    <div id="div2">
    <div id="div3"></div>
    </div>
    </div>

    <script>
    var div3 = document.getElementById('div3');
    console.log(div3.offsetLeft);
    </script>
    1. 当前元素没有定位,父级元素 #div2 定位样式值为 absolute 在IE5/6/7中输出结果:100

    2. 当前元素没有定位,父级元素 #div2 定位样式值修改为 relative 在IE5/6/7中输出结果:120。很显然计算的距离到 body 标签。

    3. 在父级元素中加入可以触发 haslayout 的样式,修改如下:

      1
      2
      3
      4
      5
      <div id="div1" style="position: relative" >
      <div id="div2" style="zoom:1" >
      <div id="div3"></div>
      </div>
      </div>

      IE5/6/7中输出结果:50。需要注意在 IE 低版本时计算距离会受到 haslayout 的影响。

    4. 当前元素中加入定位样式,修改如下:

      1
      2
      3
      4
      5
      <div id="div1" style="position: relative" >
      <div id="div2" style="zoom:1" >
      <div id="div3"style="position: relative"></div>
      </div>
      </div>

      IE5/6/7中输出结果:120。注意元素 #div1 使用的是相对定位 relative 。如果换成 absolute 绝对定位,那么输出结果:100#div2 中触发 haslayout 的样式并没有影响到距离的计算。

    5. 当前元素中加入定位样式,父级元素中去掉定位及触发 haslayout 的样式修改如下:

      1
      2
      3
      4
      5
      <div id="div1"  >
      <div id="div2" >
      <div id="div3"style="position: relative"></div>
      </div>
      </div>

      IE5/6/7中输出结果:130#div3 定位值修改为 absolute 时输出结果依然是 130,并没有受影响。

    6. 当前元素加入定位样式,父级元素中加入定位

      1
      2
      3
      4
      5
      <div id="div1" style="position: relative" >
      <div id="div2" >
      <div id="div3"style="position: absolute"></div>
      </div>
      </div>

      IE5/6/7中输出结果:120#div3 定位值修改为 relative 输出结果为 100

    注意,offsetLeft/offsetTop 在 IE6/7 低版本有很大的兼容性问题,使用时需要注意。

  • element.offsetTop 计算当前元素到顶部的距离。兼容性问题和 offsetLeft 相同。

  • 获取元素到左侧和顶部的距离方法封装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function getLeft(el) {
    let result = 0; //记录距离
    while (el) {
    result += el.offsetLeft //不包含左边框的距离
    result += el.clientLeft //左边框 宽度
    el = el.offsetParent;
    }
    return result;
    }

    function getTop(el) {
    var result = 0;
    while (el) {
    result += el.offsetTop //不包含上边框的距离
    result += el.clientTop // 上边框宽度
    el = el.offsetParent;
    }

    return result;
    }

    以上两个方法在可以解决大部分浏览器中获取元素到左侧或顶部的距离。但是在 IE6/7 自身有定位,父级元素中有定位并且定位值 relative 那么就会多加一个定位元素到 body 的距离。

  • 获到元素距离优化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    Element.prototype.getLeft = function(){
    let result = 0; //记录距离
    let el = this
    while (el) {
    result += el.offsetLeft //不包含左边框的距离
    result += el.clientLeft //左边框 宽度
    el = el.offsetParent;
    }
    return result;
    }

    Element.prototype.getTop = function(){
    let result = 0;
    let el = this
    while (el) {
    result += el.offsetTop //不包含上边框的距离
    result += el.clientTop // 上边框宽度
    el = el.offsetParent;
    }

    return result;
    }

可视区

可视区顾名思义就是可以看到的区域

  • Element.getBoundingClientRect() 获取元素的可视区对象 ClientRectClientRect 对象中有以下几个属性
    • top 元素顶部到可视区顶部的距离。
    • bottom 元素底部到可视区顶部的距离。
    • left 元素左侧到可视区左侧的距离。
    • right 元素右侧到可视区左侧的距离。
    • height 元素的高。
    • width 元素的宽。

滚动距离

window窗口在向上滚动时被遮挡部分离可视窗口的距离。

  • window.pageYOffset 获取或设置窗口的垂直滚动距离,不支持IE6/7/8。
  • window.pageXOffset 获取或设置窗口的水平滚动距离,不支持IE6/7/8。
  • document.documentElement.scrollTop 获取窗口的垂直滚动距离,支持IE及 firefox 火狐浏览器。
  • document.documentElement.scrollLeft 获取窗口的水平滚动距离,支持IE及 firefox 火狐浏览器。
  • document.body.scrollLeft 设置或获取窗口的水平滚动距离,支持 chrome 谷歌 opera 欧朋浏览器。
  • document.body.scrollTop 设置或获取窗口的垂直滚动距离,支持 chrome 谷歌 opera 欧朋浏览器。

获取滚动距离兼容性写法:

1
2
3
4
5
6
function getScrollY(){
return window.pageYOffset || document.documentElement.scrollTop;
}
function getScrollX(){
return window.pageXOffset || document.documentElement.scrollLeft;
}

设置滚动距离兼容性写法::

1
2
3
4
5
6
function setScrollX(num){
document.documentElement.scrollLeft = document.body.scrollLeft = num;
}
function setScrollY(num){
document.documentElement.scrollTop = document.body.scrollTop = num;
}

scrollTop,scrollLeft不但可以获取整个页面的滚动距离,也可以获取元素的滚动距离。

使用window.scroll 滚动到指定位置

1
2
3
4
5
window.scroll({
top: 0,
left: 0,
behavior: 'smooth' //平滑过渡
});

Cookie 并不是它的原意“甜饼”的意思, 而是一个保存在客户机中的简单的文本文件, 是一段不超过4KB的小型文本数据,由一个名称(Name)、一个值(Value)和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。不同的域名下会保存不同的cookie,并且互相不同访问。cookie会随请求往返于服务器与客户端,两者都可以操作cookie.

  • document.cookie 设置或返回与当前文档有关的所有 cookie。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* 设置cookie
* @param {String} name cookie名称
* @param {String} value cookie值
* @param {Number} time 过期时间 分钟
*/
function setCookie(name, value, minute){
let va = escape(value)
let data = new Date()
if (minute) {
data.setTime(data.getTime() + 1000 * 60 * minute)
document.cookie = `${name}=${va};expires=${data.toUTCString()}`
} else {
document.cookie = `${name}=${va}` //当前会话关闭就超时
}
}

/**
* 删除指定cookie
* @param {String} name 要删除的cookie名称
*/
function clearCookieByName(name){
setCookie(name, '', -1)
}


/**
* 获取所有cookie
* return 包含当前cookie值的对象
*/
function getCookieAll(){
let coo = document.cookie
if (coo == '') {
return null
}
//清除空格
coo = coo.replace(/\s+/g, '')
let cArr = coo.split(';')
let cookies = {}
cArr.forEach((coo) => {
let c = coo.split('=')
cookies[c[0]] = unescape(c[1])
})
return cookies
}

/**
* 获取指定名称的cookie
* 参数name: cookie名称
* return :如果有返回cookie值,否则返回为null
*/
function getCookieByName(name){
let cs = getCookieAll()

if(cs.hasOwnProperty(name)){
return cs[name]
}else {
return null
}
}

BOM

BOM

浏览器对象模型(Browser Object Model (BOM))尚无正式标准。由于现代浏览器已经(几乎)实现了 JavaScript 交互性方面的相同方法和属性,因此常被认为是 BOM 的方法和属性。

Window

BOM 中最顶层对象是 window 对象。它表示浏览器窗口。

  • window.open() 打开新窗口
  • window.close() 关闭当前窗口
  • window.innerHeight 返回窗口的文档显示区的高度。
  • window.innerWidth 返回窗口的文档显示区的宽度。

BOM中常用对象

  • document DOM的顶层对象。
  • navigator 有关浏览器的信息

    • navigator.appCodeName 返回浏览器的代码名
    • navigator.appName 返回浏览器的名称
    • navigator.appVersion 返回浏览器的平台和版本信息
    • navigator.cookieEnabled 返回浏览器是否启用 cookie,true:启用,false未启用。
    • navigator.platform 返回运行浏览器的操作系统平台
    • navigator.userAgent 返回由客户机发送服务器的user-agent 头部的值
  • screen 客户端显示屏幕的信息

    • screen.availHeight 返回屏幕的高度(不包括Windows任务栏)
    • screen.availWidth 返回屏幕的宽度(不包括Windows任务栏)
    • screen.height 返回屏幕的总高度
    • screen.width 返回屏幕的总宽度
    • screen.pixelDepth 返回屏幕的颜色分辨率(每象素的位数)
  • history 用户(在浏览器窗口中)访问过的 URL

    • history.back() 加载 history 列表中的前一个 URL
    • history.forward() 加载 history 列表中的下一个 URL
    • history.go(num) 加载 history 列表中的某个具体页面 ,num可以是负数。
  • location 当前 URL 的信息。

    • location.hash 返回一个URL的锚部分
    • location.host 返回一个URL的主机名和端口
    • location.hostname 返回URL的主机名
    • location.href 返回完整的URL
    • location.pathname 返回的URL路径名。
    • location.port 返回一个URL服务器使用的端口号
    • location.protocol 返回一个URL协议
    • location.search 返回一个URL的查询部分
    • location.assign() 载入一个新的文档
    • location.reload() 重新载入当前文档
    • location.replace() 用新的文档替换当前文档

URL

URL(Uniform Resource Locator,统一资源定位符),在WWW上,每一信息资源都有统一的且在网上唯一的地址。

  • URL由三部分组成:资源类型、存放资源的主机域名、资源文件名。
  • 也可认为由4部分组成:协议、主机、端口、路径
  • URL的一般语法格式为:带方括号[]的为可选项)
  • protocol :// hostname[:port] / path / [;parameters][?query]#fragment*