1. DOM(文档对象)

获取元素

根据ID获取:
document.getElementById(id)                # id 为大小写严格区分的字符串
注:因为文档页面从上往下加载,所以先得有标签,所以 script 写到有 id标签的下面

根据标签名获取:
document.getElementsByTagName('标签名')           # 返回的是元素对象的集合,以伪数组的形式存储
这样获取的是整个页面的该标签元素,也可以获取某个元素内部所指定标签的子元素,见下
element.getElementsByTagName('标签名')              # element 是父元素

注:因为返回元素以伪数组的形式存储,使用时也应是具体数组元素值: ol[0].getElementsByTagName('li')

H5新增的方法获取:
document.getElementsByClassName('类名')         # 根据类名返回元素对象集合
document.querySelector('选择器')                       # 根据指定选择器返回第一个元素对象(‘.box’,’#nav’,’li’)
document.querySelectorAll('选择器')                 # 返回指定选择器的所有元素对象集合(多了个All嘛)

获取特殊标签:
body:  document.body;
html:   document.documentElement;

事件基础

事件源 事件类型 事件处理程序

1
2
3
4
// 获取事件源
var div = document.getElementsByTagName('div');
// 事件类型及处理程序
div.onclick = function () {alert('你好!');}

常见事件类型

.onclick                  鼠标点击触发

.onmouseover          鼠标经过触发

.onmouseout            鼠标离开触发

.onfocus                  获得鼠标焦点触发

.onblur                   失去鼠标焦点触发

.onmousemove          鼠标移动触发

.onmouseup              鼠标弹起触发

.onmousedown          鼠标按下触发

操作元素

修改元素内容:
element.innerText             从起始位置到终止位置的内容,不识别html标签,同时空格和换行也会去掉
element.innerHTML             从起始位置到终止位置的全部内容,识别html标签,同时保留空格和换行

样式属性操作:
element.style                  行内样式操作
element.className          类名样式操作 (换一个类名指定)

注:JS里的样式均采用驼峰命名法,如:div.style.backgroundColor = 'pink';

获取属性值:
element.属性                                      获取内置属性
element.getAttribute(‘属性’);             主要获取自定义的属性

设置属性值:
element.属性 = ‘值’
element.setAttribute(‘属性’, ‘值’);
element.removeAttribute(‘属性’)         移除属性

H5自定义属性:
以 data- 开头             # 更容易区分自定义属性和内置属性
新增获取属性值: element.dataset.属性 / element.dataset['属性']

注:dataset 是一个集合 里面存放了所有以 data开头的自定义属性如果自定义属性里面有多个 - 连接的单词,获取时采用 驼峰命名法 如:class="data-list-name"   element.dataset.listName;

节点操作

概述:
节点至少拥有 nodeType(节点名称)、nodeName(节点类型)、nodeValue(节点值)这三个基本属性
元素节点 / 属性节点 / 文本节点(包含文字、空格、换行) nodeType 反别为 1 / 2 / 3

node.parentNode                    父节点        # 得到的是离元素最近的父级节点。若没有,则返回 null
parentNode.childNodes        子节点        # 返回包含指定节点的子节点的集合,子节点还包含 元素节点、文本节点等

来个栗子

1
2
3
4
5
6
7
// 返回值是所有子节点的集合,若只想获得里面的元素节点需另处理
var ul = document.querySelector('ul');
for (var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType == 1) {
console.log(ul.childNodes[i]);
}
}

parentNode.children                          子节点          # 直接获取所有的子元素节点

parentNode.firstChild                       第一个子节点 (包括文本节点)

parentNode.lastChild                         最后一个子节点

parentNode.firstElementChild          第一个子元素节点 (不包括文本节点)

parentNode.lastElementChild            最后一个子元素节点

但这又有兼容性的问题,实际中常用:

1
2
3
4
// 第一个子元素节点
console.log(ol.children[0]);
// 最后一个子元素节点
console.log(ol.children.length - 1);

node.nextSibling                                        下一个兄弟节点 (包括文本节点等)

node.previousSibling                                上一个兄弟节点

node.nextElementSibling                          下一个兄弟元素节点

node.previousElementSibling                  上一个兄弟元素节点

document.createElement('tagName')      创建元素节点

document.write('<div>123</div>')         创建元素,如果页面文档加载完毕再调用这句话会导致页面重绘

node.appendChild(child)                          添加节点 (末尾添加)

node.insertBefore(child, 指定元素)      添加节点 (添加到父节点的指定子节点前面)

实栗

1
2
3
4
5
// 创建元素节点
var li = document.createElement('li');
// 末尾添加节点,node 是父级
var ul = document.querySelector('ul');
ul.appendChild(li);

node.removeChild(child)        删除节点 (返回删除的节点)

node.cloneNode(true)              复制节点 (注:括号为空或false为浅拷贝,只复制标签)

事件高级

注册事件
前有多个button标签省略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var btns = document.querySelectorAll('button');
// 1. 传统方式注册事件
btns[0].onclick = function() {
alert('hi');
}

// 2. 事件侦听注册事件 addEventListener
// (1) 里面的事件类型是字符串 必定加引号 而且不带on
// (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
btns[1].addEventListener('click', function() {
alert(22);
})

