算法
队列
class Queue{
private items: any[];
constructor(){
this.items = [];
}
enqueue(...elements:any[]){
this.items = this.items.concat(elements);
}
dequeue(): any{
return this.items.shift();
}
front():any{
return this.items[0];
}
clear(){
this.items = [];
}
isEmpty():boolean{
return this.items.length === 0;
}
size():number{
return this.items.length;
}
print(){
console.log(this.items.toString());
}
}
单链表
interface Node {
element: any;
next: any;
}
class LinkedList {
private length:number = 0;
private list:any = null;
getNode(element:any):Node{
return {
element,
next: null
}
}
append(element:any){
let node = this.getNode(element);
if(!this.list){
this.list = node;
}else{
let current = this.list;
while(current.next){
current = current.next;
}
current.next = node;
}
this.length++;
}
insert(position:number, element:any){
let node = this.getNode(element),
current = this.list;
if(this.length >= position){
if(position === 0){
node.next = current;
this.list = node;
}else{
let previous = null;
for(let i=0; i<position; i++){
previous = current;
current = current.next;
}
previous.next = node;
node.next = current;
}
this.length++;
}else{
this.append(element);
}
}
removeAt(position:number){
if(position === 0){
this.list = this.list.next;
}else{
let previous = this.list;
for(let i=0; i<position-1; i++){
previous = previous.next;
}
let next = previous.next.next;
previous.next = next;
}
this.length--;
}
remove(element:any){
this.removeAt(this.indexOf(element));
}
indexOf(element:any):number{
var index = 0,
current = this.list;
while(element !== current.element){
index++;
current = current.next;
}
return index;
}
isEmpty():boolean{
return this.length === 0;
}
size():number{
return this.length;
}
getHead():object{
return this.list;
}
toString():string{
let string = '',
current = this.list;
if(current){
string += current.element;
while(current.next){
current = current.next;
string += current.element;
}
}
return string;
}
print(){
console.log(this.toString());
}
}
// 测试代码
var linkedList = new LinkedList();
linkedList.print();
linkedList.append(1);
linkedList.print();
linkedList.append(2);
linkedList.print();
linkedList.append(3);
linkedList.print();
linkedList.append(5);
linkedList.print();
linkedList.insert(3, 4);
linkedList.print();
console.log(linkedList.indexOf(4));
linkedList.remove(4);
linkedList.print();
linkedList.removeAt(3);
linkedList.print();
console.log(linkedList.size());
linkedList.getHead();
双向链表
interface Node {
element: any;
next: any;
prev: any;
}
class LinkedList {
private length: number = 0;
private list: any = null;
getNode(element: any): Node {
return {
element,
next: null,
prev: null
}
}
append(element: any) {
const node = this.getNode(element);
if (!this.list) {
this.list = node;
} else {
let current = this.list
while (current.next) {
current = current.next;
}
current.next = node;
node.prev = current;
}
++this.length;
}
insert(position: number, element: any) {
let current = this.list;
let node = this.getNode(element);
if (this.length > position && position >= 0) {
if (position === 0) {
node.next = current;
this.list = node;
} else {
for (let i = 1; i < position; i++) {
current = current.next;
}
current.next.prev = node;
node.next = current.next;
current.next = node;
node.prev = current;
}
++this.length
} else {
this.append(element);
}
}
removeAt(position: number) {
if (this.length > position && position >= 0) {
if (position === 0) {
this.list = this.list.next;
} else {
let current = this.list;
for (let i = 1; i < position; i++) {
current = current.next;
}
if(current.next && current.next.next){
current.next.next.prev = current;
current.next = current.next.next;
}else if(current.next && !current.next.next){
current.next = null;
}
}
--this.length
}
}
remove(element: any) {
this.removeAt(this.indexOf(element))
}
indexOf(element: any): number {
let index = -1;
let current = this.list;
for (let i = 0; i < this.length; i++) {
if (current.element === element) {
return i
}
current = current.next;
}
return -1
}
isEmpty(): boolean {
return this.length === 0;
}
size(): number {
return this.length;
}
getHead() {
return this.list;
}
toString(): string {
var string: string = '',
current = this.list;
if(current){
string += current.element;
while(current.next){
current = current.next;
string += current.element;
}
}
return string;
}
print() {
console.log(this.toString())
}
}
var linkedList = new LinkedList();
linkedList.print();
linkedList.append(1);
linkedList.print();
linkedList.append(2);
linkedList.print();
linkedList.append(3);
linkedList.print();
linkedList.append(5);
linkedList.print();
linkedList.insert(3, 4);
linkedList.print();
console.log(linkedList.indexOf(4));
linkedList.remove(4);
linkedList.print();
linkedList.removeAt(3);
linkedList.print();
console.log(linkedList.size());
linkedList.getHead();
集合
class Set{
private items: any = {};
private length: number = 0;
add(value:any): boolean{
if(!this.has(value)){
this.items[value] = value;
++this.length;
return true;
}
return true;
}
has(value:any): boolean{
return this.items.hasOwnProperty(value);
}
remove(value){
if(this.has(value)){
delete this.items[value];
--this.length;
return true;
}
return false;
}
clear(){
this.items = {};
this.length = 0;
}
size():number{
return this.length;
}
values(){
var arr = [];
Object.keys(this.items).map(item => {
arr.push(this.items[item]);
})
return arr
}
union(set):any[]{
var arr = this.values();
set.values().map(item => {
if(!this.has(item)){
arr.push(item)
}
});
return arr
}
intersectionSet(set):any[]{
var arr = [];
set.values().map(item => {
if(this.has(item)){
arr.push(item)
}
});
return arr;
}
difference(set):any[]{
var arr = [];
this.values().map(item => {
if(!set.has(item)){
arr.push(item)
}
});
return arr;
}
subset(set){
return set.values().every(item => {
return this.has(item)
})
}
}
var set = new Set();
set.add(1);
console.log(set.values());
console.log(set.has(1));
console.log(set.size());
set.add(2);
console.log(set.values());
set.add(1);
console.log(set.values());
set.remove(1);
console.log(set.values());
set.remove(2);
console.log(set.values());
var set1 = new Set();
set1.add(1);
set1.add(2);
var set2 = new Set();
set2.add(2);
set2.add(3);
var set3 = new Set();
set3.add(2);
console.log(set1.union(set2))
console.log(set1.intersectionSet(set2))
console.log(set1.difference(set2))
console.log(set2.difference(set1))
console.log(set1.subset(set2))
console.log(set1.subset(set3))
正则
时分秒:/(([0|1]\d)|(2[0-3])):([0-5]\d):([0-5]\d)/
匹配 16 进制:/#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/
知识整理
DOM2 和 DOM3
获取 css 样式值
var divDom = document.querySelector('div');
var computedStyle = window.getComputedStyle(divDom, null);
var width = computedStyle.width
边距描述
// width/height + border + padding + 滚动条
offsetWidth & offsetHeight
// margin + top
offsetLeft & offsetTop
// width/height + padding
clientWidth & clientHeight
// border + margin + top + 滚动条
clientLeft & clientTop
// width/height + padding
scrollWidth & scrollHeight
// 滚动位置
scrollLeft & scrollTop
事件机制
IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获。
事件流的三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。

