Sass 概要

前端开发中最大的坑之一莫过于写css,流水账式的写法让众多码农们头痛不已。好在有了sass,写css不再死板。sass对css的增强如下:

宏定义

宏定义的优点在于一处定义,多处使用,需要修改的时候只需要修改定义的地方即可。虽然sass没有明确的说明,但其实以下几种语法与C语言中的宏定义非常类似:

  1. 变量
    sass中的变量适用于替换css中参数的值。例如:

     
     
    scss:
    $basic-margin: "10px 20px";
    #box01 {
      margin: $basic-margin;
    }
    #box02 {
      margin: $basic-margin;
    }
    css:
    #box01 {
      margin: "10px 20px";
    }
    #box02 {
      margin: "10px 20px";
    }

    sass允许根据变量,选择性的输出css,类似于开关,例如:

     
     
    scss:
    $rounded-corners: false;
    .button {
      border: 1px solid black;
      border-radius: if($rounded-corners, 5px, null);
    }
    css:
    .button {
      border: 1px solid black;
    }

    sass 也允许在子模块中给变量设置默认值,引用的时候可以再重新定义变量的值

     
     
    _module.scss:
    $userColor: red !default;
    .bass {
      padding: 0 20px;
      color: $userColor;
    }
    test.scss:
    $userColor: black;
    @import "module";
    test.css:
    .bass {
      padding: 0 20px;
      color: black;
    }
     
     
    test.scss:
    @import "module";
    test.css
    .bass {
      padding: 0 20px;
      color: red;
    }

    使用!global可以在局部环境中设置全局变量的值,例如:

     
     
    test.scss:
    $color: red;
    .text {
      $color: black !global;
    }
    .box {
      color: $color;
    }
    test.css:
    .box {
      color: black;
    }
  2. mixin
    sass中的mixin类似于支持参数的代码片段,可以很方便的将一段常用的代码片段插入到css规则中去,例如:

     
     
    scss:
    @mixin normal-font($fontfamily) {
      font-size: 18px;
      font-family: $fontfamily;
    }
    .box {
      width: 200px;
      height: 200px;
      @include normal-font("IBM Plex Sans");
    }
    css:
    .box {
      width: 200px;
      height: 200px;
      font-size: 18px;
      font-family: "IBM Plex Sans";
    }
  3. 扩展与继承
    sass中的扩展相当于不带参数的代码片段,适用于同一组件的不同状态,语法如下:

     
     
    scss:
    %message-shared {
      border: 1px solid #ccc;
      padding: 10px;
      color: #333;
    }
    .message {
      @extend %message-shared;
    }
    .success {
      @extend %message-shared;
      border-color: green;
    }
    .error {
      @extend %message-shared;
      border-color: red;
    }
    .warning {
      @extend %message-shared;
      border-color: yellow;
    }
    css:
    .warning, .error, .success, .message {
      border: 1px solid #ccc;
      padding: 10px;
      color: #333;
    }
    .success {
      border-color: green;
    }
    .error {
      border-color: red;
    }
    .warning {
      border-color: yellow;
    }

     

模块化

sass也借鉴了编程语言中的模块化思想,允许文件引入。以下划线开头的文件类似于子模块,不会被被sass编译为css,只能被其他scss文件引用。例如:

 
 
_module.scss:
.bass {
  padding: 0 20px;
}
test.scss:
@import "module";
$basic-margin: "10px 20px";
#box01 {
  margin: $basic-margin;
}
#box02 {
  margin: $basic-margin;
}
test.css:
.bass {
  padding: 0 20px;
}
#box01 {
  margin: "10px 20px";
}
#box02 {
  margin: "10px 20px";
}

语法简化

scss也对css的语法做了一些简化,比如说:

  1. 嵌套
    写scss子元素的规则不再另起一条规则,只需要嵌套在父元素中的规则中即可,例如:

     
     
    scss:
    $basic-margin: "10px 20px";
    .bss {
      margin: $basic-margin;
      #dash {
        margin-top: 20px;
        padding-top: 20px;
      }
      .dash {
        margin-top: 20px;
        margin-bottom: 20px;
      }
    }
    css:
    .bss {
      margin: "10px 20px";
    }
    .bss #dash {
      margin-top: 20px;
      padding-top: 20px;
    }
    .bss .dash {
      margin-top: 20px;
      margin-bottom: 20px;
    }

    另一种方式的嵌套:

     
     
    scss:
    .box {
      margin: {
        top: 20px;
        bottom: 10px;
        right: 10px;
        left: 20px;
      }
    }
    css:
    .box {
      margin-top: 20px;
      margin-bottom: 10px;
      margin-right: 10px;
      margin-left: 20px;
    }