// 3. attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function() {
alert(11);
}

删除事件
来个栗子吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var divs = document.querySelectorAll('div');
divs[0].onclick = function() {
alert(11);
// 1. 传统方式删除事件
divs[0].onclick = null;
}

// 2. removeEventListener 删除事件 先注册一个事件
divs[1].addEventListener('click', fn) // 里面的fn 不需要调用加小括
function fn() {
alert(22);
// 点击后,事件被删除,即 事件只能触发一次
divs[1].removeEventListener('click', fn);
}

// 3. detachEvent (ie)
divs[2].attachEvent('onclick', fn1)
function fn1() {
alert(33);
divs[2].detachEvent('onclick', fn1);

DOM 事件流
捕获阶段 —> 当前目标阶段 —> 冒泡阶段

  1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
  2. onclick 和 attachEvent(ie) 只能得到冒泡阶段。
  3. 捕获阶段: 如果addEventListener 第三个参数是 true 那么则处于捕获阶段 document -> html -> body -> father -> son
1
2
3
4
5
6
7
8
9
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, true);

var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, true);
  1. 冒泡阶段: 如果addEventListener 第三个参数是 false 或者 省略 那么则处于冒泡阶段 son -> father ->body -> html -> document
1
2
3
4
5
6
7
8
9
10
11
12
13
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, false);

var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);

document.addEventListener('click', function() {
alert('document');
})

事件对象 (event、evt、e)
e.target 返回的是触发事件的对象(元素); this 返回的是绑定事件的对象(元素)
区别 : e.target 点击了那个元素,就返回那个元素; this 哪个元素绑定了这个点击事件,那么就返回谁

(了解) 跟 this 有个非常相似的属性 currentTarget                ie678不认识

e.type           返回事件类型

e.preventDefault()                阻止默认事件 (让链接不跳转 或者让提交按钮不提交)

1
2
3
4
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 标准写法
})

利用return false 也能阻止默认行为 没有兼容性问题       特点: return 后面的代码不执行了, 而且只限于传统的注册方式

e.stopPropagation()                      阻止冒泡

e.cancelBubble = true;                非标准 cancel 取消 bubble 泡泡

事件委托
事件委托的核心原理:给父节点添加侦听器,利用事件冒泡影响每一个子节点
作用:不是给每个子节点单独设置事件监听器,提高程序的性能

常用鼠标事件(补充)
contextmenu            可以禁用右键菜单

1
2
3
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
})

selectstart            可禁止选中文字

1
2
3
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
e.clientX/Y 鼠标在可视区的x和y坐标
e.pageX/Y 鼠标在页面文档的x和y坐标
e.screenX/Y 鼠标在电脑屏幕的x和y坐标

常用键盘事件
onkeyup                  某个键盘按键松开时触发

1
2
3
4
5
6
document.onkeyup = function() {
console.log('我弹起了');
}
document.addEventListener('keyup', function() {
console.log('我弹起了');
})

onkeydown              某个键盘按键被按下时触发
onkeypress            某个键盘按键被按下时触发,但不识别功能键

三个事件的执行顺序 keydown – keypress – keyup

keyCode属性可以得到相应键的ASCII码值

keyup 和keydown事件不区分字母大小写 a 和 A 得到的都是65 (即都是大写)
keypress 事件 区分字母大小写 a 97 和 A 得到的是65

1
2
3
4
5
6
document.addEventListener('keyup', function(e) {
console.log('up:' + e.keyCode);
})
document.addEventListener('keypress', function(e) {
console.log('press:' + e.keyCode);
})

2. BOM (浏览器对象)

小知识:
同步:每一个任务结束之后再执行下一个
异步:可以同时进行多个任务

函数执行为异步,先同步后异步

常见事件

  • 窗口加载事件
    window.onload                当文档内容全加载完成时触发该事件,在 addEventListener 里没有 on 哦
    这样js代码就可以写在任意地方了,不必在请求元素下面,但该方式只能写一次,如果使用 addEventListener 则没有限制
    document.addEventListener('DOMContentLoaded', function(){})            DOM 加载完毕,不包含图片 falsh css 等就可以执行 加载速度比 load更快一些

  • 调整窗口大小事件
    window.onresize = function(){}            当窗口大小发生变化时触发该事件
    window.innerWidth                当前屏幕的宽度

  • 定时器 (demo-14)
    window.setTimeout(调用函数, 延时时间)                延时时间单位是毫秒 但是可以省略,如果省略默认的是0
    window.clearTimeout(timeoutID)                          停止定时器            # timeoutID 为计时器的名字
    window.setInterval(调用函数, 延时时间)              每隔这个延时时间,就调用这个函数,其它特性与 setTimeout 相同
    window.clearInterval(timeID)                                停止定时器

  • location 对象
    属性
    location.href            获取或设置整个 URL
    扩充

属性 作用
.host 返回主机(域名)
.port 返回端口号,若未写返回空字符串
.pathname 返回路径
.search 返回参数
.hash 返回片段

方法
location.assign(地址) 记录浏览历史,所以可以实现后退功能

location.replace() 不记录浏览历史,所以不可以实现后退功能

location.reload() 重新加载界面,相当于刷新

  • navigator 对象
    包含浏览器有关的信息,比如判断用户使用什么打开的界面,PC 端还是移动端,从而显示不同的界面(了解即可)

  • history 对象

