ES6: 迭代器与生成器

什么是迭代器?

迭代器是一个对象,它拥有一个next方法,调用next方法会返回一个对象,该对象有两个属性值,value和done。每次调用next方法,返回的value表示可迭代对象中的下一个值,done表示迭代是否完成。根据此定义,可用下面的代码实现一个迭代器:

 
 
let create_iter = function(items) {
    let i = 0;
    return {
        next: function() {
            let value, done;
            if (i >= items.length) {
                value = null;
                done = true;
            } else {
                value = items[i++];
                done = false;
            }
            return {
                value: value,
                done: done
            }
        }
    }
}

什么是生成器?

用来创建迭代器的函数称之为生成器,ES6为了简化生成器,引入了新的语法:

  • 在生成器函数前加*
  • 使用yield关键字抛出下一个value

引入新的语法后,生成器的代码可以简化为:

 
 
let gen = function*(items) {
    for (let i = 0; i < items.length; i++) {
        yield items[i];
    }
}

举个栗子

下面的代码比较了两种创建迭代器的方法:

 
 
let create_iter = function(items) {
    let i = 0;
    return {
        next: function() {
            let value, done;
            if (i >= items.length) {
                value = null;
                done = true;
            } else {
                value = items[i++];
                done = false;
            }
            return {
                value: value,
                done: done
            }
        }
    }
}
let gen = function*(items) {
    for (let i = 0; i < items.length; i++) {
        yield items[i];
    }
}
let items = [1, 2, 3, 4, 5];
let it = gen(items);
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

输出结果:

 
 
$ node test.js 
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 4, done: false }
{ value: 5, done: false }
{ value: undefined, done: true }
{ value: undefined, done: true }

生成器的表现形式

  • function *my_generator(items) {}
  • let my_generator = function *(items) {}
  • let obj = {*my_generator(items){}};

可迭代对象

可迭代对象指的是包含Symbol.iterator属性的对象,数组、Set、Map、字符串都是可迭代对象,都有默认的迭代器。

可迭代对象可以与for-of配合使用,例如:

 
 
let numbers = [1, 2, 3, 4, 5];
for (let num of numbers) {
    console.log(num);
}

可以用一下方法自定义一个可迭代对象:

 
 
let colors = {
    items: [1, 2, 3, 4, 5],
    *[Symbol.iterator]() {
        for (let item of this.items) {
            yield item;
        }
    }
}

内置迭代器

数组、Map和Set 都有一下三个内置的迭代器:

  • entries(): 返回一个包含键值对(数组)的迭代器
  • values(): 返回一个只包含值的迭代器
  • keys(): 返回一个只包含键的迭代器

对于数组来说,keys返回的是元素的下标,对于Set来说,返回的是元素的值,而对于Map来说,返回的是元素的key。

给迭代器传入参数

当调用next方法时,若传入参数,则该参数会替代上一次yield表达式的值,例如:

 
 
let gen = function *() {
    let a = yield 1;
    let b = yield a + 1;
    yield b + 1;
}
let iter = gen();
console.log(iter.next());
console.log(iter.next(100));
console.log(iter.next(200));
console.log(iter.next());

执行结果:

 
 
$ node test.js 
{ value: 1, done: false }
{ value: 101, done: false }
{ value: 201, done: false }
{ value: undefined, done: true }

单链表反转

实现单链表反转的思路

实现单链表反转的难点在于,如果让当前节点的next指向前驱节点,那么链表就断了,所以解决的办法就是在进行反转操作之前用一个临时指针变量保存后继节点的地址。

单链表反转的代码实现

 
 
#include <stdio.h>
#include <stdlib.h>
typedef struct LIST_NODE {
    int data;
    struct LIST_NODE* next;
} LIST_NODE;
void show_list(LIST_NODE *head) {
    LIST_NODE *p = head->next;
    while(p != NULL) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
}
void reverse_list(LIST_NODE *head) {
    LIST_NODE *p, *q, *tmp;
    p = head;
    q = p->next;
    while (q != NULL) {
        tmp = q->next;
        if (p == head) {
            q->next = NULL;
        } else {
            q->next = p;
        }
        p = q;
        q = tmp;
    }
    head->next = p;
}
int main(int argc, char **argv) {
    LIST_NODE head = {
        .data = 0,
        .next = NULL
    };
    int data;
    LIST_NODE *tail = &head;
    while (1) {
        printf("Please input a number: (0 to stop input)\n");
        scanf("%d", &data);
        if (data == 0) {
            break;
        }
        LIST_NODE *new_node = malloc(sizeof(LIST_NODE));
        new_node->data = data;
        new_node->next = NULL;
        tail->next = new_node;
        tail = new_node;
    }
    show_list(&head);
    reverse_list(&head);
    show_list(&head);
    return 0;
}