可编程化

sass也做了一些工作让css更像一门编程语言而不是一遍作文。其中包括:

  1. 支持运算
    css是不支持运算的,而在scss中可以做一些简单的运算,例如:

     
     
    scss:
    .box {
      width: 100px / 200px * 100%;
    }
    css:
    .box {
      width: 50%;
    }
  2. 数值类型
    scss中的值分为以下几种类型:

    • 数字,例如: 20, 20px
    • 字符串,例如:”IBM Plex Sans”, bold
    • 颜色值,例如:#ffffff, blue
    • 布尔值, true, false
    • 列表,例如:0 20px 30px 40px
    • 字典,例如:(“background”: red, “foreground”: pink)
  3. 操作符
    scss中的操作符包括:

    • == , !=  : 判断两个值是否相等/不相等
    • + – * / %
    • < <= > >=
    • and or not
    • + – / 可用于字符串拼接
    • () 用于优先级设定
    • &父元素选择器
    • #{} 可以将sass表达式插入到css的文本中
  4. 代码注释
    • // 单行注释,不会编译到css中
    • /**/多行注释,一般会被编译进css
    • 压缩模式下,多行注释不会被编译进css,除非以/*!开头
    • ///为文档注释,不会被编译到css中,会被sassdoc工具使用,生成sass的文档
  5. 函数
    scss中的函数主要用于数值计算,例如:

     
     
    scss:
    @function pow($base, $exponent) {
      $result: 1;
      @for $_ from 1 through $exponent {
        $result: $result * $base;
      }
      @return $result;
    }
    .sidebar {
      float: left;
      margin-left: pow(4, 3) * 1px;
    }
    css:
    .sidebar {
      float: left;
      margin-left: 64px;
    }

    Sass中的内建函数详见 https://sass-lang.com/documentation/functions

  6.  流程控制
    • 分支 @if , @else, @else if
      例如:

       
       
      scss:
      @mixin triangle($color, $size, $direction) {
        display: block;
        height: 0;
        width: 0;
        border: $size/2 solid transparent;
        @if $direction == up {
          border-bottom-color: $color;
        } @else if $direction == down {
          border-top-color: $color;
        } @else if $direction == left {
          border-right-color: $color;
        } @else if $direction == right {
          border-left-color: $color;
        } @else {
          @error "wrong direction: #{$direction}";
        }
      }
      .next {
        @include triangle(green, 20px, right);
      }
      css:
      .next {
        display: block;
        height: 0;
        width: 0;
        border: 10px solid transparent;
        border-left-color: green;
      }
    • 循环 @each @for @while
      例如:

       
       
      scss:
      $sizes: 40px, 50px, 80px;
      @each $size in $sizes {
        .icon-#{$size} {
          font-size: $size;
          height: $size;
          width: $size;
        }
      }
      css:
      .icon-40px {
        font-size: 40px;
        height: 40px;
        width: 40px;
      }
      .icon-50px {
        font-size: 50px;
        height: 50px;
        width: 50px;
      }
      .icon-80px {
        font-size: 80px;
        height: 80px;
        width: 80px;
      }
       
       
      scss:
      $base-color: #036;
      @for $i from 1 through 3 {
        ul:nth-child(3n + #{$i}) {
          background-color: lighten($base-color, $i * 5%);
        }
      }
      css:
      ul:nth-child(3n + 1) {
        background-color: #004080;
      }
      ul:nth-child(3n + 2) {
        background-color: #004d99;
      }
      ul:nth-child(3n + 3) {
        background-color: #0059b3;
      }
       
       
      1. scss:
      2. @function scale-below($value, $base, $ratio: 1.618) {
      3. @while $value > $base {
      4. $value: $value / $ratio;
      5. }
      6. @return $value;
      7. }
      8. $normal-font-size: 16px;
      9. sup {
      10. font-size: scale-below(20px, 16px);
      11. }
      12. css:
      13. sup {
      14. font-size: 12.36094px;
      15. }
  7. 数值类型
    sass中的数值类型包括以下几种:数值,字符串,颜色,List, Map,布尔值,null及函数

    • 数值包含数字和单位,sass的强大之处在于支持带单位的运算,例如:
       
       
      @debug 1in + 6px; // 102px or 1.0625in
    • list和map的用法举例:
       
       
      $prefixes-by-browser: ("firefox": moz, "safari": webkit, "ie": ms);
      @function prefixes-for-browsers($browsers) {
        $prefixes: ();
        @each $browser in $browsers {
          $prefixes: append($prefixes, map-get($prefixes-by-browser, $browser));
        }
        @return $prefixes;
      }
      @debug prefixes-for-browsers("firefox" "ie"); // moz ms

获取隐藏元素的宽度

问题描述

如果DOM元素的fu’yu是被隐藏的(display: none),那么无论使用DOM的接口,还是jquery的接口来获取该元素的宽度,得到的结果始终是0. 例如:

 
 
<div id="node">
  <p>
    hello world
  </p>
</div>
 
 
#node {
    display: none;
}
 
 
alert($('#node p').width());

解决方法

在过去元素的宽度之前,临时修改元素的display为block,获取之后再讲其设置回none

例如:

 
 
function get_width(obj) {
  var width = 0;
  obj.parent().css('display', 'block');
  width = obj.width();
  obj.parent().css('display', 'none');
  return width;
}
alert(get_width($('#node p')));

给jquery添加一个函数

参考StackOverflow ,Tim Banks给jquery添加了一个函数,用来获取隐藏元素的宽度和高度信息。

 
 
(function ($) {
$.fn.getHiddenDimensions = function (include_margin) {
    var $item = this,
    props = { position: 'absolute', visibility: 'hidden', display: 'block' },
    dim = { width: 0, height: 0, innerWidth: 0, innerHeight: 0, outerWidth: 0, outerHeight: 0 },
    $hiddenParents = $item.parents().addBack().not(':visible'),
    includeMargin = (include_margin == null) ? false : include_margin;
    var oldProps = [];
    $hiddenParents.each(function () {
        var old = {};
        for (var name in props) {
            old[name] = this.style[name];
            this.style[name] = props[name];
        }
        oldProps.push(old);
    });
    dim.width = $item.width();
    dim.outerWidth = $item.outerWidth(includeMargin);
    dim.innerWidth = $item.innerWidth();
    dim.height = $item.height();
    dim.innerHeight = $item.innerHeight();
    dim.outerHeight = $item.outerHeight(includeMargin);
    $hiddenParents.each(function (i) {
        var old = oldProps[i];
        for (var name in props) {
            this.style[name] = old[name];
        }
    });
    return dim;
}
}($));
alert($('#node p').getHiddenDimensions().width);

在jsFiddle上试一试 ->

ES6: 模块编程

Javascript模块的限制

  • 只能运行于严格模式
  • 模块中的顶级作用域中的变量,不会被自动添加到全局作用域
  • 顶级作用域的this为undefined

导出

如果想让模块中的变量、函数、类被其他模块使用,需要将其导出,导出的方法如下:

  • export var color = “red”;
  • export function print_hello(){};
  • export { print_hello }
  • export { print_hello as printh };
  • export default function print_hello(){};
  • export default print_hello
  • export { print_hello as default}

导入

如果想使用其他模块中的变量、函数、类,需要将其导入。导入后的变量、类、函数为只读。导入的方法如下:

  • import { color, print_hello } from “./example.js”;
  • import * as example from “./example.js”;  //example.color, example.print_hello
  • import { print_hello as printh } from “./example.js”;
  • import print_hello, { color } from “./example.js”; // print_hello 为默认导出的函数
  • import { default as printh } from “./example.js”;

ES6:数组

新增的静态方法

Array.of() :可以将传入的参数逐个传入数组,即使只有一个数值类型的参数,也会成为新数组的成员,而不是代表数组的长度

Array.from():可以将类数组结构转化为数组,例如: Array.from(arguments). 利用Array.from()也可以转换原来的数组,例如:

 
 
  1. let trans_args = function() {
  2. let args = Array.from(arguments, (value)=>value + 1);
  3. console.log(args);
  4. }
  5. trans_args(1, 2, 3);
  6. $ node test.js
  7. [ 2, 3, 4 ]

新增的普通方法

find() 与findIndex():传入一个回调函数来表明查找的条件,例如:

 
 
> let arr = [1, 2, 3, 4, 5];
undefined
> arr.find((n)=>n>3);
4
> arr.findIndex((n)=>n>3);
3

fill(value, start, end):填充value到数组的start到end位置,不包括end. 例如:

 
 
let arr = [1, 2, 3, 4, 5];
arr.fill(1, 1, 4);
[ 1, 1, 1, 1, 5 ]

copyWithin(toIndex, fromIndex, stopIndex): 从fromIndex开始复制元素到toIndex,遇到stopIndex停止,例如:

 
 
> let arr = [1, 2, 3, 4, 5]
undefined
> arr.copyWithin(2, 0, 1);
[ 1, 2, 1, 4, 5 ]

类型化数组

javascript中的数组缓冲区类似于c的malloc,例如,可以用如下方法分配一个10个字节大小的内存区域:

 
 
> let bu = new ArrayBuffer(10);
undefined
> bu.byteLength
10

可以使用slice(start, end)方法来对分配的内存进行切片。

要操作新分配的内存,需要使用DataView(buf, index, length)创建一个视图,例如:

 
 
> let buf = new ArrayBuffer(50);
> let view = new DataView(buf, 0, 50);

获取视图的信息:

 
 
> view.buffer === buf;
true
> view.byteOffset
0
> view.byteLength
50

读写数据:

 
 
> view.setUint8(4, 233);
undefined
> view.getUint8(4)
233

ES6中的类

ES6中添加的class关键字其实并非真正的类,而是ES5用函数来模拟类的语法糖。在ES6中可以用如下的语法创建一个类:

 
 
class Students {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        console.log(this.name);
    }
    sayAge() {
        console.log(this.age);
    }
}
let st1 = new Students('sen', 18);
st1.sayName();
st1.sayAge();

使用ES6的class语法糖和ES5自定义的类还是有些区别的:

  • 类的声明不会被提升,类的实例化只能在类的声明之后
  • 类声明中的代码只能运行在严格模式下
  • 类中的方法是不可枚举的
  • 实例化的时候必须加new关键字
  • 在方法内部修改类名会抛出错误,但可以在外部修改类名

下面的例子展示了如何在外部修改类名:

 
 
class Students {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        console.log(this.name);
    }
    sayAge() {
        console.log(this.age);
    }
}
let st1 = new Students('sen', 18);
st1.sayName();
st1.sayAge();
let Stu = Students;
let st2 = new Stu('sen', 18);
st2.sayName();
st2.sayAge();
console.log(st1 instanceof Students); //true
console.log(st2 instanceof Students); //true
console.log(st2 instanceof Stu); //true
console.log(st1 instanceof Stu); //true

类中的访问器属性

可以使用get和set关键字来定义访问器属性,例如:

 
 
class Students {
    constructor(name) {
        this.name = name;
    }
    get n() {
        return this.name;
    }
    set n(value) {
        this.name = value;
    }
}
let st1 = new Students('sen', 18);
console.log(st1.n);
st1.n = 'wang';
console.log(st1.n);

需计算的属性名

和其他需计算的名字一样,都是加上方括号,例如:

 
 
let propName = "sayName";
class Students {
    constructor(name) {
        this.name = name;
    }
    [propName]() {
        console.log(this.name);
    }
}
let st1 = new Students('sen');
st1.sayName();

给类添加默认的生成器

可以利用Symbol来给类添加默认的生成器,如:

 
 
class Collection {
    constructor() {
        this.items = [];
    }
    *[Symbol.iterator]() {
        yield *this.items.values();
    }
}
let col = new Collection();
col.items.push(1);
col.items.push(2);
col.items.push(3);
for( let i of col) {
    console.log(i);
}

给类添加静态属性/方法

类的静态方法/属性只能通过类名来访问,而不能通过类的实例来访问,具体做法是在方法/属性定义前面加上static关键字,例如:

 
 
class Students {
    constructor(name) {
        this.name = name;
    }
    static sayClassName() {
        console.log("Collection");
    }
}
Students.sayClassName();
let stu = new Students('sen');
stu.sayClassName(); // error

类的继承

ES6引入了extends和super来实现类的继承,例如:

 
 
class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
    getArea() {
        return this.length * this.width;
    }
}
class Square extends Rectangle {
    constructor(length) {
        super(length, length);
    }
}
let sq = new Square(5);
console.log(sq.getArea());

使用super需要注意:

  • super只能用在派生类中
  • 在constructor里,super负责初始化this,所以必须在this使用之前调用

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 }

生成器委托

也就是生成器的嵌套,例如:

 
 
let generator0 = function *() {
    yield 0;
    yield 1;
    yield 2;
}
let generator1 = function *() {
    yield 3;
    yield 4;
    yield 5;
}
let generator = function *() {
    yield *generator0();
    yield *generator1();
    yield 6;
}
let iter = generator();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());

单链表反转

实现单链表反转的思路

实现单链表反转的难点在于,如果让当前节点的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 = (num1, 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);
"