后缀函数 作用
back() 后退功能
forward() 前进功能
go(参数) 前进后退功能,参数为 1前进,-1 后退

元素系列

  • 偏移量 offset 系列
element.offsetParent 返回该元素带有定位到父元素,若父元素都没有则返回 body(下同)
element.offsetTop 返回元素相对带有定位父元素上方的偏移
element.offsetLeft 返回元素相对带有定位父元素左边框的偏移
element.offsetWidth 返回自身包括 padding、边框、内容区的宽度数值(不带单位)
element.offsetHeight 返回自身包括 padding、边框、内容区的高度数值(不带单位)

区分:想获取元素大小位置用 offset
           想要给元素更改值用 style

来个栗子(放大镜)

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
// 在页面加载完后才执行
window.addEventListener('load', function() {
// 1. 获取元素
var son = document.querySelector(".son");
var big = document.querySelector(".big");
var father = document.querySelector(".father");
// 2. 鼠标经过时显示,移走隐藏
father.addEventListener('mouseover', function() {
father.style.cursor = 'move';
son.style.display = 'block';
big.style.display = 'block'
})
father.addEventListener('mouseout', function() {
son.style.display = 'none';
big.style.display = 'none'
})
// 鼠标移动时,蓝色盒子跟着鼠标走
father.addEventListener('mousemove', function(e) {
// (1) 先计算出鼠标在盒子内的坐标
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
// (2) 减去盒子高度 300的一半 是 150 就是我们son 的最终 left 和top值了
// (3) 我们son 移动的距离
var sonX = x - son.offsetWidth / 2;
var sonY = y - son.offsetHeight / 2;
// (4) 如果x 坐标小于了0 就让他停在0 的位置
// 遮挡层的最大移动距离
var sonMax = father.offsetWidth - son.offsetWidth;
if (sonX <= 0) {
sonX = 0;
} else if (sonX >= sonMax) {
sonX = sonMax;
}
if (sonY <= 0) {
sonY = 0;
} else if (sonY >= father.offsetHeight - son.offsetHeight) {
sonY = father.offsetHeight - son.offsetHeight;
}
son.style.left = sonX + 'px';
son.style.top = sonY + 'px';
// 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
// 大图
var bigIMg = document.querySelector('.bigImg');
// 大图片最大移动距离
var bigMax = bigIMg.offsetWidth - big.offsetWidth;
// 大图片的移动距离 X Y
var bigX = sonX * bigMax / sonMax;
var bigY = sonY * bigMax / sonMax;
bigIMg.style.left = -bigX + 'px';
bigIMg.style.top = -bigY + 'px';
})
})
  • 可视区 client 系列
element.clientTop 返回元素上边框的大小
element.clientLeft 返回元素左边框的大小
element.clientWidth 返回自身包括 padding、内容区的宽度,不包括边框(不带单位)
element.clientHeight 返回自身包括 padding、内容区的高度,不包括边框(不带单位)

扩充:
立即执行函数:不需要调用,立马能够自己执行的函数
写法:(function() {})();    或者    (function(){}());

  • 滚动 scroll 系列
element.scrollTop 返回被卷去的上侧距离(不带单位 下同)
element.scrollLeft 返回被卷去的左侧距离
element.scrollWidth 返回自身实际的宽度,不含边框
element.scrollHeight 返回自身实际的高度,不含边框

1
2
3
4
// scroll滚动事件当我们滚动条发生变化会触发的事件
div.addEventListener('scroll', function() {
console.log(div.scrollTop)
})

区分:想获取元素大小位置用 offset
            想要给元素更改值用 style
            获取元素大小用 client
            获取滚动距离用 scroll        页面的滚动距离是通过 window.pageXoffset 获得

实栗

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// 轮播图
window.addEventListener('load', function() {
// 1. 获取元素
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.focus');
var focusWidth = focus.offsetWidth;
// 2. 鼠标经过focus 就显示隐藏左右按钮
focus.addEventListener('mouseenter', function() {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
clearInterval(timer);
timer = null; // 清除定时器变量
});
focus.addEventListener('mouseleave', function() {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
timer = setInterval(function() {
//手动调用点击事件
arrow_r.click();
}, 2000);
});
// 3. 动态生成小圆圈 有几张图片,我就生成几个小圆圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
// console.log(ul.children.length);
for (var i = 0; i < ul.children.length; i++) {
// 创建一个小li
var li = document.createElement('li');
// 记录当前小圆圈的索引号 通过自定义属性来做
li.setAttribute('index', i);
// 把小li插入到ol 里面
ol.appendChild(li);
// 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
li.addEventListener('click', function() {
// 干掉所有人 把所有的小li 清除 current 类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下我自己 当前的小li 设置current 类名
this.className = 'current';
// 5. 点击小圆圈,移动图片 当然移动的是 ul
// ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
// 当我们点击了某个小li 就拿到当前小li 的索引号
var index = this.getAttribute('index');
// 当我们点击了某个小li 就要把这个li 的索引号给 num
num = index;
// 当我们点击了某个小li 就要把这个li 的索引号给 circle
circle = index;
// num = circle = index;
console.log(focusWidth);
console.log(index);

animate(ul, -index * focusWidth);
})
}
// 把ol里面的第一个小li设置类名为 current
ol.children[0].className = 'current';
// 6. 克隆第一张图片(li)放到ul 最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7. 点击右侧按钮, 图片滚动一张
var num = 0;
// circle 控制小圆圈的播放
var circle = 0;
// flag 节流阀
var flag = true;
arrow_r.addEventListener('click', function() {
if (flag) {
flag = false; // 关闭节流阀
// 如果走到了最后复制的一张图片,此时 我们的ul 要快速复原 left 改为 0
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function() {
flag = true; // 打开节流阀
});
// 8. 点击右侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
circle++;
// 如果circle == 4 说明走到最后我们克隆的这张图片了 我们就复原
if (circle == ol.children.length) {
circle = 0;
}
// 调用函数
circleChange();
}
});

// 9. 左侧按钮做法
arrow_l.addEventListener('click', function() {
if (flag) {
flag = false;
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';

}
num--;
animate(ul, -num * focusWidth, function() {
flag = true;
});
// 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
circle--;
// 如果circle < 0 说明第一张图片,则小圆圈要改为第4个小圆圈(3)
// if (circle < 0) {
// circle = ol.children.length - 1;
// }
circle = circle < 0 ? ol.children.length - 1 : circle;
// 调用函数
circleChange();
}
});