冒泡排序、插入法排序及选择排序

冒泡排序、插入法排序以及选择排序是排序算法中比较基础的三种,其平均时间复杂度都是O(n^2)。

原理介绍

冒泡排序

冒泡排序的步骤是:比较相邻两个数,看是否满足大小关系,如果不满足则交换这两个数,使他们满足大小关系,这样可以保证最大(最小)的数始终会向后流动,循环一次之后,最大(最小)的数就会被交换到数组的最后。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。

插入法排序

插入法排序的思路是:把数组分成两个部分:排好序的,和未排序的。开始的时候,数组的第一个元素会被当做拍好序的部分,对其余未排好序的数值进行迭代,将其插入到排好序的部分中合适的位置。

选择法排序

选择法排序和插入法排序类似,都是将数组分为排好序的和未排序的两个部分。不同的是,选择法排序每次迭代都会选择未排序部分中的最小(最大)值,将其插入到排好序部分的队首(队尾)。

C语言实现

需要实现四个函数, 第一个是打印数组,接下来三个函数分别实现三种排序算法:

  • void print_arry(int *a, int len)
  • static inline void insertion_sort(int *a, int len)
  • static inline void bubble_sort(int *a, int len)
  • static inline void selection_sort(int *a, int len)

main函数中,首先需要用户输入指定数目(#define LEN 10)的数值,如果用户输入-1则随机生成这些数值。然后需要用户输入排序算法。输入完成后打印排序前的数组,然后根据相应的排序算法进行排序,最后再打印出排序后的数组。代码如下:

 

 
 
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define LEN 10
#define NR_METHOD 3
#define RAND_RANGE 100
void print_arry(int *a, int len) {
    int i = 0;
    for (int i = 0; i < len; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
}
static inline void insertion_sort(int *a, int len) {
    int tmp = 0;
    int i, j;
    for (i = 1; i < len; i++) {
        tmp = a[i];
        for (j = i - 1; j >= 0; j--) {
            if (a[j] > tmp) {
                a[j + 1] = a[j];
            } else {
                break;
            }
        }
        a[j + 1] = tmp;
    }
}
static inline void bubble_sort(int *a, int len) {
    int i, j;
    int tmp;
    int swapped = 0;
    for (i = len - 1; i >= 0; i--) {
        for (j = 0; j < i; j++) {
            if (a[j] > a[j + 1]) {
                tmp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = tmp;
                swapped = 1;
            }
        }
        if (!swapped) {
            break;
        }
    }
}
static inline void selection_sort(int *a, int len) {
    int i, j, min, tmp;
    for (i = 0; i < len -1; i++) {
        min = i;
        for (j = i + 1; j < len; j++) {
            if(a[j] < a[min]) {
                min = j;
            }
        }
        if (min != i) {
            tmp = a[min];
            a[min] = a[i];
            a[i] = tmp;
        }
    }
}
int main(int argc, char **argv) {
    srand(time(NULL));
    int a[LEN];
    int data;
    for (int i = 0; i < LEN; i++) {
        printf("Please input a number: %d left: (-1 for random numbers)", LEN - i);
        scanf("%d", &data);
        if(data != -1) {
            a[i] = data;
        } else {
            for (int j = 0; j < LEN; j++) {
                a[j] = rand() % RAND_RANGE;
            }
            break;
        }
    }
    while (1) {
        printf("Please select a sort method:\n");
        printf("0: Insertion sort\n");
        printf("1: Bubble sort \n");
        printf("2: Selection sort \n");
        scanf("%d", &data);
        if(data >= 0 && data < NR_METHOD) {
            break;
        } else {
            printf("Please input a valid number!\n");
        }
    }
    printf("Original Array: \n");
    print_arry(a, LEN);
    switch(data) {
        case 0:
        printf("You selected insertion sort\n");
        insertion_sort(a, LEN);
        break;
        case 1:
        printf("You selected bubble sort\n");
        bubble_sort(a, LEN);
        break;
        case 2:
        printf("You selected selection sort\n");
        selection_sort(a, LEN);
        break;
    }
    printf("Sorted Array: \n");
    print_arry(a, LEN);
    return 0;
}

用单链表实现LRU缓存置换算法

缓存置换算法所解决的问题

在存储系统的金字塔结构中,缓存的存取速度比内存快,然而成本比内存高,所以缓存的容量有限。缓存置换算法所要解决的问题便是在容量有限的缓存中,存放哪些数据可以提升缓存命中率。

 

LRU缓存置换算法的核心思想

LRU算法认为最近访问过的数据被再次访问的可能性最大,所以缓存中存放的是最近使用过的数据。具体的做法是:

  • 把缓存当做一个队列,队首的数据是最近被访问的数据,而队尾的数据则是即将被置换出缓存的数据。
  • 每当有新数据被访问时,会在队列中查找该数据,如果存在,就被该数据挪到队首。
  • 如果被访问的数据没有在队列(缓存)中,而且缓存未满,则该数据被放到队首。
  • 如果被访问的数据没有在队列中,然而缓存已经满了,则把队尾的数据从队列中删除,再把新数据放置到队首。

 

用C语言实现LRU缓存置换算法

 
 
#include <stdio.h>
#include <stdlib.h>
#define CACHE_SIZE 20
int nr_of_list = 0;
typedef struct CACHE_ITEM {
    int data;
    struct CACHE_ITEM *next;
} CACHE_ITEM;
CACHE_ITEM list_head;
int init_list_head(CACHE_ITEM *head) {
    if (!head) {
        printf("%s: parameter error - no list head\n", __func__);
        return -1;
    }
    head->data = 0;
    head->next = NULL;
    return 0;
}
int insert_node(CACHE_ITEM *head, CACHE_ITEM *node) {
    if (!head || !node) {
        printf("%s: parameter error - no list head or node\n", __func__);
        return -1;
    }
    node->next = head->next;
    head->next = node;
    nr_of_list += 1;
    return 0;
}
int remove_node(CACHE_ITEM *head, CACHE_ITEM *node) {
    if (!head || !node) {
        printf("%s: parameter error - no list head or node\n", __func__);
        return -1;
    }
    CACHE_ITEM *p = head;
    while(p->next) {
        if(p->next == node) {
            p->next = node->next;
               nr_of_list -= 1;
           } else {
            p = p->next;
        }
    }
    return 0;
}
int remove_tail_node(CACHE_ITEM *head) {
    if (!head) {
        printf("%s: parameter error - no list head\n", __func__);
        return -1;
    }
    CACHE_ITEM *p = head;
    while(p->next) {
        if(p->next && !p->next->next) {
            p->next = NULL;
            nr_of_list -= 1;
        } else {
            p = p->next;
        }
    }
    return 0;
}
CACHE_ITEM *search_list(CACHE_ITEM *head, int number) {
    if (!head) {
        printf("%s: parameter error - no list head\n", __func__);
        return NULL;
    }
    CACHE_ITEM *p = head->next;
    CACHE_ITEM *q = NULL;
    while(p) {
        if(p->data == number) {
            q = p;
            break;
        } else {
            p = p->next;
        }
    }
    return q;
}
void show_list(CACHE_ITEM *head) {
    CACHE_ITEM *p = head->next;
    while(p) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
    printf("List Length: %d\n", nr_of_list);
}
int lru(CACHE_ITEM *head, int number) {
    if (!head) {
        printf("%s: parameter error - no list head\n", __func__);
        return -1;
    }
    CACHE_ITEM *p = search_list(head, number);
    if (p) {
        remove_node(head, p);
        insert_node(head, p);
    } else if (nr_of_list < CACHE_SIZE) {
        CACHE_ITEM *new_node = (CACHE_ITEM *)malloc(sizeof(CACHE_ITEM));
        new_node->data = number;
        insert_node(head, new_node);
    } else {
        remove_tail_node(head);
        CACHE_ITEM *new_node = (CACHE_ITEM *)malloc(sizeof(CACHE_ITEM));
        new_node->data = number;
        insert_node(head, new_node);
    }
    return 0;
}
int main(int argc, char **argv) {
    CACHE_ITEM *head = &list_head;
    init_list_head(head);
    int num = 0;
    while(1) {
        printf("请输入数字:\n");
        scanf("%d", &num);
        lru(head, num);
        show_list(head);
    }
    return 0;
}

运行结果

 
 
请输入数字:
1
1
List Length: 1
请输入数字:
2
2 1
List Length: 2
请输入数字:
3
3 2 1
List Length: 3
请输入数字:
4
4 3 2 1
List Length: 4
请输入数字:
5
5 4 3 2 1
List Length: 5
请输入数字:
3
3 5 4 2 1
List Length: 5
请输入数字:
1
1 3 5 4 2
List Length: 5
请输入数字:
6
6 1 3 5 4 2
List Length: 6
请输入数字:
7
7 6 1 3 5 4 2
List Length: 7
请输入数字:
8
8 7 6 1 3 5 4 2
List Length: 8
请输入数字:
9
9 8 7 6 1 3 5 4 2
List Length: 9
请输入数字:
10
10 9 8 7 6 1 3 5 4 2
List Length: 10
请输入数字:
11
11 10 9 8 7 6 1 3 5 4 2
List Length: 11
请输入数字:
12
12 11 10 9 8 7 6 1 3 5 4 2
List Length: 12
请输入数字:
13
13 12 11 10 9 8 7 6 1 3 5 4 2
List Length: 13
请输入数字:
14
14 13 12 11 10 9 8 7 6 1 3 5 4 2
List Length: 14
请输入数字:
15
15 14 13 12 11 10 9 8 7 6 1 3 5 4 2
List Length: 15
请输入数字:
16
16 15 14 13 12 11 10 9 8 7 6 1 3 5 4 2
List Length: 16
请输入数字:
17
17 16 15 14 13 12 11 10 9 8 7 6 1 3 5 4 2
List Length: 17
请输入数字:
18
18 17 16 15 14 13 12 11 10 9 8 7 6 1 3 5 4 2
List Length: 18
请输入数字:
19
19 18 17 16 15 14 13 12 11 10 9 8 7 6 1 3 5 4 2
List Length: 19
请输入数字:
20
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 1 3 5 4 2
List Length: 20
请输入数字:
21
21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 1 3 5 4
List Length: 20
请输入数字:
22
22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 1 3 5
List Length: 20
请输入数字:
23
23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 1 3
List Length: 20
请输入数字:
1
1 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 3
List Length: 20
请输入数字:
4
4 1 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6
List Length: 20

ES6: Set 与 Map

Set

  • let my_set = new Set();
  • let my_set = new Set([1, 1, 2, 2]);
  • my_set.add(5);
  • my_set.delete(5);
  • my_set.has(5);
  • my_set.forEach(function(value){});
  • let my_set = WeakSet() // 只允许对象作为set的元素,便于垃圾回收

Map

  • let my_map = new Map();
  • let map = new Map([[“name”, “Nicholas”], [“age”, 25]]);
  • my_map.set(key, value);
  • my_map.get(key)
  • my_map.has(key)
  • my_map.delete(key);
  • my_map.clear();
  • my_map.size;
  • my_map.forEach(function(value, key, ownerMap) {
    console.log(key + ” ” + value);
    console.log(ownerMap === map);
    });
  • let map = new WeakMap();

ES6: 符号类型

引入原因:

  • 实现私有属性
     
     
    let s = Symbol()
    let obj = {[s]: 'hello world'};
    console.log(obj.s); //undefined
  • 符号值唯一
     
     
    let s = Symbol()
    let m = Symbol()
    console.log(s === m)

创建:

  • 不能用字面量形式创建
  • 使用Symbol(desc)函数创建,不需要添加new
  • Symbol.for(key)在全局符号注册表中查找key符号值,如果找到就返回,没找到就创建一个新的符号值
  • Symbol.keyFor(symbol): 返回符号值的key

枚举:

  • 符号类型的key是不能被枚举的,Object.keys()以及Object.getOwnPropertyNames()都不能返回符号类型的key
  • Object.getOwnPropertySymbols()则会返回对象中所有符号类型的key

共享符号值:

  • Symbol.for(desc): 在全局符号注册表中查找描述为desc的符号,如果找到,返回这个符号值,如果没有,则创建一个新的符号值并返回
  • Symbol.keyFor(var): 返回全局符号注册表中符号值为var的desc.

转换:

  • 不能强制转化为字符串或者数值类型, 所以 symbol + “hello” 或者 symbol/1 会报错
  • 可以调用String()来转换

知名符号:

  • Symbol.hasInstance(obj): 判断obj是不是但前函数的实例,如Array[Symbol.hasInstance](obj); 可以通过以下代码改变instanceof的默认行为:
     
     
        Object.defineProperty(MyObject, Symbol.hasInstance, {
            value: function(v) {
                return false;
            }
        });
  • Symbol.isConcatSpreadable
     
     
    let collection = {
    0: "Hello",
    1: "world",
    length: 2,
    [Symbol.isConcatSpreadable]: true
    };
    let messages = [ "Hi" ].concat(collection);
    console.log(messages.length); // 3
    console.log(messages); // ["hi","Hello","world"]
  • Symbol.match 、 Symbol.replace 、 Symbol.search 、Symbol.split
  • Symbol.toPrimitive
     
     
    function Temperature(degrees) {
        this.degrees = degrees;
    }
    Temperature.prototype[Symbol.toPrimitive] = function(hint) {
        switch (hint) {
        case "string":
            return this.degrees + "\u00b0"; // 温度符号
        case "number":
            return this.degrees;
        case "default":
            return this.degrees + " degrees";
        }
    };
    let freezing = new Temperature(32);
    console.log(freezing + "!"); // "32 degrees!"
    console.log(freezing / 2); // 16
    console.log(String(freezing)); // "32°"
  • Symbol.toStringTag
     
     
    > age[Symbol.toStringTag] = 'Node';
    'Node'
    > age
    { age: 32,
    [Symbol(Symbol.toPrimitive)]: [Function],
    [Symbol(Symbol.toStringTag)]: 'Node' }
    > age.toString();
    '[object Node]'

ES6: 对象和数组解构

所谓解构,指的是将数据结构分解为更小的部分,从而使数据提取变得容易。

对象解构:

  • 使用解构时,必须提供初始化值

    [code lang=”js” collapse=”false”]
    let Person = {
    name: ‘sen’,
    age: 18
    }
    let {name, age} = Person;
    [/code]

  • 解构表达式的值为=右侧的值
  • 如果对象中没有同名属性时,解构表达式新赋值的变量的值为undefined
  • 解构对象只是赋值时,需要加()
  • 赋值给不同名的变量

    [code lang=”js” collapse=”false”]
    let Person = {
    name: ‘sen’,
    age: 18
    }
    let {name: localName, age: localAge} = Person;
    [/code]

  • 设置默认值

    [code lang=”js” collapse=”false”]
    let Person = {
    name: ‘sen’,
    age: 18
    }
    let {name, age: localAge = 18} = Person;
    [/code]

  • 嵌套解构

    [code lang=”js” collapse=”false”]
    let Person = {
    name: ‘sen’,
    age: 18,
    score: {
    maths: 100
    }
    }
    let {name, score: {maths}} = Person;
    console.log(maths);
    [/code]

数组解构

  • [code lang=”js” collapse=”false”]
    let score = [99, 88, 77];
    let [maths, english, chinese] = score;
    [,,chinese] = score;
    [/code]
  • 数组解构赋值不需要加()
    [code lang=”js” collapse=”false”]
    let a = 3;
    let b = 4;
    [a, b] = [b, a]
    [/code]
  • 嵌套的解构
    [code lang=”js” collapse=”false”]
    let score = [99, 88, [77]];
    let [,,[chinese]] = score;
    [/code]
  • 剩余项
    [code lang=”js” collapse=”false”]
    let score = [99, 88, 77];
    let [maths, …restScore] = score;
    console.log(restScore) // [88, 77]
    [/code]
  • 数组和对象可以混合解构
    [code lang=”js” collapse=”false”]
    let Person = {
    name: ‘sen’,
    age: [0, 18]
    }
    let {name: localName, age: [start]} = Person;
    console.log(start);
    [/code]

ES6: 对象扩展

  • 初始化简写:

    [code lang=”js” collapse=”false”]
    function createPerson(name, age) {
    return { name: name, age: age };
    }
    —&amp;amp;amp;amp;gt;
    function createPerson(name, age) {
    return { name, age };
    }

    [/code]

  • 方法简写:

    [code lang=”js” collapse=”false”]
    var person = {
    name: "sen",
    sayName: function() {
    return this.name;
    }
    }
    —&amp;amp;amp;gt;
    var person = {
    name: "sen",
    sayName() {
    return this.name;
    }
    }
    [/code]

  • 需要计算的属性名用[]表示
  • Object.is() 与===表现相同,除了
    Object.is(NaN, NaN) // true
    Object.is(+0, -0) // false
  • Object.assign(target, source) 将source中的属性和方法混入target对象
  • 允许重发的属性定义,排在最后的为实际值
  • 属性枚举的顺序:
    • 所有数字类型的键按升序排列
    • 所有字符串类型的键按添加进对象的顺序排列
    • 所有符号类型的键也按照添加进对象的顺序排列
  • 可修改对象的原型:
    Object.setPrototypeOf(obj, new_prototype)
  • 使用super作为指向原型对象的指针

ES6: 函数扩展

  • 带默认值的函数:var get_name = function(url, id=1, callback){};
    • 如果传入了第二个参数,将不会使用默认值
    • 如果给第二个参数赋值为undefined,会使用默认值
    • 带有默认值的参数后,arguments的内容将不会随着形参的改变而改变
    • 排在后面的参数可以将前面的参数作为默认值,而前面的参数不能引用后面的参数作为默认值
  • 剩余参数:var get_name = function(url, …keys)
    • 除了第一个参数url外,剩余所有参数都会被放到keys数组中
    • 函数只能有一个剩余参数,且必须放到最后
    • 剩余参数不能再对象字面量的setter属性中使用
  • 延展运算符:var args = [‘url’, 123, ‘st’]; get_names(…args);
  • new.target: 使用new创建一个对象时,会被赋值为新对象的构造器
  • ES6允许在代码块中定义函数,在严格模式中,块级函数只能在块级作用域中使用,而非严格模式中,块级函数会被提升到全局
  • 箭头函数
    • 没有this,super,arguments,new.target,这些值由所在的、最靠近的非箭头函数来决定
    • 不能使用new来调用
    • 没有原型
    • 不能更改this
    • 不允许有重复的具名参数
    • 语法
      • var reflect = value => value;
      • var sum = (num1, num2) => num1 + num2;
      • var getName = () => ‘Nicholas’;
      • var sum = (numb1, num2) => {return num1 + num2};
      • var doSomething = () => {};
      • var getItem = (id) => ({id: id, name: ‘Nicholas’});
      • var person = ((name) => {return {getName: function() {return name}};})(‘Nicholas’);
  • 尾调用优化:满足以下条件的尾调用将会被优化,在尾调用时不会创建新的栈帧,而是清除当前栈帧并再次利用它
    • 尾调用不能引用当前栈帧中的变量(意味着该函数不能是闭包)
    • 进行尾调用的函数在尾调用返回结果后不能做额外操作
    • 尾调用的结果作为当前函数的返回值

ES6: 字符串处理

UTF-16编码:

  • str.codePointAt(index)
  • String.fromCodePoint(codepoint);
  • normalize()

正则表达式

  • 新增u修饰符用来处理UTF-16编码的问题  /&.$/u
  • 新增pattern.flags 属性 // ‘ig’
  • 新增pattern.sticky 属性及’y’修饰符 // 只从lastIndex处匹配,如果和’g’一起存在则’g’被忽略
  • 复制正则表达式  // let pattern = new RegEX(old_pattern, ‘gi’)

字符串处理

  • str.includes(sub_string) // true or false
  • str.startsWith(sub_string)  // true or false
  • str.endsWith(sub_string) // true or false
  • str.repeat(times);  // str * times
  • 模板字面量
    • 支持多行字符串
    • 支持变量  // `hello ${name}`

ES6: 块级作用域

var

  • 没有块级作用域的概念
  • 会被提升到定义函数的顶部
  • 可以重复声明

let

  • 有块级作用域
  • 不会被提升
  • 禁止重复声明

const

  • 有块级作用域
  • 不会被提升
  • 禁止重复声明
  • 其值不可更改
  • 必须进行初始化

Javascript中的表单

var form = document.getElementById(‘myform’);

  • form.acceptCharset 服务器能处理的字符集
  • form.action 接受请求的URL
  • form.elements 表单中的所有控件
  • form.enctype 编码类型
  • form.length 控件数量
  • form.method HTTP请求类型
  • form.name   // document.forms[form.name]
  • form.reset()
  • form.submit()
  • form.target

var text = form.elements[‘sex’];

  • text.disabled
  • text.form
  • text.name
  • text.value
  • text.readOnly
  • text.tabIndex
  • text.type
  • text.select()
  • text.setSelectionRange(start, end)
  • text.value.substring(text.selectionStart, text.selectionEnd)

text的事件:

  • change
  • focus
  • blur

剪贴板

var clipboard = window.clipboardData ; //event.clipboardData;

  • clipboard.getData()
  • clipboard.setData()
  • clipboard.clearData()

剪贴板事件

  • beforecopy
  • copy
  • beforecut
  • cut
  • beforepaste
  • paste

 

Javascript中的事件

事件捕获(capturing)和事件冒泡(bubbling)

添加事件处理程序的两种方法

  • <div onclick=”alert(‘ddd’)”></div>  // onclick=null
  • elem.addEventListener(“click”, e_func, false); //elem.removeEventListener(‘click’, e_func, false); //false 表明在冒泡阶段被触发

事件对象

  • e.bubbles // true or false 是否冒泡
  • e.cancelable // true of false 是否可取消默认行为
  • e.currentTarget // 正在处理事件的元素,事件处理程序中的this指向该元素
  • e.target // 事件发生的目标元素
  • e.defaultPrevented // true or false 默认行为是否被取消
  • e.detail
  • e.eventPhase // 1 捕获阶段 2 处于目标 3冒泡阶段
  • e.preventDefault()
  • e.stopImmediatePropagation() //阻止事件冒泡和所有事件处理程序
  • e.stopPropagation() //阻止事件冒泡
  • e.trusted // true 表示事件由浏览器生成,false表示有javascript触发
  • e.type
  • e.view
  • e.clientX e.clientY 事件发生时鼠标的视图位置
  • e.pageX e.pageY 事件发生时鼠标的页面位置
  • e.screenX e.screenY 事件发生时鼠标的屏幕位置
  • e.ctrlKey e.shiftKey e.altKey e.metaKey 事件发生时修饰键是否被按下
  • e.button // 0 主键 1 滚轮键 2右键 被按下
  • e.wheelDelta
  • e.charCode
  • e.keyCode
  • e.data //textinput 事件时输入的字符
  • e.iputMethod //0-9 输入来源,如键盘、粘贴、拖放等

事件类型

  • UI事件
    • load  (window, img)
    • unload
    • resize (window)
    • scroll (window)
  • 焦点事件
    • blur 失去焦点事触发,不会冒泡
    • focusin
    • focusout
    • focus 不会冒泡
  • 鼠标与滚轮事件
    • click
    • dbclick
    • mousedown 任何鼠标按键被按下时触发
    • mouseup
    • mouseenter 不冒泡
    • mouseover 冒泡
    • mousemove
    • mouseleave 不冒泡
    • mouseout 冒泡
    • mousewheel
    • contextmenu 鼠标右键弹出菜单
  • 键盘事件
    • keydown
    • keypress
    • keyup
    • textinput
  • DOM变动事件
    • DOMSubtreeModified
    • DOMNodeInserted
    • DOMNodeRemoved
    • DOMNodeInsertedIntoDocument
    • DOMNodeRemovedFromDocument
    • DOMAttrModified
    • DOMCharacterDataModified

事件委托和移除

为提升性能,利用事件冒泡来减少事件处理程序;在DOM元素被innerHTML移除后,需要手动的移除事件处理程序

事件模拟

  • var e = document.createEvent(‘MouseEvents’);
  • e.initMouseEvent(….);
  • elem.dispatchEvent(e);

DOM中的选择符API及元素遍历

选择符API

  • document.querySelector(css_selector)
    var checked_radio = document.querySelector(‘.checked’);
  • var node_list = document.querySelector(css_selector);
  • elem.matchesSelector(css_selector) // true or false

HTML 5中的类名操作

classList是DOMTokenList的实例,因而具有以下方法:

  • elem.classList.add(value); 如果已经有了就不添加了
  • elem.classList.contain(value) //true or false
  • elem.classList.remove(value)
  • elem.classList.toggle(value); 如果有就删除,如果没有就添加

 元素遍历

Element新增5个属性:

  • childElementCount: 子元素的个数(不包括Text和Comment类型)
  • firstElementChild
  • lastElementChild
  • previousElementSibling
  • nextElementSibling

Javascript中的DOM节点类型

Node类型

  • node.nodeType:
    • Node.ELEMENT_NODE(1);
    • Node.ATTRIBUTE_NODE(2);
    • Node.TEXT_NODE(3);
    • Node.CDATA_SECTION_NODE(4);
    • Node.ENTITY_PREFERENCE_NODE(5);
    • Node.ENTITY_NODE(6);
    • Node.PROCESSING_INSTRUCTION_NODE(7);
    • Node.COMMENT_NODE(8);
    • Node.DOCUMENT_NODE(9);
    • Node.DOCUMENT_TYPE_NODE(10);
    • Node.DOCUMENT_FRAGMENT_NODE(11);
    • Node.NOTATION_NODE(12);
  • node.nodeName: 标签名
  • node.childNodes[0]  node.childNodes.item(0);
  • node.childNodes.length;
  • node.parentNode;
  • node.nextSibling;
  • node.previousSibling;
  • node.firstChild;
  • node.lastChild;
  • node.appendChild(newNode);
  • node.insertBefore(newNode);
  • node.replaceChild(newNode, oldNode);
  • node.removeChild(node);
  • node.cloneNode(true//深复制)

HTMLDocument类型

  • document.nodeType = 9
  • document.nodeName = “#document”
  • document.nodeValue = null
  • document.parentNode = null
  • document.ownerDocument = null
  • document.firstChild === document.childNodes[0] === document.documentElement 都指向<html>元素
  • document.body指向<body>元素
  • document.head
  • document.charset
  • document.URL;
  • document.title;
  • document.domain;
  • document.getElementById();
  • document.getElementByClassName();
  • document.getElementsByTagName();
  • document.getElementsByName();
  • document.images;
  • document.anchors;
  • document.links;
  • document.forms;
  • document.createElement(‘div’)
  • document.activeElement: 指向获得焦点的元素
  • document.readyState // loading or complete

Element类型

var elem = document.getElementById(‘elem_id’);

  • elem.nodeType = 1
  • elem.nodeName 为元素的标签名
  • elem.nodeValue = null
  • elem.parentNode 为document或者Element
  • elem.nodeName === elem.tagName
  • elem.getAttribute(“id”)
  • elem.setAttribute(“id”, “new_id”)
  • elem.attributes.getNamedItem(‘id’)
  • elem.attributes.removeNamedItem(‘id’)
  • elem.attributes.setNamedItem(new_attr)
  • elem.getElementsByTagName()
  • elem.dataset // data- 前缀自定义的属性及属性值
  • elem.innerHTML // 元素的内容(所有子节点HTML)
  • elem.scrollIntoView() //将元素滚动至可见视图中
  • elem.style.width
  • elem.style

Text类型

<div>hello world</dev>

var text_node = div.firstChild;

var text_node = document.createTextNode(text);

  • text_node.nodeValue = text_node.data = ‘hello world’
  • text_node.appendData(‘string’);
  • text_node.deleteData(offset, count);
  • text_node.insertData(offset, text);
  • text_node.replaceData(offset, count, new_text);
  • text_node.splitText(offset);
  • text_node.substringData(offset);
  • text_node.length = text_node.data.length = text_node.nodeValue.length;
  • text_node.parentNode.normalize() // 将两个子文本节点合并

Comment类型

<div id=’mydiv’><!– comment –></div>

var mydiv = document.getElementById(‘mydiv’);

var comment = mydiv.firstChild;

var comment = document.createComment(‘comment’);

  • comment类型和Text类型继承自同一个基类,拥有除splitText之外Text的所有属性和方法

Attr类型

elem.attributes中的节点

var attr = document.createAttribute(attr_name);

  • attr.nodeType = 11
  • attr.nodeName 属性名
  • attr.nodeValue 属性值
  • attr.parentNode = null
  • elem.setAttributeNode(attr);

Javascript中的location对象

  • window.location === document.location // true
  • location的属性列表
  • 修改location的属性会导致页面刷新并产生新的历史记录
  • window.location = “URL” 和 location.href=”URL”效果一样,都会调用location.assign(URL)
  • location.replace(URL)不会产生新的历史记录,而且会禁用后退
  • location.reload(true): 重新加载页面,参数可以选择是否从服务器加载(而不是从缓存加载)

Javascript中的window对象

window对象的两个作用:

  • 表示浏览器的一个实例
  • ECMAscript中的Global对象
    直接定义Global变量与在window上定义Global变量的区别是:直接定义的Global变量的[[configurable]]特性为false,也就是说,它不能用delete删除

窗口关系及框架

  • 如果html中包含框架(frame),那么每个框架都有自己的window对象,它们从上到下,从左到右,依次存放于window.frames数组中。可以通过window.frames[index]或者window.frames[frame_name]来获取frame的window对象
  • window.name为frame的name
  • top对象表示最外层html的window对象
  • parent对象表示上层frame的window对象
  • self表示frame自身的window对象

窗口位置

  • window.screenLeft window.screenTop 或者firefox中的window.screenX window.screenY表示窗口左上角的位置
     
     
        var left = (typeof window.screenLeft == "number") ? window.screenLeft: window.screenX;
        var top = (typeof window.screenTop == "number") ? window.screenTop: window.screenY;
       
  • window.outerHight window.outerWidth 表示窗口的高和宽
  • window.innerHight window.innerWidth 表示页面视图的高和宽

打开新窗口

  • window.open(URL, target, args, is_replace)
    URL: 新窗口打开的网址
    target: 目标frame的名字,或者_self, _parent,_top,_blank
    args: is_replace: 是否替代当前窗口
  • window.close() : 关闭一个窗口
  • window.closed: 布尔值,window是否已经关闭
  • window.opener: 表示打开本窗口的窗口window对象

超时调用与间歇调用

  • var id = setTimeout(function, ms)  ; clearTimeout(id);
  • var id = setInterval(function, ms) ; clearInterval(id);

系统对话框

  • alert(‘string’)
  • var boolean = confirm(“string”);
  • var result_string = prompt(“string”, “default_answer”);
"