[1-4]是事件捕获阶段 [4-5]是处于目标阶段 [5-9]是事件冒泡阶段
各浏览器的事件触发及事件委托
IE只支持事件冒泡,不支持事件捕获,IE的事件流只有事件从发生的目标开始,沿着文档逐层向上冒泡到document为止,其他浏览器支持捕获也支持冒泡。很明显如果在捕获阶段触发的话,事件会被触发两次。在冒泡阶段捕获只会触发一次。
W3C事件模型中你可以选择是在捕获阶段还是冒泡阶段绑定事件处理函数,这是通过addEventListener()方法实现的,如果这个函数的最后一个参数是true,则在捕获阶段绑定函数,反之false,在冒泡阶段绑定函数。
element1.addEventListener('click',doSomething1,true) // 捕获阶段触发
element2.addEventListener('click',doSomething2,false) // 冒泡阶段触发
这种模型下,还可以把事件冒泡关掉。在回调函数doSomething中,可以得到一个目标事件对象参数,这里取值为event。可以使用e.stopPropagation(),关掉从这个事件之后的事件冒泡。还有个常用方法是e.preventDefault(),这个是阻止目标事件的默认事件的触发,比如<a>标签的跳转,<button>按钮的提交等。千万不要记混了。
IE事件模型中不支持事件捕获,所以也就不会有第三个参数,使用方法也变了。
element.attachEvent("onclick", doSomething);
IE事件模型也有阻止事件冒泡的方法window.event.cancelBubble = true;,也有阻止事件的默认事件的触发window.event.returnValue = false;
还有些事件本身就不会冒泡blur、focus、load、unload
event
| 属性/方法 | 说明 |
|---|---|
| bubbles | 表明事件是否冒泡 |
| cancelable | 表明是够可以取消事件的默认行为 |
| currentTarget | 其事件处理程序当前正在处理事件的哪个元素 |
| defaultPrevented | 是否已经调用 event.prevented() |
| detail | 与时间相关的细节信息 |
| eventPhase | 调用事件处于哪个阶段 1:捕获,2:目标,3:冒泡 |
| preventDefault() | 取消事件的默认行为 |
| stopImmediatePropagation() | 取消事件的进一步捕获或冒泡,阻断任何事件处理程序被调用 |
| stopPropagation() | 取消事件的的捕获或者冒泡,不阻断事件处理程序被调用 |
事件类型
| 时间名 | 说明 |
|---|---|
| load | 当页面完全加载后 |
| unload | 当页面完全卸载之后 |
| abort | 用户停止加载时 |
| error | 发生了错误的时候 |
| select | 当用户选择文本框时 |
| resize | 当窗口大小发生变化的时候 |
| scroll | 当用户滚动带滚动条的元素时 |
| 鼠标事件 | |
| click | 点击事件 |
| dbclick | 双击 |
| mousedown | 按下鼠标 |
| mouseenter | 当鼠标滑入区域内时 |
| mouseleave | 当鼠标离开区域时触发 |
| mousemove | 当鼠标在区域内移动时触发 |
| mouseover | 当鼠标滑入区域内时触发 |
| mouseout | 当鼠标离开区域时触发 |
| mouseup | 当鼠标放开时触发 |
| 键盘事件 | |
| keydown | 键盘按下 |
| keypress | 按住的过程中 |
| keyup | 键盘那件释放时触发 |
| 设备事件 | |
| orientationchange | 屏幕发生旋转时触发 |
| deviceorientation | 陀螺仪 |
| 移动手点 | |
| touchstart | 手指触摸屏幕时触发 |
| touchmove | 手指在屏幕上滑动时连续触发 |
| touchend | 当手指离开时触发 |
| touchcancel | 当系统停止追踪时触发 |
| 手势 | |
| gesturestart | 当一个手指已经按照屏幕上,另一只手又触摸到屏幕了 |
| gesturechange | 当触摸屏幕的任何一个手指的位置发生变化时 |
| gestureend | 当任何一个手指离开时 |
事件顺序
touchstart -> mouseover -> mousemove -> mousedown -> mouseup -> click -> touchend
BOM
不同的 frame window 对象不同。
窗口位置
X 坐标:screenLeft 和 screenX
Y 坐标:screenTop 和 screenY
moveTo(移动至某点) 和 moveBy(移动距离)
窗口大小
// 现代浏览器器
window.innerWidth 和 window.innerHeight
// IE 8 以前
document.documentElement.clientWidth
document.documentElement.clinetHeight
// IE 的混杂模式
document.body.clientWidth
document.body.clinetHeight
// 修改窗口大小
window.resizeTo()
window.resizeBy()
location
| 属性名 | 例子 | 属性说明 |
|---|---|---|
| href | – | 完整的 URL 链接 |
| protocol | https | 协议 |
| hostname | www.baidu.com | 不带端口的服务器名称 |
| port | 8080 | 返回端口号 |
| host | www.baidu.com:80 | 返回服务器名称和端口号 |
| pathname | /filename/xxx.html | 返回目录名和文件名 |
| search | ?q=aaa | 查询字符串,到 # 号为止 |
| hash | #page | 到结束 |
// 返回上一页,go(-1): 返回上一页,原页面表单中的内容会丢失;back(-1): 返回上一页,原页表表单中的内容会保留,一般还是back(-1)
history.go(-1)
history.back()
// 下一页
history.go(1)
history.forward()
URLSearchParams
用户将 URL 参数的结构解析成对象获取。a=111&b=222&c=333这种结构,实例会有以下方法用户获取 params 信息。
- has():返回一个布尔值,表示是否具有某个参数
- get():返回指定参数的第一个值
- getAll():返回一个数组,成员是指定参数的所有值
- set():设置指定参数
- delete():删除指定参数
- append():在查询字符串之中,追加一个键值对
- toString():返回整个查询字符串
var paramsString = 'a=111&b=222&c=333';
var searchParams = new URLSearchParams(paramsString);
searchParams.has('a') // true
searchParams.get('a') // "111"
searchParams.getAll('a') // ["111"]
searchParams.get('d') // null,注意Firefox返回空字符串
searchParams.set('d', 444);
searchParams.get('d') // 444
searchParams.append('e', '555');
searchParams.toString() // "a=111&b=222&c=333&d=444&e=555"
searchParams.append('a', 666);
searchParams.getAll('a') // [111, 666]
searchParams.delete('e');
searchParams.toString() // "a=111&b=222&c=333&d=444&a=666"
hash
前端路由的实现一般有两种方式:
- location.hash + hashchange 事件
- history.pushState() + popState事件
hash 的改变并不会触发页面的跳转,所以现在的 SPA 基本上都是基于 hash 实现的,URL 中锚点的取值是从 #到结束,而参数是取自 ?到# 之间。所以 hash 后面带参数都会被认为是 hash,所以才能在现在前端路由中页面跳转带参数,将 hash 取出来再按照一定的规则进行处理得到路由的配置,执行不同的实例。
hashchange
hash 改变路由需要有监听,主要的监听方式由 hashchange 实现,当路由发生改变时会触发。
window.addEventListener('hashchange', function(){}, false);
state change
这种的实现主要是为了短暂的记录当前页面的状态。可能是 ajax 获取到了页面的数据,渲染页面,当页面返回时能从页面的状态中获取数据直接用于渲染
popstate
历史发生改变时都会触发,需要注意的是,仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用 back、forward、go 方法时才会触发。
window.onpopstate = function (event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
};
// 或者
window.addEventListener('popstate', function(event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
});
history.pushState
history.pushState 会让 URL 立即发生改变,但是页面并不会发生任何改变,只是历史记录中多了条记录,如果设置的是锚点也不会触发 hashchange 事件。包含以下参数:
- state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。
- title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null。
- url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。url必须同域,不能跨域
history.pushState({page: 2}, 'title 2', '?page=2');
history.replaceState
replaceState 和 pushState 相同,replace 是替换,push 是新增。
history.replaceState({page: 3}, 'title 3', '?page=3');
表单
表单类型
// 单选输入框
color、date、datetime、datetime-local、email、month、number、range、search、tel、time、url、week
<input type="text" name="firstname">
// 单选按钮 name 相同
<input type="radio" name="sex" value="male" checked>Male
<input type="radio" name="sex" value="female">Female
// 多选
<input type="checkbox" name="vehicle" value="Bike">I have a bike
<input type="checkbox" name="vehicle" value="Car">I have a car
// 下拉筛选
<select name="cars">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="fiat">Fiat</option>
<option value="audi">Audi</option>
</select>
// 输入文本框
<textarea name="message" rows="10" cols="30">
The cat was playing in the garden.
</textarea>
// 提交按钮
<input type="submit" value="Submit">
通用属性
<form>表单属性
- acceptCharset:服务器能够处理的字符集
- action:接受请求的 URL
- elements:接受请求的 URL
- enctype:请求的编码类型
- length:表单中控件的数量
- method:要发送的 HTTP 请求类型
- name:表单的名称
- reset():将所有表单域重置为默认值
- submit():提交表单
- target:用于发送请求和接受响应的窗口
<input>通用属性:
- disabled:当前字段是否被禁用
- form:指向当前字段所属表单的指针
- name:当前字段名
- value:当前字段值
- readOnly:当前字段是否只读
- tabIndex:当前字段的切换序号
- type:当前字段的类型
通用事件,
- blur:当前字段失去焦点时触发
- change:失去焦点且值改变之后触发
- focus:当前字段获取焦点
剪贴板操作
- beforecopy:在发生复制操作前
- copy:在发生复制操作时
- beforecut:在发生剪切操作前
- cut:在发生剪切操作时
- beforepaste:在发生粘贴操作前
- paste:在发生粘贴操作时
pasteEle.addEventListener("paste", function (e){
// clipboardData 粘贴对象
if ( !(e.clipboardData && e.clipboardData.items) ) {
return ;
}
for (var i = 0, len = e.clipboardData.items.length; i < len; i++) {
var item = e.clipboardData.items[i];
// 当粘贴内容为字符串时
if (item.kind === "string") {
item.getAsString(function (str) {
// str 是获取到的字符串
})
// 当粘贴内容为文件时
} else if (item.kind === "file") {
var pasteFile = item.getAsFile();
// pasteFile就是获取到的文件的 blob 对象
}
}
});
验证约束
- required:必填
- min:最小长度
- max:最大长度
- pattern:正则,验证内容书否符合规则
- novalidate:禁止验证
- checkValidaity():验证表单是够符合规则