function circleChange() {
// 先清除其余小圆圈的current类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下当前的小圆圈的current类名
ol.children[circle].className = 'current';
}
// 10. 自动播放轮播图
var timer = setInterval(function() {
//手动调用点击事件
arrow_r.click();
}, 2000);

})

3. 面向对象

创建及继承

来个栗子

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
// 创建一个类
class Father {
// constructor 为构造函数
constructor(x, y) {
this.x = x;
this.y = y;

sum() {
console.log(this.x + this.y);

say() {
return '我是爸爸';
}
}
// 类的继承
class Son extends Father {
constructor(x, y) {
// 利用 super 调用父类中的构造函数
super(x, y);
// x y 也可以从父类中继承过来
// this.x = x;
// this.y = y;

say() {
// super 关键字调用父类普通函数
console.log(super.say() + '的儿子');

subtract() {
console.log(this.x - this.y);
}
}

var son = new Son(1, 2);
// 父类的方法
son.sum();
// 子类的方法
son.say();
son.subtract();

在使用函数时,尤其需要注意 this 指向的到底是什么,一个口诀,谁调用,this 就指向谁
实例见案例/js/tab.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
34
35
36
37
38
39
40
41
42
43
44
// 1. 利用 new Object() 创建对象
var obj1 = new Object()

// 2. 利用 对象字面量创建对象
var obj2 = {}

// 3. 利用构造函数创建对象
function Star(uname, age) {
this.uname = uname;
this.age = age;
// 利用构造函数创建对象会有一些问题,不同对象的相同函数是占不同空间的 (不同于C++)
this.sing = function() {
console.log('xiao_shuang 哈哈哈');
}

// 这时就可以利用原型对象(本质上也是一个对象)
// 一般情况下,公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上
Star.prototype.sing = function() {
console.log('我会唱歌');

// 方法的查找规则: 先看对象是否有对应方法,再看 prototype 里是否有对应方法
// 为了验证,我在构造函数里定义了一个与 prototype 里同名的方
// 构造函数需与 new 一起使用
var ldh = new Star('刘德华', 18);
console.log(ldh);
ldh.sing()

// 定义在原型里打印出 true,在构造函数里是 false ,进一步验证
var xiao = new Star('xiao', 18);
console.log(ldh.sing === xiao.sing)

// 注意,prototype 是构造函数的原型对象,对象也有原型 __proto__ , 是指向 prototype 的
console.log(ldh.__proto__ === Star.prototype)

// 1. 实例成员 就是构造函数内部通过this添加的成员 uname age sing 就是实例成员
// 实例成员只能通过实例化的对象来访问
console.log(ldh.uname);
console.log(Star.uname); // 不可以通过构造函数来访问实例成员 undefined

// 2. 静态成员 在构造函数本身上添加的成员,sex 就是静态成员
Star.sex = '男';
// 静态成员只能通过构造函数来访问
console.log(Star.sex);
console.log(ldh.sex); // 不能通过对象来访问 undefined

原型对象

理解原型链: (套娃)

    1. 只要是对象就有__proto__原型, 指向原型对象
    1. Star原型对象里面的__proto__原型指向的是Object.prototype
    1. Object.prototype原型对象里面的__proto__原型指向为 null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
var ldh = new Star('刘德华', 18);
console.log(Star.prototype);
console.log(Star.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__);
// 沿着链
ldh.sex = '男';
// Star.prototype.sex = '女';
// Object.prototype.sex = '男';
console.log(ldh.sex);
// 一些方法之所以可以直接使用,就是因为有人在 Object.prototype 里已经定义好了

// 顺带一提,原型对象函数里面的 this 指向的是实例对象
Star.prototype = {
// 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回的构造函数
constructor: Star,
sing: function() {
console.log('我会唱歌');
},
}

区分:
Star.prototype.sing = function() {} 在原型对象中添加方法
Star.prototype = {} 直接覆盖原型对象

实栗

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
// 原型对象的应用 扩展内置对象方法
Array.prototype.sum = function() {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
};
// 这样是覆盖,要注意哦
// Array.prototype = {
// sum: function() {
// var sum = 0;
// for (var i = 0; i < this.length; i++) {
// sum += this[i];
// }
// return sum;
// }
// }
var arr = [5, 2, 3];
console.log(arr.sum());
// 这个是已有方法:排序
console.log(arr.sort());
console.log(Array.prototype);
// new 一个数组
var arr1 = new Array(11, 22, 33);
console.log(arr1.sum());

class 出现之前的继承 (了解)

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
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function() {
console.log(100000);
};
// 2 .子构造函数
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score;
}
// Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也着一起变化 (对象的直接赋值是指针)
// 相当于加了个中间介质
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;
// 这个是子构造函数专门的方法
Son.prototype.exam = function() {
console.log('孩子要考试');
}
var son = new Son('刘德华', 18, 100);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);
son.money();

扩充方法

call()

1
2
3
4
5
6
7
8
9
10
11
12
function fn(x, y) {
console.log('我想喝手磨咖啡');
console.log(this);
console.log(x + y);
}
var o = {
name: 'andy'
};
// 1. call() 可以调用函数
fn.call(); //等同于 fn();
// 2. call() 可以改变这个函数的 this 指向,此时这个函数的 this 就指向了o这个对象
fn.call(o, 1, 2)

forEach()

1
2
3
4
5
6
7
8
9
10
11
// 迭代(遍历)数组
var arr = [1, 2, 3];
var sum = 0;
arr.forEach(function(value, index, array) {
// 三个参数
console.log('每个数组元素' + value);
console.log('每个数组元素的索引号' + index);
console.log('数组本身' + array);
sum += value;
})
console.log(sum);

filter()

1
2
3
4
5
6
7
// 筛选数组 (直接返回一个数组)
var arr = [12, 66, 4, 88, 3, 7];
var newArr = arr.filter(function(value, index) {
// return value >= 20;
return value % 2 === 0;
});
console.log(newArr);

some()

1
2
3
4
5
6
7
8
9
10
11
12
13
// 查找数组中是否有满足条件的元素
var arr = [10, 30, 4];
var flag = arr.some(function(value) {
// 没有小于 3的,false
return value < 3;
});
console.log(flag)
var arr1 = ['red', 'pink', 'blue'];
var flag1 = arr1.some(function(value) {
// 有 pink,true
return value == 'pink';
});
console.log(flag1);

  1. filter 是查找满足条件的元素,返回的是一个数组,而且是把所有满足条件的元素返回回来
  2. some 是查找满足条件的元素是否存在,返回的是一个布尔值,如果查找到第一个满足条件的元素就终止循环

trim()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 去除字符串两侧空格
var str = ' an dy ';
console.log(str);
var str1 = str.trim();
console.log(str1);
var input = document.querySelector('input');
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.onclick = function() {
var str = input.value.trim();
if (str === '') {
alert('请输入内容');
} else {
console.log(str);
console.log(str.length);
div.innerHTML = str;
}
}

Object.keys()

1
2
3
4
5
6
7
8
9
10
11
12
13
// 用于获取对象自身所有的属性
var obj = {
id: 1,
pname: '小米',
price: 1999,
num: 2000
};
var arr = Object.keys(obj);
// 返回的是一个数组
console.log(arr);
arr.forEach(function(value) {
console.log(value);
})

Object.defineProperty()

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
var obj = {
id: 1,
pname: '小米',
price: 1999
};
// 1. 以前的对象添加和修改属性的方式
// obj.num = 1000;
// obj.price = 99;
// console.log(obj)

// 2. Object.defineProperty() 定义新属性或修改原有的属性
Object.defineProperty(obj, 'num', {
value: 1000,
enumerable: true
});
Object.defineProperty(obj, 'price', {
value: 9.9
});
console.log(obj);
Object.defineProperty(obj, 'id', {
// 如果值为 false 不允许修改这个属性值,默认值也是false
writable: false,
});
// 无效修改
obj.id = 2;
Object.defineProperty(obj, 'address', {
value: '中国山东蓝翔技校xx单元',
// 如果只为false 不允许修改这个属性值 默认值也是 false
writable: false,
// enumerable 如果值为 false 则不允许遍历, 默认的值是 false
enumerable: false,
// configurable 如果为 false 则不允许删除这个属性,不允许再修改第三个参数里面的特性,默false
configurable: false
});
console.log(obj);
// 利用 key 方法获取键
console.log(Object.keys(obj));
// 无效删除
delete obj.address;
delete obj.pname;
console.log(obj)
Object.defineProperty(obj, 'address', {
value: '中国山东蓝翔技校xx单元',
// 如果只为false 不允许修改这个属性值 默认值也是false
writable: true,
// enumerable 如果值为false 则不允许遍历, 默认的值是 false
enumerable: true,
// configurable 如果为false 则不允许删除这个属性 默认为false
configurable: true
});
// address 里的 configurable 不允许再次修改,所以会报错
console.log(obj.address);

函数

定义方式

  1. 自定义函数 (命名函数)
    function fn() {};

  2. 函数表达式 (匿名函数)
    var fun = function() {};

  3. 利用 new Function('参数1','参数2', '函数体');

1
2
var f = new Function('a', 'b', 'console.log(a + b)');
f(1, 2);

补充

  • 所有函数都是 Function 的实例(对象)
  • 函数也属于对象 (万物皆对象)
    console.log(f instanceof Object); –> true

this 指向谁

  1. 普通函数 (this 指向 window)
1
2
3
4
5
function fn() {
console.log('1.人生的巅峰');
console.log('普通函数的this -> ' + this);
}
window.fn(); // fn.call();
  1. 对象 (this 指向的是对象 o)
1
2
3
4
5
6
7
var o = {
sayHi: function() {
console.log('2.人生的巅峰');
console.log('对象方法的this -> ' + this);
}
}
o.sayHi();
  1. 构造函数 (this 指向 ldh 这个实例对象)
1
2
3
4
5
6
7
function Star() {};
Star.prototype.sing = function() {
console.log('3.人生的巅峰');
console.log('构造函数的this -> ' + this);
}
var ldh = new Star();
ldh.sing();
  1. 绑定事件函数 (this 指向的是函数的调用者)
1
2
3
4
var btn = document.querySelector('button');
btn.onclick = function() {
console.log('绑定时间函数的this -> ' + this);
};
  1. 定时器函数 (this 指向的也是 window)
1
2
3
window.setTimeout(function() {
console.log('定时器的this -> ' + this);
}, 1000);
  1. 立即执行函数 自动调用,不需要调用 (this 还是指向 window)
1
2
3
4
(function() {
console.log('4.人生的巅峰');
console.log('立即执行函数的this -> ' + this);
})();

改变函数内 this 指向

方法 作用
call() 调用函数; 改变函数内的 this 指向 (主要作用可以实现继承)
apply() 主要作用同 call(), 但是它的参数必须是数组 (利用 apply 借助于数学内置对象求数组最大值 )
bind() 不会调用原来的函数; 返回的是原函数改变 this 之后产生的新函数
1
2
3
4
5
6
7
8
9
10
11
12
13
// 如果有的函数我们不需要立即调用, 但是又想改变这个函数内部的 this 指向时用 bind
// 栗:有一个按钮, 当点击了之后, 就禁用这个按钮, 3秒钟之后开启这个按钮
var btns = document.querySelectorAll('button');
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
this.disabled = true; // 这个 this 指向的是点击的按钮
// 定时器函数里面的 this 指向的是 window
setTimeout(function() {
// 此处 this 已经被改变
this.disabled = false;
}.bind(this), 2000); // 这个 this 指向的是 btn[i] 这个对象 (在函数外面)
}
}

严格模式

'use strict'

  • 严格模式的变化
  1. 变量名必须先声明再使用
  2. 不能随意删除已经声明好的变量
  3. 全局作用域中函数中的 this 是 undefined
  4. 如果构造函数不加 new 调用, this 指向的是 undefined, 如果给他赋值则会报错
  5. 定时器 this 还是指向 window
  6. 函数里面的参数不允许有重名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 为某个函数开启严格模式

// 此时是给整个脚本开启严格模式
<script>
'use strict';
</script>

// 此时只是给fn函数开启严格模式
function fn() {
'use strict';
// 下面的代码按照严格模式执行
}

function fun() {
// 里面的还是按照普通模式执行
}

高阶函数

1
2
3
4
5
6
7
8
9
// 高阶函数 --> 函数可以作为参数传递/ 函数可以作为返回值输出
function fn(a, b, callback) {
console.log(a + b);
// 前面所以代码指向完后再调用 callback
callback && callback();
}
fn(1, 2, function() {
console.log('我是最后调用的');
});

闭包

指有权访问另一个函数作用域中变量的函数(像立即执行函数就是),其作用是延伸了变量的作用范围

来几个应用

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// * 闭包应用 - 点击 li输出当前 li的索引号
// 1. 可以利用动态添加属性的方式
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
lis[i].index = i;
lis[i].onclick = function() {
// 不能直接打印 i,因为 function 在点击之后才会执行,但 for循环是同步任务,会立即执行得 i = lis.length
// console.log(i);
console.log(this.index);
}

// 2. 利用闭包的方式得到当前小li 的索引号
for (var i = 0; i < lis.length; i++) {
// 利用 for 循环创建了4个立即执行函数
// 立即执行函数也称为小闭包,因为立即执行函数里面的任何一个函数都可以使用它的i这变量
(function(i) {
lis[i].onclick = function() {
// 每次传递进来的 i 是不一样的
console.log(i);
}
})(i);

// * 闭包应用 - 3秒钟之后, 打印所有li元素的内容
for (var i = 0; i < lis.length; i++) {
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(i);
}
// setTimeout(function() {
// for (var i = 0; i < lis.length; i++) {
// console.log(lis[i].innerHTML);
// }
// }, 3000)

// * 闭包应用 - 计算打车价格
// 打车起步价13(3公里内), 之后每多一公里增加5块钱. 用户输入公里数就可以计算打车价格
// 如果有拥堵情况, 总价格多收取10块钱拥堵费
// - 匿名函数
var car = (function() {
var start = 13; // 起步价 局部变量
var total = 0; // 总价 局部变量
return {
// 正常的总价
price: function(n) {
if (n <= 3) {
total = start;
} else {
total = start + (n - 3) * 5
}
return total;
},
// 拥堵之后的费用
yd: function(flag) {
return flag ? total + 10 : total;
}
}
})();
console.log(car.price(5)); // 23
console.log(car.yd(true)); // 3
console.log(car.price(1)); // 13
console.log(car.yd(false)); // 1


// 思考题 1:
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
// console.log(this.name);
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()()) // The Window
// 类似于
var f = object.getNameFunc();
// var f = function() {
// return this.name;
// }
console.log(f())

// 思考题 2:
var name = "The Window";  
var object = {    
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()()) // My Object

递归函数

函数内部自己调用自己

1
2
3
4
5
6
7
8
// 栗:利用递归函数求斐波那契数列(兔子序列)  1、1、2、3、5、8、13、21...
function fb(n) {
if (n === 1 || n === 2) {
return 1;
}
return fb(n - 1) + fb(n - 2);
}
console.log(fb(6));

拷贝

  • 浅拷贝:只是拷贝一层,更深层次对象级别的只拷贝引用(地址),一改全改(指向一个地址)
  • 深拷贝:每一级别的数据都会拷贝
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
var obj1 = {
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
};
var o1 = {};
// 封装函数 -- 利用递归
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
// 判断我们的属性值属于那种数据类型
// 1. 获取属性值 oldobj[k]
var item = oldobj[k];
// 2. 判断这个值是否是数组
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item)
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象
newobj[k] = {};
deepCopy(newobj[k], item)
} else {
// 4. 属于简单数据类型
newobj[k] = item;
}
}
}
deepCopy(o1, obj1);
console.log(o1);
var arr = [];
// 数组属于对象
console.log(arr instanceof Object);
o1.msg.age = 20;
console.log(obj1);

