2018-01

博客分类: 江河计划-月总结

2018-01

算法

队列

 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的事件流是事件捕获。

事件流的三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。

image

[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 信息。

    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

前端路由的实现一般有两种方式:

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>表单属性

<input>通用属性:

通用事件,

剪贴板操作

    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 对象
            }
        }
    });

验证约束