正则表达式

创建方法:

    1. 利用 RegExp(正则表达式) 对象来创建 - 万物皆对象 -
    1. 利用字面量创建正则表达式
    1. test 方法用来检测字符串是否符合正则表达式要求的规范

呃,细节太多不好学,直接看代码吧

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
// - 边界符 ^ (开始) / $ (结尾)
var reg = /^abc/;
console.log(reg.test('abc'));
console.log(reg.test('babcd')); // false
console.log(reg.test('aabcd')); // false
console.log('---------------------------');
// 精确匹配 要求必须是 abc字符串才符合规范
var reg1 = /^abc$/;
console.log(reg1.test('abc'));
console.log(reg1.test('abcd')); // false
console.log(reg1.test('abcabc')); // fals
// - 字符类: [] 表示有一系列字符可供选择,只要匹配其中一个就可以了
var rg = /[abc]/; // 只要包含有 a 或者包含有 b 或者包含有 c 都返回为true
console.log(rg.test('andy'));
console.log(rg.test('color'));
var rg1 = /^[abc]$/; // 三选一 只有是a 或者是 b 或者是c 这三个字母才返回 true
console.log(rg1.test('aa')); // false
console.log(rg1.test('a'));
console.log(rg1.test('c'));
console.log(rg1.test('abc')); // false
console.log('------------------');
// - 表示的是 a 到 z 的范围
var reg = /^[a-z]$/; // 26个英文字母任何一个字母返回 true
console.log(reg.test('a'));
console.log(reg.test('z'));
console.log(reg.test(1)); // false
console.log(reg.test('A')); // false
// 字符组合 (不需要空格)
var reg1 = /^[a-zA-Z0-9_-]$/; // 26个英文字母(大写和小写都可以)任何一个字母返回 true
console.log(reg1.test('a'));
console.log(reg1.test('B'));
console.log(reg1.test(8));
console.log(reg1.test('-'));
console.log(reg1.test('_'));
console.log(reg1.test('!')); // false
console.log('----------------');
// 如果中括号里面有 ^ 表示取反的意思,千万和边界符 ^ 别混淆
var reg2 = /^[^a-zA-Z0-9_-]$/;
console.log(reg2.test('a')); // false
console.log(reg2.test('B')); // false
console.log(reg2.test(8)); // false
console.log(reg2.test('-')); // false
console.log(reg2.test('_')); // false
console.log(reg2.test('!'));

ES6

let/ const

  • 使用 let 关键字声明的变量具有块级作用域,var关键字是不具备这个特点的
  • 防止循环变量变成全局变量
  • 使用 let关键字声明的变量没有变量提升 (即不能先打印再声明)
  • 使用 let关键字声明的变量具有暂时性死区特性 (使用了 let 声明变量,则外部同名变量无效)

const 就不多说了

解构

数组解构
允许按照一一对应的关系从数组中提取值,然后将值赋值给变量

1
2
3
4
5
6
7
8
let ary = [1, 2, 3];
// a, b, c 等是变量名,即:a=1; b=2; c=3; d, e为 undefined
let [a, b, c, d, e] = ary;
console.log(a)
console.log(b)
console.log(c)
console.log(d)
console.log(e)

对象解构
允许使用变量的名字匹配对象的属性,匹配成功,将对象属性的值赋值给变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let person = {
name: 'lisi',
age: 30,
sex: '男'
};
let {
name,
age,
sex
} = person; // 匹配属性成功,将对象属性的值赋值给变量
console.log(name)
console.log(age)
console.log(sex)
let {
name: myName
} = person;
console.log(myName)

箭头函数

箭头函数是用来简化函数定义语法的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// - 在箭头函数中, 如果函数体中只有一句代码, 并且代码的执行结果就是函数的返回值, 函数体大括号可以省略
const sum = (n1, n2) => n1 + n2; // return n1 + n2;
const result = sum(10, 20);
console.log(result)

// - 在箭头函数中, 如果形参只有一个, 形参外侧的小括号也是可以省略的
const fn1 = v => {
console.log(v);
}
fn1(20)

// - 箭头函数不绑定 this, 箭头函数没有自己的 this 关键字, 如果在箭头函数中使用 this, this 关键将指箭头函数定义位置中的 this
function fn2() {
console.log(this);
return () => {
console.log(this) // 此处 this 指向 fn 中的 this
}
}
const obj = {
name: 'zhangsan'
};
const resFn = fn2.call(obj); // 传过去了对象 obj, 默认是 window
resFn();

剩余参数

跟 python 的很像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const sum1 = (...args) => {
let total = 0;
// 遍历数组
args.forEach(item => total += item);
return total;
};
console.log(sum1(10, 20));
console.log(sum1(10, 20, 30));

// 这样赋值。。。呃
let ary1 = ['张三', '李四', '王五'];
let [s1, ...s2] = ary1;
console.log(s1)
console.log(s2)

扩展运算符

  • 可以将数组拆分成以逗号分隔的参数序列
1
2
3
4
5
// 区分:在函数(主要)里赋值是将多个参数化为一个数组,直接使用是将数组变为参数序列
let ary = ["a", "b", "c"];
// ...ary "a", "b", "c"
console.log(...ary) // 打印时,逗号被当作空格,所以没有显示
console.log("a", "b", "c")
  • 应用于数组合并 — 既然是把数组拆了,那再加上 [] 就又变成数组了
1
2
3
4
5
let ary1 = [1, 2, 3];
let ary2 = [4, 5, 6];
let ary3 = [...ary1, ...ary2];
// 也可以使用函数 push: ary1.push(...ary2)
console.log(ary3)
  • 利用扩展运算符将伪数组转换为真正的数组
1
2
3
4
5
6
var oDivs = document.getElementsByTagName('div'); // 得伪数组
console.log(oDivs)
var ary_ = [...oDivs]; // 变为真正的数组
// oDivs.push('a') 伪数组是不能使用数组方法的
ary_.push('a');
console.log(ary_);

扩展方法

Array

  • from(): 将伪数组转换为真正的数组,还可以接受第二个参数:箭头函数
  • find(): 找到第一个符合条件的数组成员并返回,没有找到则返回 undefined
  • finfindex(): 找到第一个符合条件的数组成员位置,没有找到则返回 -1
  • includes(): 数组是否含有给定值,返回布尔值
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
// from()
var arrayLike = {
0: "张三",
1: "李四",
2: "王五",
length: 3
}
var ar = Array.from(arrayLike);
console.log(ar)

var arrayLike1 = {
0: "1",
1: "2",
length: 2
}
var ar1 = Array.from(arrayLike1, item => item * 2)
console.log(ar1)

// find()
var a = [{
id: 1,
name: '张三'
}, {
id: 2,
name: '李四'
}];
// 根据项目经验,find 中有两个已定义参数:item, index 分别表示值和索引
// 所以说,find 是调用了多次括号里的函数,类似于遍历 (即有数组多少个元素就会调用多少次括号里数)
let target = a.find(item => item.id == 1);
console.log(target

// finfindex()
let ary0 = [10, 20, 50];
let index = ary0.findIndex(item => item > 15); // 1
console.log(index)

// includes()
// 变量命名也是头疼
let ary4 = ["a", "b", "c"];
let result = ary4.includes('a') // true
console.log(result)
result = ary4.includes('e') // false
console.log(result)

String

  • startWith(): 参数字符串是否在原字符串的头部,返回布尔值
  • endWith(): 参数字符串是否在原字符串的尾部,返回布尔值
  • repeat(): 将原字符串重复 n 次,返回一个新的字符串
1
2
3
4
5
6
let str = 'Hello ECMAScript 2015';
let r1 = str.startsWith('Hello'); // true
console.log(r1);
let r2 = str.endsWith('2020'); // false
console.log(r2)
console.log('y'.repeat(5))

模板字符串

使用反引号定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let name = `张三`;
// 模板字符串中可以直接解析变量
// 这里是单 {},不要和 {{}} 弄混了
let sayHello = `Hello, 我的名字叫${name}`;
console.log(sayHello)
let result = {
name: "zhangsan",
age: 20
};
let html = `
<div>
<span>${result.name}</span>
<span>${result.age}</span>
</div>
`;
console.log(html)
const fn = () => '我是fn函数'
let html_ = `我是模板字符串 ${fn()}`;
console.log(html_)

set 数据结构

  • Set 类似于数组,但成员的值都是唯一的,没有重复的值
  • Set 本身是一个构造函数,所以像对象一样 new 一个
  • 可以进行 add、delete、has、clear等操作