《JavaScript 高级程序设计》

第 3 章 基本概念

3.5.2 位操作符

ECMAScript 中所有数值都是以 IEEE-754 64 位格式存储,但位操作符并不直接操作 64 位的值。而是先将 64 位的值转换成 32 位的整数,然后执行操作,最后再将结果转换为 64 位。(对于开发人员 64 位存储格式是透明的,因此整个过程像是只存在 32 位的整数一样)

3.5.6 关系操作符

  • 比较的操作数为对象,则调用 valueOf() 方法(没有 valueOf() 调用 toString() 方法),用得到的值进行比较
  • 比较的操作数为布尔值,则转换为数字比较

3.7.1 理解参数

  • 修改 arguments 数组会改变形参的值(这并不是说它们访问相同的内存空间;它们的内存空间是独立的,但它们的值会同步。比如未传入形参时,修改了 arguments 数组形参值也不会变)
function func(para1, para2){
  console.log('修改前', arguments , para1, para2);
  arguments [0] = 'arguements_0';
  arguments [1] = 'arguements_1';
  console.log('修改后', arguments , para1, para2);
}
func('para1')
  • arguments 对象的长度是由传入的参数个数决定的,不是由定义函数时的命名参数的个数决定的。
  • ECMAScript 中的参数都是值传递,不存在引用传递

第 4 章 变量、作用域和内存问题

4.1 基本类型和引用类型的值

基本类型:

  1. Undefined
  2. Null
  3. Boolean
  4. Number
  5. String(注意 String 是基本类型)

基本类型的值无法添加属性(尽管这样不会报错),eg:

var name = "zhang";
name.age = 17;
console.log(name.age);// undefined

引用类型:

  1. Object

4.1.3 传递参数

ECMAScript 中所有函数的参数都是按值传递的。

eg:这里的 obj 形参是创建了一个指针让他与 person 指针指向同一个地址,obj 和 person 都指向同一个地址,但改变 obj 的指向 person 不改变

function setName(obj) {
    obj.name = "Nicholas";
    obj = new Object();
    obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"

4.2.2 没有块级作用域

使用 var 声明的变量会自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境;在 with 语句中,最接近的环境是函数环境。如果初始化变量时没有使用 var 声明,该变量会自动被添加到全局环境。

// 1.if
if (true) {
    var color = "blue";
}
alert(color); //"blue"

// 2.for
for (var i=0; i < 10; i++){
}
alert(i); //10

// 3.function
function add(num1, num2) {
    var sum = num1 + num2;
    return sum;
}
alert(sum); //由于 sum 不是有效的变量,因此会导致错误

现在使用 ES6 的let会声明块级作用域变量。

4.3 垃圾收集

常用标记清除,不常用引用计数

第 5 章 引用类型

5.2 Array 类型

如果 slice() 方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。例如,在一个包含 5 项的数组上调用 slice(-2,-1) 与调用 slice(3,4) 得到的结果相同。如果结束位置小于起始位置,则返回空数组。

5.2.8 迭代方法

  • every():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。
  • filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
  • forEach():对数组中的每一项运行给定函数。这个方法没有返回值。
  • map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
  • some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true

5.2.9 归并方法

ECMAScript 5 还新增了两个归并数组的方法: reduce() 和 reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中, reduce() 方法从数组的第一项开始,逐个遍历到最后。而 reduceRight() 则从数组的最后一项开始,向前遍历到第一项。

第 6 章 面向对象的程序设计

6.1.1 属性类型

  1. 数据属性
  2. 访问器属性(VUE 双向绑定实现的基础)

6.2 创建对象

  1. 工厂模式(使用函数创建对象,为对象添加属性和方法然后返回对象,被构造函数模式取代)

    function createPersion(name, age) {
        return {
            name,
            age
        }
    }
    
    var p1 = createPersion("zhang", 17);
    var p2 = createPersion("foo", 18);
  2. 构造函数模式(每个实例不共用属性和方法,JS 中函数是对象)

    function Persion(name, age) {
        this.name = name;
        this.age = age;
        this.print = function () {
            console.log(this.name);
        }
    }
    
    var p1 = new Persion("zhang", 17);
    var p2 = new Persion("foo", 18);
  3. 原型模式(每个实例共用一套属性和方法)

    function Persion() {}
    Persion.prototype.name = "zhang";
    Persion.prototype.age = 17;
    Persion.prototype.print = function () {
        console.log(this.name);
    }
    
    var p1 = new Persion();
    var p2 = new Persion();
  4. 组合使用构造函数模式和原型模式(既有共用又有不共用的属性和方法)

    • 这是目前使用最广泛的创建自定义类型的方法。

    • 使用对象覆盖原型(字面量方式修改原型)要注意已经创建的实例的constructor指针不会自动变(还是覆盖前的原型)。

    function Persion(name, age) {
        this.name = name;
        this.age = age;
    }
    Persion.prototype = {
        constructor: Persion,
        print: function () {
            console.log(this.name);
        }
    }
    
    var p1 = new Persion("zhang", 17);
    var p2 = new Persion("foo", 18);
  5. 动态原型模式(给原型添加属性的操作放到构造函数内)

    function Persion(name, age) {
        this.name = name;
        this.age = age;
        // 第一次执行时初始化
        if (typeof this.print !== "function") {
            Persion.prototype.print = function () {
                console.log(this.name);
            }
        }
    }
    
    var p1 = new Persion("zhang", 17);
    var p2 = new Persion("foo", 18);
  6. 寄生构造函数模式(和工厂模式几乎一样,可以为对象创建构造函数)

    function MyArray(){
        let arr = new Array();
        arr.push.apply(arr, arguments);
        arr.toPipeString = function(){
            return this.join("|");
        }
        return arr;
    }
    
    var marr = new MyArray(1,2,3);
    console.log(marr.toPipeString());
  7. 稳妥构造函数模式

    • 稳妥对象:没有公共属性,方法也不引用this对象。适合用在安全环境中,或防止数据被其他程序改动。
    function Persion(name, age) {
        var o = {};
    
        // 除了使用 print 没有其他方法能获取到 name
        o.print = function () {
            console.log(name);
        }
        return o;
    }
    
    var p1 = new Persion("zhang", 17);
    var p2 = new Persion("foo", 18);

6.3 继承

OO 语言支持两种继承:接口继承和实现继承。由于函数没有签名,ES 无法实现接口继承。

  1. 原型链

    有两个问题:1 属性共享, 2 无法参数传递

    function SuperType(para) {
        this.property = true;
        this.arr = [1, 2, 3];
        this.para = para;
    }
    
    SuperType.prototype.getSuperProp = function () {
        return this.property;
    }
    
    function SubType() {
        this.subproperty = false;
    }
    
    // 继承(这时 SubType.prototype.constructor 属性没有了,SubType.[[prototype]].constructor 为 SuperType)
    SubType.prototype = new SuperType();
    
    SubType.prototype.getSubProp = function () {
        return this.subproperty;
    }
    
    // 问题1:无法向父类构造函数传值
    var instance1 = new SubType();
    var instance2 = new SubType();
    
    // 问题2:instance2.arr 也改变了
    instance1.arr.push(4);
  2. 借用构造函数

    和创建对象的构造函数模式的问题一样,每个实例不共用属性和方法。

    function SuperType(para) {
        this.para = para;
        this.getPara = function(){
            return this.para;
        }
    }
    
    function SubType() {
        SuperType.apply(this, arguments);
    }
    
    // 可以向父类构造函数传值
    var instance1 = new SubType("instance1");
    var instance2 = new SubType("instance2");
  3. 组合继承

    原型链 + 借用构造函数

    JS 中最常用的继承模式。

    function SuperType(para) {
        this.property = true;
        this.arr = [1, 2, 3];
        this.para = para;
    }
    
    SuperType.prototype.getSuperProp = function () {
        return this.property;
    }
    
    function SubType() {
        SuperType.apply(this, arguments);
        this.subproperty = false;
    }
    
    // 继承
    SubType.prototype = new SuperType();
    SubType.prototype.constructor = SubType;
    
    SubType.prototype.getSubProp = function () {
        return this.subproperty;
    }
    
    // 可以向父类构造函数传值
    var instance1 = new SubType("instance1");
    var instance2 = new SubType("instance2");
    
    // instance2.arr 不变
    instance1.arr.push(4);
  4. 原型式继承

    有两个问题:1 属性会共享,2 每个实例无法共用方法

    function createObj(obj) {
        function f() {};
        f.prototype = obj;
        return new f();
    }
    
    var person = {
        name: "zhang",
        age: [17]
    }
    
    // person、 zi 和 ma 共用引用属性 age
    var zi = createObj(person);
    var ma = createObj(person);
    
    zi.age.push(18);
    ma.age.push(17);
    console.log(person.age);
  5. 寄生式继承

    和创建对象的寄生构造函数模式类似,也有属性会共享的问题

    function createObjParasitic(obj) {
        let o = Object.create(obj);
        // o 目前是如下对象:
        // {
        //     [[prototype]] = obj
        // }
        o.sayHi = function () {
            console.log("hi")
        }
        return o;
    }
    
    var person = {
        name: "zhang",
        age: [17]
    }
    
    var zi = createObjParasitic(person);
    
    zi.sayHi();
  6. 寄生组合式继承

    寄生式继承 + 组合式继承

    new SuperType()改为Object.create(SuperType.prototype)减少一次父类的调用,还避免了在SubType.prototype上添加不必要的属性(如:下面的propertyarr属性)。

    function inheritPrototype(sub, sup) {
        // 使 sub.prototype 的 [[prototype]] “指针”指向 sup.prototype
        sub.prototype = Object.create(sup.prototype);
        sub.prototype.constructor = sub;
    }
    
    function SuperType() {
        this.property = true;
        this.arr = [1, 2, 3];
    }
    
    SuperType.prototype.getSuperProp = function () {
        return this.property;
    }
    
    function SubType() {
        SuperType.apply(this, arguments);
        this.subproperty = false;
    }
    
    inheritPrototype(SubType, SuperType);
    
    console.log((new SubType()).getSuperProp());

第 7 章 函数表达式

定义函数有两种形式:函数声明(会函数声明提升)和函数表达式。

7.2 闭包

闭包指有权访问另一个函数作用域中的变量的函数。

7.2.2 关于 this 对象

每个函数在被调用时都会自动取得两个特殊变量: this 和 arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。不过,把外部作用域中的 this 对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。

如果想访问作用域中的 this 和 arguments 对象必须把她们保存在闭包可以访问的变量中。

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        return function () {
            // this指向window
            return this.name;
        };
    }
};
console.log(object.getNameFunc()()); //"The Window"(在非严格模式下)

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        var that = this;
        return function () {
            // 闭包可以访问that
            // this指向window,that指向object
            return that.name;
        };
    }
};
console.log(object.getNameFunc()()); //"My Object"

7.4 私有变量

JS 中没有使用成员的概念,所有对象属性都是共有的。但在函数中定义的变量都可以认为是私有比阿尼浪,因为不能在函数外部访问这些变量(包括:参数、局部变量和函数内部定义的其他函数)。

7.4.2 模块模式

JS 以字面量的方式创建单例对象。

var singleton = {
    name: "xxx",
    method: function () {
        // ...
    }
}

7.4.3 增强的模块模式

// 这时 function(){...}() 是当做语句解析的
var application = function(){
    //私有变量和函数
    var components = new Array();
    //初始化
    components.push(new BaseComponent());
    //创建 application 的一个局部副本
    var app = new BaseComponent();
    //公共接口
    app.getComponentCount = function(){
        return components.length;
    };
    app.registerComponent = function(component){
        if (typeof component == "object"){
             components.push(component);
        }
    };
    //返回这个副本
    return app;
}();

第 8 章 BOM

8.1 window 对象

BOM 的核心是 window 对象,它表示浏览器的一个实例,同时也是 ES 中规定的 Global 对象。

8.1.2 窗口关系及框架

如果页面中包含框架,则每个框架都拥有自己的 window 对象,并且保存在 frames 集合中。在 frames 集合中,可以通过数值索引(从 0 开始,从左至右,从上到下)或者框架名称来访问相应的 window 对象。每个 window 对象都有一个 name 属性,其中包含框架的名称。

在使用框架的情况下,浏览器中会存在多个 Global 对象。在每个框架中定义的全局变量会自动成为框架中 window 对象的属性。由于每个 window 对象都包含原生类型的构造函数,因此每个框架都有一套自己的构造函数,这些构造函数一一对应,但并不相等。例如,top.Object并不等于top.frames[0].Object。这个问题会影响到对跨框架传递的对象使用instanceof操作符。

  • window.top对象始终指向最外层框架,也就是浏览器窗口
  • window.parent指向父窗口
  • window.self指向 window

8.1.3 窗口位置

窗口相对屏幕的位置:window.screenLeftwindow.screenTop

窗口位置移动(默认是禁用的):window.moveTo()window.moveBy()

8.1.4 窗口大小

浏览器窗口大小(不同浏览器对这些属性的解释不同):window.innerHeightwindow.innerWidthwindow.outerWidthwindow.outerWidth

页面视口信息:window.document.documentElement.clientWidthwindow.document.documentElement.clientHeight

浏览器窗口大小改变(默认是禁用的):window.resizeTo()window.resizeBy()

8.1.6 间歇调用和超时调用

一般认为,使用超时调用来模拟间歇调用是一种最佳模式。在开发环境下,很少使用真正的间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束之前启动。而像下面示例中那样使用超时调用,则完全可以避免这一点。所以,最好不要使用间歇调用。

// 超时调用模拟间歇调用
var num = 0;
var max = 10;
function incrementNumber() {
    num++;
    //如果执行次数未达到 max 设定的值,则设置另一次超时调用
    if (num < max) {
        setTimeout(incrementNumber, 500);
    } else {
        alert("Done");
    }
}
setTimeout(incrementNumber, 500);

8.2 location 对象

8.2.2 位置操作

// 基本
location.assign("https://developer.mozilla.org")
// 下面两种和和显示调用 assign 方法一样
window.location = "https://developer.mozilla.org"
location.href = "https://developer.mozilla.org"
// 其他
location.hash = "#123"
location.pathname = "zh-CN"
// replace 的 location 不记录在历史记录中
location.replace("https://developer.mozilla.org")

获取浏览器信息、主语言、插件、设置等信息。

8.4 screen 对象

用来获取浏览器窗口外部的显示器信息,如像素宽度、高度等。

8.5 history 对象

// 跳转到最近的 mdn 界面
history.go("developer.mozilla.org");

第 9 章 客户端检测

  • 能力(特性)检测:检测浏览器是否支持某项功能
function isHostMethod(object, property) {
    var t = typeof object[property];
    return t === 'function' || (!!(t === 'object' && object[property])) || t === 'unknown';
}
console.log(isHostMethod([], 'find'));
  • 怪癖检测:检测浏览器某项功能是否有 bug
// 是否有将属性不列举的bug
var hasDontEnumQuirk = function() {
    var o = {
        // 这里新的 toString 应该要列举出来的,因为新的 toString 已经覆盖了旧的 [[Enumerable]] false 的 toString
        // 但 IE8 及更低版本的浏览器会不将 toString 列举。
        toString: function () {}
    };
    for (var prop in o) {
        if (prop === 'toString') {
            return false;
        }
    }
    return true;
}();
console.log(hasDontEnumQuirk());
  • 用户代理检测:检测呈现引擎、浏览器、平台、设备和操作系统

    因为用户代理字符串有很长的发展历史,在此期间浏览器供应商试图在用户代理字符串中添加一些欺骗信息,让网站相信自己是另一种浏览器,使得用户代理检测比较复杂。但通过navigator.userAgent(大部分信息在这个字符串中),navigator.platformwindow的属性还是可以检测出来呈现引擎、浏览器、平台、设备和操作系统这些信息的。

第 10 章 DOM

10.1 节点层次

HTML 页面中,文档元素始终是元素,XML 中没有预定义的元素,任何元素都可能成为文档元素。

10.1.1 Node 接口

  • nodeType属性

每个节点都有nodeType属性,用于表示节点类型。节点类型在 Node 类型中定义的下列 12 个数值常量表示,任何节点的类型必居其一。

参见:Node.nodeType - Web APIs | MDN

  • childNodes属性

childNodes属性中保存着一个NodeList对象。

NodeList对象是一种类数组对象,但不是 Array 实例。

DOM 结构变化会自动反映在NodeList对象中,也就是说NodeList对象是动态变化的,而不是一张快照。

NodeList对象转为数组:

function convertToArray(nodes){
    return Array.prototype.slice.call(nodes, 0);
}
  • clonNode()方法

参数为 true 或 false,true 执行深复制(克隆子节点),false 执行浅复制(不克隆子节点)。

clonNode()方法不会复制 JS 的属性(IE 存在 bug,会复制时间处理程序)。

  • normalize()方法

    处理文档树中的文本节点。

10.1.2 Document 接口

Document 接口继承自 Node 接口,Document 接口描述了任何类型文档的公共属性和方法。

document 对象是 HTMLDocument 的一个实例。

  • dcoumentElement属性

取得的引用

  • doctype属性

取得的引用

  • titel属性

修改titel属性不会改变</code>元素</p> <ul> <li><code>domain</code>属性</li> </ul> <p>由于跨域安全限制,来自不同子域的页面无法通过 JS 通信。而将每个<code>document.domain</code>设置为相同的值,这些页面就可以互相访问对方包含的 JS 对象了。</p> <p><code>domain</code>属性修改有两个限制:1. 同源,2. 低级(eg:三级子域名<code>p2p.wrox.com</code>)向高级(eg:二级子域名<code>wrox.com</code>)。</p> <ul> <li><code>write</code>方法</li> </ul> <p>页面加载过程调用会动态加入内容,页面加载结束后调用会重写整个页面内容。</p> <h3 id="element-接口">10.1.3 Element 接口</h3> <p>所有 HTML 元素都是由 HTMLElement 类型或她的子类型表示。</p> <ul> <li><code>getAttribute()</code>方法</li> </ul> <p><code>getAttribute()</code>方法返回属性<strong>值</strong>,例如:使用属性访问 style 和 onclick 时分别返回对象和方法,使用<code>getAttribute()</code>方法返回字符串。</p> <ul> <li><code>attributes</code>属性</li> </ul> <p><code>attributes</code>属性包含一个 NamedNodeMap 对象,与 NodeList 类似是一个动态集合。</p> <h3 id="text-接口">10.1.4 Text 接口</h3> <p>修改文本节点时,字符串会根据文档类型进行编码(将预留字符转义)。</p> <ul> <li><code>splitText()</code>方法</li> </ul> <p>调用这个方法原来的文本节点变为从开始到指定位置之前的内容,返回剩下的内容。</p> <h3 id="documentfragment-类型">10.1.8 DocumentFragment 类型</h3> <blockquote> <p>在所有节点类型中,只有 DocumentFragment 在文档中没有对应的标记。 DOM 规定文档片段(document fragment)是一种 “轻量级” 的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。</p> </blockquote> <p>通过<code>apppendChild()</code>或<code>insertBefore()</code>将文档片段添加到文档中时,只会将文档片段的子节点添加到响应位置上,文档片段本身不会成为文档的一部分。例如:</p> <pre><code class="language-javascript"><code>let fragment = document.createDocumentFragment(); let ul = document.createElement("ul"); let li = null; for (let i = 0; i < 3; i++) { li = document.createElement("l1"); li.appendChild(document.createTextNode("Item " + (i + 1))); fragment.appendChild(li); } document.body.appendChild(ul); ul.appendChild(fragment);</code></code></pre> <h2 id="dom-操作技术">10.2 DOM 操作技术</h2> <blockquote> <p>一般来说,尽量减少访问 NodeList 的次数。因为每次访问 NodeList,都会运行一次基于文档的查询。所以可以考虑将从 NodeList 中取得的值缓存起来。</p> </blockquote> <h1 id="第-11-章-dom-拓展">第 11 章 DOM 拓展</h1> <h2 id="元素遍历">11.2 元素遍历</h2> <p>对于元素间的空格,浏览器会当做文本节点(除了 IE9 之前的浏览器)。Element Traversal Specification定义了一组新属性可以只访问 Element 节点。</p> <p>遍历子元素:</p> <pre><code class="language-javascript"><code>// 新 var child = document.body.firstElementChild; while(child != document.body.lastElementChild){ console.log(child); child = child.nextElementSibling; } // 旧 var child = document.body.firstChild; while(child != document.body.lastChild){ if(child.nodeType == 1){ console.log(child); } child = child.nextSibling; }</code></code></pre> <h2 id="html5">11.3 HTML5</h2> <h3 id="交点管理">11.3.2 交点管理</h3> <blockquote> <p>HTML5 也添加了辅助管理 DOM 焦点的功能。</p> <p>首先就是 <code>document.activeElement</code> 属性,这个属性始终会引用 DOM 中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是通过按 Tab 键)和在代码中调用<code>focus()</code>方法。</p> <p>另外是新增了<code>document.hasFocus()</code>方法,用于判断文档是否获取焦点。</p> </blockquote> <h3 id="htmldocument-的变化">11.3.4 HTMLDocument 的变化</h3> <ol> <li>readyState 属性:判断页面加载情况(旧方法是在触发 onload 事件时记录页面加载完成)。</li> <li>compatMode 属性:判断页面是标准(Standards)模式还是混杂(Quirks)模式(混杂模式下浏览器会尝试模拟<strong>非常旧</strong>的浏览器的行为)。</li> <li>head 属性:引用文档的 head 元素。</li> <li>charset 属性:获取和设置字符集。(MDN 中是<code>Document.characterSet</code>)</li> </ol> <h3 id="scrollintoview方法">11.3.4 <code>scrollIntoView()</code>方法</h3> <blockquote> <p>通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。</p> </blockquote> <h1 id="第-12-章-dom2-和-dom3">第 12 章 DOM2 和 DOM3</h1> <h2 id="dom-变化">12.1 DOM 变化</h2> <ul> <li>DOM Level 2 Core:为大多数 DOM1 方法提供特定与命名空间的版本(方法名中带有 NS)。</li> <li>DOM Level 2 View</li> <li>DOM Level 2 HTML</li> </ul> <h2 id="样式">12.2 样式</h2> <h3 id="访问元素的样式">12.2.1 访问元素的样式</h3> <blockquote> <p>多数情况下,都可以通过简单地转换属性名的格式来实现转换。其中一个不能直接转换的 CSS 属性就是 float。由于 float 是 JavaScript 中的保留字,因此不能用作属性名。“DOM2 级样式” 规范规定样式对象上相应的属性名应该是 cssFloat; Firefox、 Safari、 Opera 和 Chrome 都支持这个属性,而 IE 支持的则是 styleFloat。</p> </blockquote> <p>实践中,最好始终指定度量单位(标准模式下所有的度量值必须指定单位,混杂模式下可以不指定单位)。</p> <pre><code><div id="myDiv">myDiv</div> <script> var myDiv = document.getElementById("myDiv"); //浮动 myDiv.style.cssFloat = "left"; //背景颜色 myDiv.style.backgroundColor = "red"; //改变大小 myDiv.style.width = "100px"; myDiv.style.height = "200px"; //指定边框 myDiv.style.border = "1px solid black"; </script></code></pre> <h3 id="操作样式表">12.2.2 操作样式表</h3> <h3 id="元素大小">12.2.3 元素大小</h3> <ol> <li>偏移量 offsetTop/Left/Width/Height(Width 和 Height 包括边框)。</li> <li>客户区大小 clientTop/Left/Width/Height(width 和 height 不包括边框)。</li> <li>滚动大小 scrollTop/Left/Width/Height。</li> <li>确定元素大小<code>Element.getBoundClientRect()</code>。</li> </ol> <h2 id="遍历">12.3 遍历</h2> <p>NodeIterator 和 TreeWalker。IE 不支持!</p> <h2 id="范围">12.4 范围</h2> <blockquote> <p>为了让开发人员更方便地控制页面,“DOM2 级遍历和范围” 模块定义了 “范围”(range)接口。通过范围可以选择文档中的一个区域,而不必考虑节点的界限(选择在后台完成,对用户是不可见的)。在常规的 DOM 操作不能更有效地修改文档时,使用范围往往可以达到目的。</p> </blockquote> <h1 id="第-13-章-事件">第 13 章 事件</h1> <h2 id="事件流">13.1 事件流</h2> <p>由于老版本浏览器不支持,因此很少有人使用事件捕获。建议放心使用事件冒泡,特殊需要时使用事件捕获。</p> <h3 id="事件冒泡">13.1.1 事件冒泡</h3> <p>IE 提出的事件流叫事件冒泡(event bubling),事件由最具体(深)的节点向不具体(文档)节点传播。</p> <h3 id="事件捕获">13.1.2 事件捕获</h3> <p>Netscape 提出的事件流叫事件捕获(event capturing),思想是不太具体的节点应先接收到事件,具体的节点后接收到事件。</p> <h3 id="dom-事件流">13.1.3 DOM 事件流</h3> <p>UI Events-event-flow</p> <blockquote> <p>“DOM2 级事件” 规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。</p> </blockquote> <ul> <li>事件捕获:为截获事件提供机会(实际目标在捕获阶段不会接收到事件)</li> <li>目标阶段:事件目标接收到事件</li> <li>冒泡阶段:对事件作出响应</li> </ul> <p>PS:多数浏览器都实现了在捕获阶段触发事件对象上的事件这种特定行为。即使 “DOM2 级事件” 规范明确要求捕获阶段不会涉及事件目标。</p> <h2 id="事件处理程序">13.2 事件处理程序</h2> <p>诸如 click、load 和 mouseover 叫事件的名字。响应某个事件的函数叫做事件处理程序,事件处理程序的名字以‘’on“开头。</p> <h3 id="html-事件处理程序">13.2.1 HTML 事件处理程序</h3> <blockquote> <p>某个元素支持的每种事件,都可以用一个与之相应事件处理程序同名的 HTML 特性来指定。</p> </blockquote> <p>这样指定事件处理程序:首先会创建一个封装着元素属性值的函数,其次这种方式动态创建的函数会拓展作用域,例如:</p> <pre><code class="language-html"><code><!-- 1. 查看属相和参数 --> <input type="button" value="click" onclick="console.log(arguments, event, this, value)"> <!-- 2. 拓展作用域 --> <form> <input type="text" name="uname" value="zzz"> <input type="button" value="click" onclick="console.log(uname.value)"> </form> <!-- 这个动态创建的函数像这样拓展作用域: function(){ with(document){ with(this,form){ with(this){ console.log(uname.value) } } } } --></code></code></pre> <h3 id="dom0-级事件处理程序">13.2.2 DOM0 级事件处理程序</h3> <blockquote> <p>使用 DOM0 级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在<br> 元素的作用域中运行;换句话说,程序中的 this 引用当前元素。来看一个例子。</p> </blockquote> <pre><code class="language-html"><code><button id="myBtn">myBtn</button> <script> var btn = document.getElementById("myBtn"); btn.onclick = function(){ alert(this.id); //"myBtn" }; </script></code></code></pre> <p>将事件处理程序属性的值设置为 null 可以删除通过 DOM0 级方法指定的事件处理程序:<br><code>btn.onclick = null;</code></p> <h3 id="dom2-级事件处理程序">13.2.3 DOM2 级事件处理程序</h3> <blockquote> <p>“DOM2 级事件” 定义了两个方法,用于处理指定和删除事件处理程序的操作:<code>addEventListener()</code> 和 <code>removeEventListener()</code>。所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。</p> </blockquote> <pre><code class="language-html"><code><button id="myBtn">myBtn</button> <script> var btn = document.getElementById("myBtn"); btn.addEventListener("click", function(){ alert(this.id); }, false); btn.addEventListener("click", function(){ alert("Hello world!"); }, false); </script></code></code></pre> <p>删除事件时必须传入绑定的事件的 “指针”。</p> <h3 id="ie-事件处理程序">13.2.4 IE 事件处理程序</h3> <blockquote> <p>IE 实现了与 DOM 中类似的两个方法:<code>attachEvent()</code> 和 <code>detachEvent()</code>。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。由于 IE8 及更早版本只支持事件冒泡,所以通过 <code>attachEvent()</code>添加的事件处理程序都会被添加到冒泡阶段。</p> </blockquote> <h2 id="事件对象">13.3 事件对象</h2> <h3 id="dom-中的事件对象">13.3.1 DOM 中的事件对象</h3> <blockquote> <p>兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0 级或 DOM2 级),都会传入 event 对象。</p> </blockquote> <p>event 对象的属性参见:Event - Web APIs | MDN</p> <pre><code class="language-html"><code><button id="myBtn">myBtn</button> <script> var btn = document.getElementById("myBtn"); btn.onclick = function(event){ alert(event.type); //"click" }; btn.addEventListener("click", function(event){ alert(event.type); //"click" }, false); </script></code></code></pre> <h3 id="ie-中的事件对象">13.2.2 IE 中的事件对象</h3> <p>DOM0 事件处理程序:event 对象作为 window 对象的一个属性存在。</p> <p><code>attachEvent()</code>添加事件处理程序:event 对象作为参数传入事件处理函数中,也可以通过 window 对象的 event 属相得到。</p> <h2 id="事件类型">13.4 事件类型</h2> <p>DOM3 级事件类型:DOM-Level-3-Events</p> <ul> <li>User Interface Events debugger</li> <li>Focus Events</li> <li>Mouse Events</li> <li>Wheel Events</li> <li>Input Events</li> <li>Keyboard Events</li> <li>Composition Events:用于处理 IME 输入序列。IME(Input Method Editor)可以让用户输入在物理键盘上找不到的字符。例如,使用拉丁文键盘的用户通过 IME 可以输入日文字符。</li> <li>变动(mutation)事件:底层 DOM 结构发生变化时触发。变动事件是为 XML 或 HTML DOM 设计的,并不特定于某种语言。</li> </ul> <h3 id="ui-事件">13.4.1 UI 事件</h3> <p>HTML 中无法访问 widow 元素,所以一般在 window 上面发生的任何事件都可以在<code><body></code>元素中通过相应特性指定。</p> <h3 id="鼠标与滚轮事件">13.4.3 鼠标与滚轮事件</h3> <p>mousedown、mouseup、click 和 dbclick 顺序:</p> <ol> <li>mousedown</li> <li>mouseup</li> <li>click</li> <li>mousedown</li> <li>mouseup</li> <li>dbclick</li> </ol> <p>如果 mousedown 或 mouseup 中的一个被取消,click 事件就不会触发。</p> <ol> <li>客户区坐标位置:clientX|Y,鼠标指针在浏览器视口的水平和垂直位置</li> <li>页面坐标位置:pageX|Y,鼠标在页面中的位置</li> <li>屏幕坐标位置:screenX|Y,鼠标在电脑屏幕的位置</li> <li>相关元素:在执行 mouseover 和 mouseout 时 relatedTarget 提供了涉及到的(移入、移出的)元素的信息。</li> <li>触摸设备:<br> iOS 和 Android 设备的实现非常特别,因为这些设备没有鼠标。在面向 iPhone 和 iPod 中的 Safari 开发时,要记住以下几点。 <ul> <li>不支持 dblclick 事件。双击浏览器窗口会放大画面,而且没有办法改变该行为。</li> <li>轻击可单击元素会触发 mousemove 事件。如果此操作会导致内容变化,将不再有其他事件发生;如果屏幕没有因此变化,那么会依次发生 mousedown、 mouseup 和 click 事件。轻击不可单击的元素不会触发任何事件。可单击的元素是指那些单击可产生默认操作的元素(如链接),或者那些已经被指定了 onclick 事件处理程序的元素。</li> <li>mousemove 事件也会触发 mouseover 和 mouseout 事件。</li> <li>两个手指放在屏幕上且页面随手指移动而滚动时会触发 mousewheel 和 scroll 事件。</li> </ul></li> </ol> <h3 id="键盘与文本事件">13.4.4 键盘与文本事件</h3> <ul> <li><p>键盘事件:keydown、keypress、keyup</p> <p>按下字符键时:会先触发 keydown 然后触发 keypress 最后触发 keyup,按住不放时 keydown、keypress 会重复触发。</p> <p>按下非字符键时:会触发 keydown 最后触发 keyup,按住不放时 keydown 会重复触发。</p></li> <li><p>文本事件:textInput</p></li> </ul> <h3 id="html5-事件">13.4.7 HTML5 事件</h3> <ol> <li><p>contextmenu 事件:用户打开上下文菜单时触发(Windows 下是鼠标右键单击,Mac 下时 Ctrl + 单击),可以阻止默认的上下文菜单。</p></li> <li><p>beforeunload 事件:在页面卸载前触发,并弹出确认框让用户确认是否离开页面。这个事件不能彻底取消,因为那样就相当于让用户就无法离开当前页面了。。</p></li> <li><p>DOMContentLoaded 事件 :</p> <blockquote> <p>window 的 load 事件会在页面中的一切都加载完毕时触发,但这个过程可能会因为要加载的外部资源过多而颇费周折。而 DOMContentLoaded 事件则在形成完整的 DOM 树之后就会触发,不理会图像、 JavaScript 文件、 CSS 文件或其他资源是否已经下载完毕。与 load 事件不同,DOMContentLoaded 支持在页面下载的早期添加事件处理程序,这也就意味着用户能够尽早地与页面进行交互。</p> </blockquote></li> <li><p>readystatechange 事件:提供与文档或元素加载状态有关的信息。</p></li> <li><p>pageshow 和 pagehide 事件:浏览器会缓存前进和后退的页面(bfcache, before-forward chache),页面位于 bfcache 中,load 事件不会触发,pageshow 事件会触发。</p></li> <li><p>hashchange 事件:页面 URL 中 "#" 后面的字符串发生变化时触发。</p></li> </ol> <h3 id="设备事件">13.4.8 设备事件</h3> <p>智能手机和平板相关的事件,可以监测横竖屏切换、方向改变、移动。</p> <h3 id="触摸与手势事件">13.4.9 触摸与手势事件</h3> <ol> <li><p>触摸事件</p> <p>事件发生顺序入下</p> <ol> <li>touchstart</li> <li>mouseover</li> <li>mousemove(一次)</li> <li>mousedown</li> <li>mouseup</li> <li>click</li> <li>touchend</li> </ol></li> <li><p>手势事件</p></li> </ol> <h2 id="内存和性能">13.5 内存和性能</h2> <h3 id="事件委托">13.5.1 事件委托</h3> <blockquote> <p>对 “事件处理程序过多” 问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如, click 事件会一直冒泡到 document 层次。也就是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。</p> </blockquote> <h3 id="移除事件处理程序">13.5.2 移除事件处理程序</h3> <p>通过手动去除事件处理程序的引用防止内存泄露。</p> <h2 id="模拟事件">13.6 模拟事件</h2> <h3 id="dom-中的事件模拟">13.6.1 DOM 中的事件模拟</h3> <p>步骤(1,2 步的方法已经过时,推荐直接用 new 创建 event 对象):</p> <ol> <li>创建事件对象:在 documnet 对象上使用<code>createEvent()</code>方法创建 event 对象。</li> <li>初始化事件对象:调用 event 对象的<code>initXXXEvent()</code>方法。</li> <li>触发事件:目标元素调用<code>dispatchEvent()</code>方法。</li> </ol> <p>例,模拟鼠标事件:</p> <pre><code class="language-html"><code><!-- 新 --> <button id="btn" onclick="alert('clicked')">click me</button> <script> var btn = document.querySelector("#btn"); var event = new MouseEvent("click", {button: 0}); btn.dispatchEvent(event); </script> <!-- 过时 --> <button id="btn2" onclick="alert('clicked Deprecated')">click me</button> <script> var btn = document.querySelector("#btn2"); var event = document.createEvent("MouseEvents"); event.initMouseEvent("click", true, true, document.defaultView); btn.dispatchEvent(event); </script></code></code></pre> <h1 id="第-14-章-表单脚本">第 14 章 表单脚本</h1> <h2 id="表单基础知识">14.1 表单基础知识</h2> <pre><code class="language-html"><code><form> input1: <input type="text" name="i1"> input2: <input type="text" name="i2" autofocus> <select name="s"> <option>zzz</option> <option>jjj</option> <option>fff</option> </select> </form> <script> // 通过document.forms可以取得所有表单 var forms = document.forms; // form 的 submit() 方法*不会*触发 submit 事件 forms[0].addEventListener("submit", () => alert('submit')); // 取消下面这个注释会非常鬼畜 // forms[0].submit(); // form 的 reset() 方法*会*触发 reset 事件 forms[0].addEventListener("reset", () => alert('reset')); forms[0].reset(); // form 的 elements 属性是表单中元素的集合 var filed = forms[0].elements[1]; // 自动将焦点移动到输入框 window.addEventListener("load", () => { // autofocus 属性在支持她的浏览器中应该为 true,不支持的为空字符串 if(filed.autofocus !== true){ filed.focus(); } }); // change 事件对于输入元素失去焦点(blur)触发,对于选择元素修改选项后触发 forms[0].elements["i2"].addEventListener("change", () => { console.log("input change"); }); forms[0].elements["s"].addEventListener("change", () => { console.log("select change"); }); </script></code></code></pre> <h2 id="文本框脚本">14.2 文本框脚本</h2> <pre><code class="language-html"><code><form> <!-- size : 可以显示的字符数 --> <input type="text" name="i1" value="initial value" size="4"> <br> <!-- 第一个文本节点为初始值 --> <textarea name="t">initial value</textarea> <br> <!-- 绑定限制输入类型的事件 --> <input type="text" name="onlyNum"> <br> <!-- 正则验证 --> <input type="text" pattern="\w+"> </form> <script> var forms = document.forms; var input = forms[0].elements[0]; var textarea = forms[0].elements[1]; var onlyNumInput = forms[0].elements[2]; // 不建议用 DOM 方法修改文本框的值(例如:修改 <textarea> 的第一个子节点,或使用 setAttribute() 来设置 value 属性)。 // 因为这些修改不一定会反映在DOM中。 // 建议像下面这样使用 value 属性读取和设置文本框的值。 textarea.value = "new initial value"; console.log(input.value); // 选择文本 input.addEventListener("focus", (e) => { e.target.select(); }); // 取得选择文本 console.log(input.value.substring(input.selectionStart, input.selectionEnd)); // 选择部分文本 textarea.setSelectionRange(3, 9); textarea.focus(); // keydown 屏蔽字符(keypress 已经弃用了) onlyNumInput.addEventListener("keydown", (e) => { if(!/\d/.test(e.key) && !e.ctrlKey){ e.preventDefault(); } }); // 屏蔽粘贴的字符 onlyNumInput.addEventListener("paste", (e) => { if(!/\d/.test(e.clipboardData.getData("text"))){ e.preventDefault(); } }); </script></code></code></pre> <h2 id="选择框脚本">14.3 选择框脚本</h2> <ul> <li>添加 / 删除选项:使用 DOM 方法(<code>appendChild()</code>、<code>removeChild()</code>)或选择框的方法(<code>add()</code>、<code>remove()</code>)</li> <li>移动选项:使用<code>appendChild()</code>将一个选择框中的选项移动到另一个选择框</li> <li>重排选项:使用<code>insertBefore()</code>移动选项的位置</li> </ul> <h2 id="表单序列化">14.4 表单序列化</h2> <p>目前还没有内置的表单序列化的方法,参见:HTMLFormElement - Web APIs | MDN。造序列化的轮子要注意制定表单字段的显示隐藏、是否禁用、按钮、单选多选等情况的处理。</p> <h2 id="富文本编辑">14.5 富文本编辑</h2> <p>设置 iframe 的 designMode 属性可以使这个 iframe 可编辑。</p> <h3 id="使用-contenteditable-属性">14.5.1 使用 contenteditable 属性</h3> <p>通过设置元素的 contenteditable 属性控制该元素是否可编辑。</p> <h3 id="操作富文本">14.5.2 操作富文本</h3> <p>使用<code>document.execCommand()</code>执行预定义的命令。</p> <h3 id="富文本选区">14.5.3 富文本选区</h3> <p>使用 Selection 和 Range 的相关的属性和方法,参见:Selection - Web APIs | MDN、Range - Web APIs | MDN。</p> <p>设置选择的文本背景为黄色:</p> <pre><code class="language-html"><code><div id="rechedit" contenteditable="true" style="background: #ddd;"> Hello world! </div> <button onclick="setbg()">setbg</button> <script> function setbg(){ var selection = document.getSelection(), range = selection.getRangeAt(0); span = document.createElement("span"); // 设置背景 span.style.backgroundColor = "yellow"; // 设置到选区 range.surroundContents(span); } </script></code></code></pre> <h3 id="表单与富文本">14.5.4 表单与富文本</h3> <blockquote> <p>由于富文本编辑是使用 iframe 而非表单控件实现的,因此从技术上说,富文本编辑器并不属于表单。换句话说,富文本编辑器中的 HTML 不会被自动提交给服务器,而需要我们手工来提取并提交 HTML。为此,通常可以添加一个隐藏的表单字段,让它的值等于从 iframe 中提取出的 HTML。具体来说,就是在提交表单之前,从 iframe 中提取出 HTML,并将其插入到隐藏的字段中。</p> </blockquote> <h1 id="第-15-章-使用-canvas-绘图">第 15 章 使用 Canvas 绘图</h1> <h2 id="基本用法">15.1 基本用法</h2> <p>在使用<code><canvas></code>前首先要检测<code>getContext()</code>方法是否存在。</p> <p>使用<code>toDataURL()</code>方法可以导出在<code><canvas></code>上绘制的图形。</p> <h2 id="d-上下文">15.2 2D 上下文</h2> <h3 id="变换">15.2.5 变换</h3> <blockquote> <p>如果你知道将来还要返回某组属性与变换的组合,可以调用 save() 方法。调用这个方法后,当时的所有设置都会进入一个栈结构,得以妥善保管。然后可以对上下文进行其他修改。等想要回到之前保存的设置时,可以调用 restore() 方法,在保存设置的栈结构中向前返回一级,恢复之前的状态。连续调用 save() 可以把更多设置保存到栈结构中,之后再连续调用 restore() 则可以一级一级返回。</p> </blockquote> <pre><code><canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas> <script> var drawing = document.getElementById("drawing"); //确定浏览器支持<canvas>元素 if (drawing.getContext){ var context = drawing.getContext("2d"); context.fillStyle = "#ff0000"; context.save(); //save1 context.fillStyle = "#00ff00"; context.translate(100, 100); context.save(); //save2 context.fillStyle = "#0000ff"; context.fillRect(0, 0, 100, 200); //从点(100,100)开始绘制蓝色矩形 context.restore(); //返回save2 context.fillRect(10, 10, 100, 200); //从点(110,110)开始绘制绿色矩形 context.restore(); //返回save2 context.fillRect(0, 0, 50, 50); //从点(0,0)开始绘制红色矩形 } </script></code></pre> <h3 id="使用图像数据">15.2.10 使用图像数据</h3> <p>使用<code>getImageData</code>可以获取<code><canvas></code>的ImageData实例(包括图像的宽高和每个像素的信息)。<br> 使用<code>putImageData()</code>可以将ImageData实例设置到<code><canvas></code>。</p> <h2 id="webgl">15.3 WebGL</h2> <h3 id="类型化数组">15.3.1 类型化数组</h3> <p>WebGL 涉及的复杂计算需要提前知道数值的精度,而标准的 JS 数值无法满足。为此 WebGL 引入了类型化数组。</p> <h3 id="webgl-上下文">15.3.2 WebGL 上下文</h3> <ul> <li>视口与坐标:WebGL 中的蛇口坐标和网页坐标不一样,其左下角为原点。</li> <li>着色器:使用 GLSL 语言编写。以字符串的形式传给 WebGL 上下文对象<code>gl</code>的<code>compileShader()</code>方法进行编译。</li> </ul> <blockquote> <p>WebGL: 2D and 3D graphics for the web - Web APIs | MDN</p> </blockquote> <h1 id="第-16-章-html5-脚本编程">第 16 章 HTML5 脚本编程</h1> <h2 id="跨文档消息传递">16.1 跨文档消息传递</h2> <blockquote> <p>跨文档消息传送(cross-document messaging),有时候简称为 XDM,指的是在来自不同域的页面间传递消息。例如, www.wrox.com 域中的页面与位于一个内嵌框架中的 p2p.wrox.com 域中的页面通信。在 XDM 机制出现之前,要稳妥地实现这种通信需要花很多工夫。 XDM 把这种机制规范化,让我们能既稳妥又简单地实现跨文档通信。</p> <p>XDM 的核心是 <code>postMessage()</code> 方法。在 HTML5 规范中,除了 XDM 部分之外的其他部分也会提到这个方法名,但都是为了同一个目的:向另一个地方传递数据。对于 XDM 而言, “另一个地方” 指的是包含在当前页面中的<code><iframe></code>元素,或者由当前页面弹出的窗口。</p> </blockquote> <p>发送消息:iframe 的 contentWindow 的<code>postMessage()</code> 方法</p> <p>处理消息:window 的<code>onmessage</code>事件</p> <h2 id="原生拖放">16.2 原生拖放</h2> <p>拖动某元素时,将依次触发下列事件:</p> <ol> <li>dragstart</li> <li>drag</li> <li>dragend</li> </ol> <p>当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生:</p> <ol> <li>dragenter</li> <li>dragover</li> <li>dragleave 或 drop</li> </ol> <p>使用<code>DragEvent</code>的<code>dataTransfer</code>属性的<code>setData()</code>和<code>getData()</code>方法可以实现拖放的数据交换。</p> <p>默认情况下图片、链接文本可以拖动,可以通过设置<code>draggable</code>属性为<code>true</code>使其他元素可拖动。</p> <pre><code class="language-html"><code><div id="destination" style="width: 200px; height: 200px; background: green;" ondrop="alert(event.dataTransfer.getData('text'))" >destination</div> <div id="box" style="display: inline; background: lightyellow; cursor: all-scroll;" draggable="true" ondragstart="event.dataTransfer.setData('text/plain', 'test text')" >drag me to destination</div></code></code></pre> <h2 id="媒体元素">16.3 媒体元素</h2> <p><code><video></code>和<code><audio></code>这两个媒体元素都有一个<code>canPlayType()</code>方法检测浏览器是否支持某种格式和解码器。</p> <p>Audio 不用像 Image 那样必须插入到文档中,只要创建一个实例并传入音频即可使用。</p> <h2 id="历史状态管理">16.4 历史状态管理</h2> <ul> <li>使用<code>history.pushState()</code>方法增加历史状态</li> <li>用户点击后退后触发<code>popState</code>事件</li> </ul> <p>注意:请确保每个<code>pushState()</code>创造的假的 URL,服务器上都有一个真的 URL 与之对应, 否侧用户点击刷新会导致 404 错误。</p> <h1 id="第-17-章-错误处理与调试">第 17 章 错误处理与调试</h1> <h2 id="错误处理">17.2 错误处理</h2> <h3 id="try-catch-语句">17.2.1 try-catch 语句</h3> <blockquote> <p>ECMA-262 第 3 版引入了 try-catch 语句,作为 JavaScript 中处理异常的一种标准方式。这与 Java 中的 try-catch 语句是完全相同的。</p> </blockquote> <pre class="js"><code>try{ // 可能会导致错误的代码 } catch(error){ // 在错误发生时怎么处理 alert(error.message) // message 属性是唯一一个能够保证所有浏览器都支持的属性 }</code></pre> <blockquote> <p>只要代码中包含 finally 子句,则无论 try 或 catch 语句块中包含什么代码——甚至 return 语句,都不会阻止 finally 子句的执行。</p> </blockquote> <pre><code class="language-html"><code><script> alert(testFinally());// 0 function testFinally(){ try { return 2;// 不执行 alert('try');// 不执行 } catch (error){ return 1; } finally { return 0; } } </script></code></code></pre> <p>只要代码中包含 finally 子句,无论 try 还是 catch 语句块中的 return 语句都会被忽视。</p> <h3 id="抛出错误">17.2.2 抛出错误</h3> <blockquote> <p>与 try-catch 语句相匹配的还有 throw 操作符,用于抛出自定义错误。</p> <p>遇到 throw 操作符时代码会立即停止,仅当有 try-catch 语句捕获到抛出的值时,代码才会继续执行。</p> <p>利用原型链可以通过继承 Error 来创建自定义错误类型。</p> </blockquote> <h3 id="错误error事件">17.2.3 错误(error)事件</h3> <pre><code class="language-html"><code><!-- 图像 --> <img src="xxx" onerror="console.log('img not loaded')"> <!-- window --> <script> window.onerror = function(message, source, lineno, colno, error) { console.log(message); } throw "test"; </script></code></code></pre> <p>GlobalEventHandlers.onerror - Web APIs | MDN</p> <h3 id="常见的错误类型">17.2.5 常见的错误类型</h3> <ol> <li>类型转换错误:注意类型的自动转换引起的错误,尤其是在作为判断条件时</li> <li>数据类型错误:使用参数和变量前应该保证其类型是正确的(这点利用 TS 可以非常好地解决)</li> <li>通信错误:对于查询字符串要使用<code>encodeURIComponent()</code>处理</li> </ol> <h3 id="区分致命错误和非致命错误">17.2.6 区分致命错误和非致命错误</h3> <p>非致命错误,可以根据下列一或多个条件来确定:</p> <ul> <li>不影响用户的主要任务;</li> <li>只影响页面的一部分;</li> <li>可以恢复;</li> <li>重复相同操作可以消除错误。</li> </ul> <p>eg:非致命错误添加 try-catch 可以使非致命错误发生后后续代码继续执行,后面的模块继续加载</p> <p>致命错误,可以通过以下一或多个条件来确定:</p> <ul> <li>应用程序根本无法继续运行;</li> <li>错误明显影响到了用户的主要操作;</li> <li>会导致其他连带错误。</li> </ul> <h3 id="把错误记录到服务器">17.2.7 把错误记录到服务器</h3> <p>推荐将 JS 错误写回服务器,把前后端的错误集中起来能够极大地方便对数据的分析。</p> <blockquote> <p>建立这样一种 JavaScript 错误记录系统,首先需要在服务器上创建一个页面(或者一个服务器入口点),用于处理错误数据。这个页面的作用无非就是从查询字符串中取得数据,然后再将数据写入错误日志中。这个页面可能会使用如下所示的函数:</p> </blockquote> <pre><code>function logError(sev, msg){ var img = new Image(); img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" + encodeURIComponent(msg); }</code></pre> <blockquote> <p>这个 logError() 函数接收两个参数:表示严重程度的数值或字符串(视所用系统而异)及错误消息。其中,使用了 Image 对象来发送请求,这样做非常灵活,主要表现如下几方面。</p> </blockquote> <ul> <li>所有浏览器都支持 Image 对象,包括那些不支持 XMLHttpRequest 对象的浏览器。</li> <li>可以避免跨域限制。通常都是一台服务器要负责处理多台服务器的错误,而这种情况下使用 XMLHttpRequest 是不行的。</li> <li>在记录错误的过程中出问题的概率比较低。大多数 Ajax 通信都是由 JavaScript 库提供的包装函数来处理的,如果库代码本身有问题,而你还在依赖该库记录错误,可想而知,错误消息是不可能得到记录的。</li> </ul> <h1 id="第-18-章-javascript-与-xml">第 18 章 JavaScript 与 XML</h1> <ul> <li>DOMParser 和 XMLSerializer 接口提供的 XML 和 DOM 文档的转换</li> <li>XPath 查找 XML 节点</li> <li>XSLT 转换 XML 文档</li> </ul> <h1 id="第-19-章-e4x">第 19 章 E4X</h1> <p>E4X 已经废弃了(E4X - Archive of obsolete content | MDN),大概看了一下感觉 JSX 和 XML 字面量有点像啊(PS:确实只是有点像,JSX | XML-like syntax extension to ECMAScript)。</p> <h1 id="第-20-章-json">第 20 章 JSON</h1> <p>JSON 的语法可以表示以下三种类型的值。</p> <ul> <li>简单值:使用与 JavaScript 相同的语法,可以在 JSON 中表示字符串、数值、布尔值和 null。但 JSON 不支持 JavaScript 中的特殊值 undefined。</li> <li>对象:对象作为一种复杂数据类型,表示的是一组无序的键值对儿。而每个键值对儿中的值可以是简单值,也可以是复杂数据类型的值。</li> <li>数组:数组也是一种复杂数据类型,表示一组有序的值的列表,可以通过数值索引来访问其中的值。数组的值也可以是任意类型——简单值、对象或数组。</li> </ul> <blockquote> <p>与 JavaScript 的对象字面量相比, JSON 对象有两个地方不一样。首先,没有声明变量(JSON 中没有变量的概念)。其次,没有末尾的分号(因为这不是 JavaScript 语句,所以不需要分号)。再说一遍,对象的属性必须加双引号,这在 JSON 中是必需的。属性的值可以是简单值,也可以是复杂类型值。</p> </blockquote> <h2 id="序列化和解析">20.2 序列化和解析</h2> <p>与 XML 数据结构解析成 DOM 文档提取数据很麻烦,而 JSON 解析为 JS 对象提取数据非常简单。</p> <h3 id="json-对象">20.2.1 JSON 对象</h3> <p>早期 JSON 解析器基本上就是使用 JS 的<code>eavl()</code>函数,由于 JSON 是 JS 语法的子集,因此<code>eavl()</code>函数可以解析并返回数据。ES5 对即系解析 JSON 的行为进行规范,定义了全局对象 JSON。</p> <p>JSON 对象有两个方法:<code>stringify()</code>和<code>parse()</code>。</p> <h3 id="序列化选项">20.2.2 序列化选项</h3> <blockquote> <p><code>JSON.stringify()</code> 除了要序列化的 JavaScript 对象外,还可以接收另外两个参数,这两个参数用于指定以不同的方式序列化 JavaScript 对象。第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在 JSON 字符串中保留缩进。</p> </blockquote> <p>可以为对象添加<code>toJSON()</code>方法,这个方法会返回自身的 JSON 数据格式。</p> <p>例如:</p> <pre class="js"><code>var purple = { name: "zzz", age: 17, toJSON() { return { age: this.age, fans: [] } } }; JSON.stringify(purple, (k, v) => k === "age" ? 99999 : v, 4); // '{\n "age": 99999,\n "fans": []\n}'</code></pre> <h3 id="解析选项">20.2.3 解析选项</h3> <blockquote> <p><code>JSON.parse()</code>方法也可以接收另一个参数,该参数是一个函数,将在每个键值对上调用。</p> </blockquote> <p>例如:</p> <pre class="js"><code>var purple = { name: "zzz", age: 17, birthday: new Date(0) }; var str = JSON.stringify(purple); var obj = JSON.parse(str, (k, v) => k === "birthday" ? new Date(v) : v); console.log(obj.birthday.getFullYear());// 1970</code></pre> <h1 id="第-21-章-ajax-与-comet">第 21 章 Ajax 与 Comet</h1> <h2 id="xmlhttprequest-对象">21.1 XMLHttpRequest 对象</h2> <p>XMLHttpRequest - Web APIs | MDN</p> <pre><code class="language-javascript"><code>var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } } }; xhr.open("GET", "example.txt", true); xhr.send(null);</code></code></pre> <h3 id="get-请求">21.1.3 GET 请求</h3> <blockquote> <p>查询字符串中每个参数的名称和值必须使用<code>encodeURIComponent()</code>进行编码然后放到 URL 的末尾。</p> </blockquote> <ol> <li>使用<code>open()</code>方法时第一个参数为 GET(书上是小写,应该是打错了)</li> <li>构建带查询字符串的 URL</li> </ol> <h3 id="post-请求">21.1.4 POST 请求</h3> <blockquote> <p>POST 请求应该把数据作为请求主体提交。</p> <p>POST 请求的主体可以包含非常多的数据,而且格式不限。</p> </blockquote> <ol> <li>使用<code>open()</code>方法时第一个参数为 POST(书上是小写,应该是打错了)</li> <li>使用<code>send()</code>方法发送数据</li> </ol> <h2 id="xmlhttprequest-2-级">21.2 XMLHttpRequest 2 级</h2> <h3 id="formdata">21.2.1 FormData</h3> <p>FormData - Web APIs | MDN</p> <pre><code class="language-html"><code><form> <input type="text" name="inp"> <input type="radio" name="ra" value="male" checked> <input type="radio" name="ra" value="famale"> <select name="sel"> <option>t1</option> <option>t2</option> </select> </form> <script> var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } } }; xhr.open("POST", "example.php"); xhr.send(new FormData(document.forms[0])); </script></code></code></pre> <h2 id="进度事件">21.3 进度事件</h2> <p>XMLHttpRequest: progress event - Web APIs | MDN</p> <p>当请求接收到数据这个事件会定期触发。</p> <h2 id="跨源资源共享">21.4 跨源资源共享</h2> <blockquote> <p>CORS(Cross-Origin Resource Sharing,跨源资源共享)是 W3C 的一个工作草案,定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通。 CORS 背后的基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。</p> <p>比如一个简单的使用 GET 或 POST 发送的请求,它没有自定义的头部,而主体内容是 text/plain。在发送该请求时,需要给它附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。下面是 Origin 头部的一个示例:</p> </blockquote> <p><code>Origin: http://www.nczonline.net</code></p> <blockquote> <p>如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发 "*")。例如:</p> </blockquote> <p><code>Access-Control-Allow-Origin: http://www.nczonline.net</code></p> <blockquote> <p>如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意,请求和响应都不包含 cookie 信息。</p> </blockquote> <p>浏览器对 CORS 的实现:</p> <blockquote> <p>要请求位于另一个域中的资源,使用标准的 XHR 对象并在<code>open()</code>方法中传入绝对 URL 即可:</p> </blockquote> <pre class="js"><code>var xhr = new XMLHttpRequest(); xhr.onload = function(){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } }; xhr.open("GET", "http://www.somewhere-else.com"); xhr.send(null);</code></pre> <p>跨越 XHR 的限制:</p> <ol> <li>不能使用<code>setRequestHeader()</code>设置自定义请求头</li> <li>不能发送和接收 cookie</li> <li>调用<code>getAllResponseHeaders()</code>总会返回空字符串</li> </ol> <h3 id="preflighted-request">21.4.3 Preflighted Request</h3> <p>Preflight request - MDN Web Docs Glossary: Definitions of Web-related terms | MDN</p> <blockquote> <p>一个 CORS 预检请求是用于检查服务器是否支持 CORS 即跨域资源共享。</p> <p>它一般是用了以下几个 HTTP 请求首部的 <code>OPTIONS</code> 请求:<code>Access-Control-Request-Method</code> 和 <code>Access-Control-Request-Headers</code>,以及一个 <code>Origin</code> 首部。</p> <p>当有必要的时候,浏览器会自动发出一个预检请求;所以在正常情况下,前端开发者不需要自己去发这样的请求。</p> </blockquote> <h3 id="带凭据请求">21.4.4 带凭据请求</h3> <blockquote> <p>默认情况下,跨域请求不提供凭据(cookie,HTTP 认证,及客户端 SSL 证明等)。通过将 withCredentials 设置为 true,可以指定某个请求应该发送凭据。</p> <p>IE10 及更早版本都不支持!</p> </blockquote> <h2 id="其他跨域技术">21.5 其他跨域技术</h2> <h3 id="图像-ping">21.5.1 图像 Ping</h3> <p>无法处理相应的信息,只能使用 onload 和 onerror 确定是否接收到相应,因此只能用于浏览器与服务器单向通信。</p> <pre><code class="language-javascript"><code>var img = new Image(); img.onload = img.onerror = function(){ alert("Done!"); }; img.src = "http://www.example.com/test?name=Nicholas";</code></code></pre> <h3 id="jsonp">21.5.2 JSONP</h3> <p>例:通过查询地理定位服务来显示你的 IP 地址和位置信息。(接口已经废弃:apilayer/freegeoip: IP geolocation web server)</p> <pre><code class="language-javascript"><code>function handleResponse(response){ alert("You’ re at IP address " + response.ip + ", which is in " + response.city + ", " + response.region_name); } var script = document.createElement("script"); script.src = "http://freegeoip.net/json/?callback=handleResponse"; document.body.insertBefore(script, document.body.firstChild);</code></code></pre> <p>问题:</p> <ol> <li>执行其他域的代码不安全</li> <li>确定 JSONP 请求是否失败不容易(目前除了 FireFox 和 Chrome,其他浏览器的对 onerror 事件的支持都是未知。GlobalEventHandlers.onerror - Web APIs | MDN)</li> </ol> <h2 id="comet">Comet</h2> <blockquote> <p>Ajax 是一种从页面向服务器请求数据的技术,而 Comet 则是一种服务器向页面推送数据的技术。 Comet 能够让信息近乎实时地被推送到页面上,非常适合处理体育比赛的分数和股票报价。</p> </blockquote> <p>实现方式:</p> <ol> <li>使用长轮询:页面发起请求等待服务器回复,服务器回复后开启发起新请求</li> <li>使用 HTTP 流:浏览器发送一个请求,服务器保持连接打开,然后周期性地像浏览器发送数据,浏览器通过 readystatechange 事件检测 readyState 是否为 3 就可以利用 HTTP 流。</li> </ol> <h2 id="sse-与-web-sockets">SSE 与 Web Sockets</h2> <ul> <li>EventSource - Web APIs | MDN</li> <li>WebSocket - Web APIs | MDN</li> </ul> <blockquote> <p>面对某个具体的用例,在考虑是使用 SSE 还是使用 Web Sockets 时,可以考虑如下几个因素。</p> <p>首先,你是否有自由度建立和维护 Web Sockets 服务器?因为 Web Socket 协议不同于 HTTP,所以现有服务器不能用于 Web Socket 通信。 SSE 倒是通过常规 HTTP 通信,因此现有服务器就可以满足需求。</p> <p>第二个要考虑的问题是到底需不需要双向通信。如果用例只需读取服务器数据(如比赛成绩),那么 SSE 比较容易实现。如果用例必须双向通信(如聊天室),那么 Web Sockets 显然更好。别忘了,在不能选择 Web Sockets 的情况下,组合 XHR 和 SSE 也是能实现双向通信的。</p> </blockquote> <h2 id="安全">21.6 安全</h2> <blockquote> <p>对于未被授权系统有权访问某个资源的情况,我们称之为 CSRF(Cross-Site Request Forgery,跨站点请求伪造)。</p> </blockquote> <p>为了确保通过 XHR 访问的 URL 安全,通行的做法就是验证发送请求者是否有权访问相应的资源。有以下方法可供选择:</p> <ul> <li>通过 SSL 连接来访问可以通过 XHR 请求的资源</li> <li>每次请求都携带 token</li> </ul> <h1 id="第-22-章-高级技巧">第 22 章 高级技巧</h1> <h2 id="高级函数">22.1 高级函数</h2> <h3 id="安全类型检测">22.1.1 安全类型检测</h3> <pre class="js"><code>Object.prototype.toString.call(JSON); // '[object JSON]' JSON = function(){}; Object.prototype.toString.call(JSON); // '[object Function]'</code></pre> <h3 id="作用域安全的构造函数">22.1.2 作用域安全的构造函数</h3> <p>浏览器控制台运行下面的例子,你会神奇地发现页面居然跳转了(好神奇 =_=!!!)</p> <pre class="js"><code>function Persion(loc){ this.location = loc; } var zi = Persion('zi');// 不小心忘记使用 new</code></pre> <p>这时您需要用作用域安全的构造函数,然后你会神奇地发现页面又跳转了(好神奇 =_=!!!)</p> <pre class="js"><code>function Persion(loc){ if(this instanceof Persion){ this.location = loc; } else { return new Persion(loc) } } var zi = Persion('zi');// 又不小心忘记使用 new</code></pre> <p>使用作用域安全的构造函数后您就必须要用原型链了。</p> <pre class="js"><code>function Persion(loc){ if(this instanceof Persion){ this.location = loc; } else { return new Persion(loc) } } function ZM(loc){ Persion.call(this, loc); this.age = 17; } ZM.prototype = new Persion(); // 不将原型链连上就无法继承 ZM.prototype.constructor = ZM; var zi = new ZM('zi');</code></pre> <h3 id="惰性载入函数">22.1.3 惰性载入函数</h3> <blockquote> <p>惰性载入表示函数执行的分支仅会发生一次。有两种实现惰性载入的方式</p> <p>第一种就是在函数被调用时再处理函数。在第一次调用的过程中,该函数会被覆盖为另外一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支了。</p> <p>第二种实现惰性载入的方式是在声明函数时就指定适当的函数。这样,第一次调用函数时就不会损失性能了,而在代码首次加载时会损失一点性能。</p> </blockquote> <pre class="js"><code>// 第一种方式 function flatArr(arr){ if(typeof Array.prototype.flat === "function"){ flatArr = arr => arr.flat(); }else{ flatArr = arr => { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat const stack = [...arr]; const res = []; while (stack.length) { // pop value from stack const next = stack.pop(); if (Array.isArray(next)) { // push back array items, won't modify the original input stack.push(...next); } else { res.push(next); } } //reverse to restore input order return res.reverse(); } } return flatArr(arr); } flatArr([1, 2, 3, [4, 5, 6]]);</code></pre> <pre class="js"><code>// 第二种方式 var flatArr = (function(){ if(typeof Array.prototype.flat === "function"){ return arr => arr.flat(); }else{ return arr => { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat const stack = [...arr]; const res = []; while (stack.length) { // pop value from stack const next = stack.pop(); if (Array.isArray(next)) { // push back array items, won't modify the original input stack.push(...next); } else { res.push(next); } } //reverse to restore input order return res.reverse(); } } })(); flatArr([1, 2, 3, [4, 5, 6]]);</code></pre> <h3 id="函数绑定">22.1.4 函数绑定</h3> <blockquote> <p>另一个日益流行的高级技巧叫做函数绑定。函数绑定要创建一个函数,可以在特定的 this 环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。</p> </blockquote> <p>使用函数的<code>bind()</code>方法。被绑定函数与普通函数相比需要更多开销,最好只在必要时使用。</p> <pre class="js"><code>var handler = { msg: "text", handleClick: function(){ alert(this.msg) } } // not bind document.addEventListener('click', handler.handleClick); // bind document.addEventListener('click', handler.handleClick.bind(handler));</code></pre> <h3 id="函数柯里化">22.1.5 函数柯里化</h3> <p>函数柯里化(function currying),用于创建已经设置好一个或多个参数的函数。</p> <pre class="js"><code>function curry(fn, ...args){ return function(...innerArgs){ return fn(...args, ...innerArgs); } } function add(a, b){ return a + b; } var curriedAdd = curry(add, 3); console.log(curriedAdd(5)); // 8 // 精简版 var add2 = a => b => a + b; console.log(add2(3)(5)); // 8</code></pre> <h2 id="防篡改对象">22.2 防篡改对象</h2> <p>注意:一旦将对象定义为防篡改就无法撤销了。</p> <h3 id="不可拓展对象">22.2.1 不可拓展对象</h3> <p>禁止添加属性。</p> <p>Object.preventExtensions() - JavaScript | MDN</p> <h3 id="密封对象">22.2.2 密封对象</h3> <p>禁止添加属性,且已有成员的<code>[[Configurable]]</code>特性设为为<code>false</code>(不能删除和修改访问器属性)。</p> <p>Object.seal() - JavaScript | MDN</p> <h3 id="冻结的对象">22.2.3 冻结的对象</h3> <p>禁止添加属性,且已有成员的<code>[[Configurable]]</code>特性设为为<code>false</code>(不能删除和修改访问器属性),且已有成员的<code>[[[Writable]]</code>特性设为<code>false</code>(如果定义<code>[[Set]]</code>函数,访问器依然是可写的)。</p> <p>Object.freeze() - JavaScript | MDN</p> <h2 id="高级定时器">22.3 高级定时器</h2> <p>JS 是单线程的,<code>setTimeout()</code>和<code>setInterval()</code>只是在它们<strong>开始执行后</strong>过了设置的时间,将回调函数添加到事件队列。如果还没有到设置的事件就清除了,这个回调函数就不会添加到事件队列了。</p> <h3 id="重复的定时器">22.3.1 重复的定时器</h3> <p>使用<code>setTimeout()</code>代替<code>setInterval()</code>可以解决<code>setInterval()</code>在任务队列已有该回调函数时不会重复添加的问题。</p> <h3 id="yielding-processes">22.3.2 Yielding Processes</h3> <blockquote> <p>运行在浏览器中的 JavaScript 都被分配了一个确定数量的资源。不同于桌面应用往往能够随意控制他们要的内存大小和处理器时间, JavaScript 被严格限制了,以防止恶意的 Web 程序员把用户的计算机搞挂了。其中一个限制是长时间运行脚本的制约,如果代码运行超过特定的时间或者特定语句数量就不让它继续执行。如果代码达到了这个限制,会弹出一个浏览器错误的对话框,告诉用户某个脚本会用过长的时间执行,询问是允许其继续执行还是停止它。所有 JavaScript 开发人员的目标就是,确保用户永远不会在浏览器中看到这个令人费解的对话框。定时器是绕开此限制的方法之一。</p> </blockquote> <p>不需同步和按顺序完成的循环可以使用定时器进行分割,这时一种叫数组分块(array chunking)的技术。</p> <pre class="js"><code>function chunk(arr, process, context){ setTimeout(function(){ let item = arr.shift(); process.call(context, item); if(arr.length){ setTimeout(arguments.callee, 100) } }, 100) } chunk([1, 2, 3], d => console.log(d));</code></pre> <h3 id="函数节流throttle和防反弹-debounce">22.3.3 函数节流(throttle)和防反弹 (debounce)</h3> <blockquote> <p>浏览器中某些计算和处理要比其他的昂贵很多。例如, DOM 操作比起非 DOM 交互需要更多的内存和 CPU 时间。连续尝试进行过多的 DOM 相关操作可能会导致浏览器挂起,有时候甚至会崩溃。尤其在 IE 中使用 onresize 事件处理程序的时候容易发生,当调整浏览器大小的时候,该事件会连续触发。在 onresize 事件处理程序内部如果尝试进行 DOM 操作,其高频率的更改可能会让浏览器崩溃。为了绕开这个问题,你可以使用定时器对该函数进行节流。</p> <p>函数节流背后的基本思想是指,某些代码不可以在没有间断的情况连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行。</p> </blockquote> <p>PS:书中的函数节流应该叫防反弹比较合适。</p> <p>Underscore.js - throttle</p> <p>Underscore.js - debounce</p> <pre class="js"><code>// 书中将下面的函数叫做 throttle function debounce(callback){ let timeoutID; function wrapper () { clearTimeout(timeoutID); timeoutID = setTimeout(callback, 1000); } return wrapper; } function throttle(callback){ let lastExec = 0; function wrapper () { if(Date.now() - lastExec > 1000){ lastExec = Date.now(); setTimeout(callback, 1000); } } return wrapper; } var resizeThrottle = throttle(() => console.log('throttle')); var resizeDebounce = debounce(() => console.log('debounce')); window.onscroll = () => { resizeThrottle(); resizeDebounce(); }</code></pre> <h2 id="自定义事件">22.4 自定义事件</h2> <blockquote> <p>事件是一种叫做观察者的设计模式,这是一种创建松散耦合代码的技术。对象可以发布事件,用来表示在该对象生命周期中某个有趣的时刻到了。然后其他对象可以观察该对象,等待这些有趣的时刻到来并通过运行代码来响应。</p> <p>观察者模式由两类对象组成:主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即使观察者不存在。从另一方面来说,观察者知道主体并能注册事件的回调函数(事件处理程序)。涉及 DOM 上时, DOM 元素便是主体,你的事件处理代码便是观察者。</p> </blockquote> <p>EventTarget - Web APIs | MDN</p> <pre class="js"><code>function EventTarget(){ this.handlers = {}; } EventTarget.prototype = { constructor: EventTarget, addHandler: function(type, handler){ if (typeof this.handlers[type] == "undefined"){ this.handlers[type] = []; } this.handlers[type].push(handler); }, fire: function(event){ if (!event.target){ event.target = this; } if (this.handlers[event.type] instanceof Array){ var handlers = this.handlers[event.type]; for (var i=0, len=handlers.length; i < len; i++){ handlers[i](event); } } }, removeHandler: function(type, handler){ if (this.handlers[type] instanceof Array){ var handlers = this.handlers[type]; for (var i=0, len=handlers.length; i < len; i++){ if (handlers[i] === handler){ break; } } handlers.splice(i, 1); } } }; function handleMessage(event){ alert("Message received: " + event.message); } //创建一个新对象 var target = new EventTarget(); //添加一个事件处理程序 target.addHandler("message", handleMessage); //触发事件 target.fire({ type: "message", message: "Hello world!"}); //删除事件处理程序 target.removeHandler("message", handleMessage); //再次,应没有处理程序 target.fire({ type: "message", message: "Hello world!"});</code></pre> <h2 id="拖放">22.5 拖放</h2> <p>使用 DragEvent - Web APIs | MDN 或者使用鼠标事件进行模拟。</p> <h1 id="第-23-章-离线应用与客户端存储">第 23 章 离线应用与客户端存储</h1> <p>支持离线 Web 应用开发是 HTML5 的另一个重点。</p> <p>开发离线 Web 应用:</p> <ol> <li>检测设备是否可以上网</li> <li>能访问资源(图片、CSS、JS 等)</li> <li>本地空间用于保存数据</li> </ol> <h2 id="离线检测">23.1 离线检测</h2> <p>通过<code>navigator.onLine</code>属性检测是否离线。</p> <p>在线离线状态切换时会触发 window 的 online 和 offline 事件。</p> <h2 id="应用缓存application-cache">23.2 应用缓存(application cache)</h2> <p>书中的方法已经废弃!!!现在应该这样实现:通过 Service workers 让 PWA 离线工作 - 渐进式 Web 应用(PWA) | MDN</p> <p><del>使用描述文件(manifest file)例如出要下载和缓存的资源。</del></p> <p><del>可以在<code><html></code>标签的 manifest 属性中指定这个文件的路径,例如<code><html mainfest="/offline.manifest"></code>。</del></p> <h2 id="数据存储">23.3 数据存储</h2> <h3 id="cookie">23.3.1 Cookie</h3> <p>cookies.Cookie - Mozilla | MDN</p> <ol> <li>限制:50 个以内,总长度 4095B 以内</li> <li>构成:名称、值域、路径、失效时间、安全标志、只供 HTTP 使用等</li> <li>子 cookie:使用一个 cookie 的值存过个键值对儿</li> </ol> <h3 id="web-存储机制">23.3.3 Web 存储机制</h3> <p>Storage - Web API 接口参考 | MDN</p> <ol> <li>sessionStorage 对象:存储到页面关闭,同会话共享(新标签或窗口中打开页面会创建新的会话)</li> <li>localStorage 对象:永久存储,同域共享</li> </ol> <h3 id="indexeddb">23.3.4 IndexedDB</h3> <p>IndexedDB 是一个数据库,和 MySQL 等数据库类似。</p> <p>IndexedDB 最大的特点是使用对象保存数据,而不是表来保存数据。(和 MongoDB 有点像)</p> <p>一个 IndexedDB 数据库,就是一组位于相同命名空间下的对象的集合。</p> <p>IndexedDB API - Web APIs | MDN</p> <p>留坑。。</p> <h1 id="第-24-章-最佳实践">第 24 章 最佳实践</h1> <h2 id="可维护性">24.1 可维护性</h2> <p>编写可维护的代码很重要,因为大部分开发人员都花费大量时间维护他人代码。</p> <h3 id="可维护的代码的特征">24.1.1 可维护的代码的特征</h3> <ul> <li>可理解性</li> <li>直观性</li> <li>可适应性</li> <li>可扩展性</li> <li>可调试性</li> </ul> <h3 id="代码约定">24.1.2 代码约定</h3> <ol> <li>可读性:一般要在函数和方法、大段代码、复杂的算法、Hack 这些地方加上注释</li> <li>变量和函数名:变量应为名词,函数名应该以动词开头(返回布尔类型值得函数一般以 is 开头)</li> <li>变量类型透明:使用TypeScript或匈牙利标记法等方法。</li> </ol> <h3 id="松散耦合">24.1.3 松散耦合</h3> <ol> <li>解耦 HTML/JS:不要混这写,比如:如果使用 JSX 就尽量不要直接操作 DOM,如果直接操作 DOM 就尽量不要使用 JS 生成 HTML 标签。</li> <li>解耦 CSS/JS:尽量不用 JS 直接改 CSS,而是通过修改 class 间接修改样式。</li> <li>解耦应用逻辑 / 事件处理程序:事件处理程序应处理事件、然后将数据转交给应用逻辑处理。</li> </ol> <h3 id="编程实践">24.1.4 编程实践</h3> <ol> <li>尊重对象所有权:也许是企业环境中总重要的实践。 <ul> <li>不要为实例或原型添加属性和方法。</li> <li>不要重定义已经存在的方法。</li> </ul></li> <li>避免全局量:使用命名空间,虽然使用命名空间需要多写一些代码,但命名空间有助于确保代码和页面的其他代码以无害的方式一起工作。</li> <li>使用常量:以下情况可以考虑使用常量。 <ul> <li>重复值:多处用到的值应抽取为常量,包括 CSS 类名等。</li> <li>用户界面字符串:方便国际化。</li> <li>URLs:Web 应用中资源位置很容易变,推荐用一个公共的地方存 URL。</li> <li>任意可能会更改的值:当您用到字面量时,先考虑一下这个值在未来是不是会变化,如果会变化就应该提取出来作为常量。</li> </ul></li> </ol> <h2 id="性能">24.2 性能</h2> <h3 id="注意作用域">24.2.1 注意作用域</h3> <ol> <li>避免全局查找:将在一个函数中多次用到的全局对象存储为局部变量总是没错的。</li> <li>避免 with 语句:with 会创建自己的作用域链,会增加其中执行的代码的作用域链长度。</li> </ol> <h3 id="选择正确的方法">24.2.2 选择正确的方法</h3> <p>数据量大的循环可以考虑:Duff's device</p> <h3 id="优化-dom-交互">24.2.4 优化 DOM 交互</h3> <ol> <li><p>最小化现场更新:通过文档片段(fragment)将 DOM 一起更新。</p> <blockquote> <p>一旦你需要访问的 DOM 部分是已经显示的页面的一部分,那么你就是在进行一个现场更新。之所以叫现场更新,是因为需要立即(现场)对页面对用户的显示进行更新。每一个更改,不管是插入单个字符,还是移除整个片段,都有一个性能惩罚,因为浏览器要重新计算无数尺寸以进行更新。现场更新进行得越多,代码完成执行所花的时间就越长;完成一个操作所需的现场更新越少,代码就越快。</p> </blockquote></li> <li><p>使用 innerHTML:对于大量 DOM 更改,innerHTML 比 DOM 方法快。</p> <blockquote> <p>有两种在页面上创建 DOM 节点的方法:使用诸如 createElement() 和 appendChild() 之类的 DOM 方法,以及使用 innerHTML。对于小的 DOM 更改而言,两种方法效率都差不多。然而,对于大的 DOM 更改,使用 innerHTML 要比使用标准 DOM 方法创建同样的 DOM 结构快得多。</p> </blockquote></li> <li><p>使用事件代理:将事件处理程序附加到更高层的地方负责多个目标的事件处理。</p> <blockquote> <p>大多数 Web 应用在用户交互上大量用到事件处理程序。页面上的事件处理程序的数量和页面响应用户交互的速度之间有个负相关。为了减轻这种惩罚,最好使用事件代理。</p> </blockquote></li> <li><p>注意 HTMLCollection</p> <blockquote> <p>HTMLCollection 对象的陷阱已经在本书中讨论过了,因为它们对于 Web 应用的性能而言是巨大的损害。记住,任何时候要访问 HTMLCollection,不管它是一个属性还是一个方法,都是在文档上进行一个查询,这个查询开销很昂贵。最小化访问 HTMLCollection 的次数可以极大地改进脚本的性能。</p> </blockquote></li> </ol> <h2 id="部署">24.3 部署</h2> <h3 id="构建过程">24.3.1 构建过程</h3> <p>软件开发典型模式:写代码 -> 编译 -> 测试</p> <p>JS 是非编译型语言,模式变成:写代码 -> (语法转换) -> 测试</p> <p>现在通常是使用 ES6 和更新的语法,一些语法测试和生产环境还不支持,需要用进行语法转换(一般使用 Babe)。</p> <h3 id="验证">24.3.2 验证</h3> <p>XXLint(常见的有 JSLint、TSLint、ESLint) 可以查找代码中的语法错误以及常见的编码错误。</p> <h3 id="压缩">24.3.3 压缩</h3> <blockquote> <p>当谈及 JavaScript 文件压缩,其实在讨论两个东西:代码长度和配重(Wire weight)。代码长度指的是浏览器所需解析的字节数,配重指的是实际从服务器传送到浏览器的字节数。在 Web 开发的早期,这两个数字几乎是一样的,因为从服务器端到客户端原封不动地传递了源文件。而在今天的 Web 上,这两者很少相等,实际上也不应相等。</p> </blockquote> <ol> <li>文件压缩:删除额外的空白、删除注释、缩短变量名等</li> <li><p>HTTP 压缩</p> <blockquote> <p>配重指的是实际从服务器传送到浏览器的字节数。因为现在的服务器和浏览器都有压缩功能,这个字节数不一定和代码长度一样。所有的五大 Web 浏览器(IE、 Firefox、 Safari、 Chrome 和 Opera)都支持对所接收的资源进行客户端解压缩。这样服务器端就可以使用服务器端相关功能来压缩 JavaScript 文件。一个指定了文件使用了给定格式进行了压缩的 HTTP 头包含在了服务器响应中。接着浏览器会查看该 HTTP 头确定文件是否已被压缩,然后使用合适的格式进行解压缩。结果是和原来的代码量相比在网络中传递的字节数量大大减少了。</p> </blockquote></li> </ol> <h1 id="第-25-章-新兴的-api">第 25 章 新兴的 API</h1> <h2 id="requestanimationframe">25.1 requestAnimationFrame()</h2> <pre><code class="language-html"><code><style> div { width: 200px; height: 20px; } #bar { border: 1px solid #000; position: relative; background-color: #666; overflow: hidden; } #progress { position: absolute; background-color: #fff; top: 0; left: 0; } </style> <div id="bar"> <div id="progress"></div> </div> <script> var start = null; var element = document.getElementById("progress"); element.style.position = "absolute"; function step(timestamp) { if (!start) start = timestamp; var progress = timestamp - start; element.style.left = Math.min(progress / 10, 200) + "px"; if (progress < 2000) { window.requestAnimationFrame(step); } } window.requestAnimationFrame(step); </script></code></code></pre> <h2 id="page-visibility-api">25.2 Page Visibility API</h2> <blockquote> <p>不知道用户是不是正在与页面交互,这是困扰广大 Web 开发人员的一个主要问题。如果页面最小化了或者隐藏在了其他标签页后面,那么有些功能是可以停下来的,比如轮询服务器或者某些动画效果。而 Page Visibility API(页面可见性 API)就是为了让开发人员知道页面是否对用户可见而推出的。</p> </blockquote> <ul> <li><code>document.hidden</code>:页面是否隐藏</li> <li><code>document.visibilityState</code>:表示当前页面的可视状态</li> </ul> <h2 id="geolocation-api">25.3 Geolocation API</h2> <blockquote> <p>地理定位(geolocation)是最令人兴奋,而且得到了广泛支持的一个新 API。 通过这套 API, JavaScript 代码能够访问到用户的当前位置信息。当然,访问之前必须得到用户的明确许可,即同意在页面中共享其位置信息。如果页面尝试访问地理定位信息,浏览器就会显示一个对话框,请求用户许可共享其位置信息。</p> </blockquote> <p>使用<code>navigator.geolocation</code>对象</p> <h2 id="file-api">25.4 File API</h2> <blockquote> <p>不能直接访问用户计算机中的文件,一直都是 Web 应用开发中的一大障碍。 2000 年以前,处理文件的唯一方式就是在表单中加入<code><input type="file"></code>字段,仅此而已。 File API(文件 API)的宗旨是为 Web 开发人员提供一种安全的方式,以便在客户端访问用户计算机中的文件,并更好地对这些文件执行操作。支持 File API 的浏览器有 IE10+、 Firefox 4+、 Safari 5.0.5+、 Opera 11.1 + 和 Chrome。</p> </blockquote> <p>HTML5 在 DOM 中为文件输入元素添加了一个 files 集合,里面包含着一组带有文件信息的 File 对象。</p> <h3 id="filereader">25.4.1 FileReader</h3> <blockquote> <p>FileReader 类型实现的是一种异步文件读取机制。可以把 FileReader 想象成 XMLHttpRequest,区别只是它读取的是文件系统,而不是远程服务器。</p> </blockquote> <h3 id="读取部分内容">25.4.2 读取部分内容</h3> <p>调用 blob 的 slice() 方法返回一个 Blob 实例,然后使用 FileReader 读取这个 Blob 实例。</p> <p>只读文件的一部分可以节省时间,适合只关注文件特定部分的情况。</p> <h3 id="对象-url">25.4.3 对象 URL</h3> <blockquote> <p>对象 URL 也被称为 blob URL,指的是引用保存在 File 或 Blob 中数据的 URL。使用对象 URL 的好处是可以不必把文件内容读取到 JavaScript 中而直接使用文件内容。为此,只要在需要文件内容的地方提供对象 URL 即可。要创建对象 URL,可以使用<code>window.URL.createObjectURL()</code>方法,并传入 File 或 Blob 对象。</p> </blockquote> <p>注意:要在不需要某个对象 URL 是手动释放内存。</p> <h3 id="读取拖放的内容">25.4.4 读取拖放的内容</h3> <p><code>通过 event.dataTransfer.files</code>获取文件信息。</p> <h3 id="使用-xhr-上传文件">25.4.5 使用 XHR 上传文件</h3> <p>可以使用 File API 读取文件内容然后 post 给后端(这样后端收到的是问价的内容,还要再将她们保存),但更方便的做法是以提交表单的方式上传文件(这样后端就像接收到常规表单一样处理就行):</p> <ol> <li>创建 21 章介绍的 FormData 的对象</li> <li>调用 FormData 对象的<code>append()</code>方法添加文件</li> <li>调用 XHR 对象的<code>send()</code>方法发送 FormData 对象</li> </ol> <h2 id="web-计时">25.5 Web 计时</h2> <p>Performance:用于查看页面加载的各个阶段的时间(13 位的时间戳,精确到毫秒)</p> <h2 id="web-workers">25.6 Web Workers</h2> <blockquote> <p>随着 Web 应用复杂性的与日俱增,越来越复杂的计算在所难免。长时间运行的 JavaScript 进程会导致浏览器冻结用户界面,让人感觉屏幕 “冻结” 了。 Web Workers 规范通过让 JavaScript 在后台运行解决了这个问题。浏览器实现 Web Workers 规范的方式有很多种,可以使用线程、后台进程或者运行在其他处理器核心上的进程,等等。具体的实现细节其实没有那么重要,重要的是开发人员现在可以放心地运行 JavaScript,而不必担心会影响用户体验了。</p> </blockquote> <p>非常消耗时间的操作转交给 Worker 就不会阻塞用户界面了,例如:使用 Worker 排序数组 web worker example - CodeSandbox。</p> <p>Worker 可以使用<code>importScripts()</code>方法导入其他脚本。</p> <p>Worker 分为专用 Worker(dedicated worker)和共享 Worker(shared worker),前者不能在页面间共享,后者可以在多个窗口,iframe 或 Worker 间共享。</p> <h1 id="附录-a-ecmascript-harmony">附录 A ECMAScript Harmony</h1> <p>书中好多特性(例如,迭代器对象、数组领悟等)都没有纳入 ES6 标准。</p> <h2 id="代理对象">代理对象</h2> <p><strong>Proxy</strong> 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。</p> <p>另一种描述:<strong>Proxy</strong> 对象可以处理(捕捉)原生功能,并用自己的函数处理。</p> <h2 id="映射与集合">映射与集合</h2> <p>用普通对象保存键值对融合与原生属性混淆,使用<code>Map</code>类型可以避免混淆。</p> <p><code>WeakMap</code>是 ES 中唯一一个能够让你知道对象什么时候已经完全解除引用的类型。例:Babel 将类的私用属性转换为 WeakMap,这样私有属性就不会强引用实例化的对象(强引用实例化的对象会导致对象内存无法释放)。</p> <pre><code class="language-javascript"><code>class Test { #t1 = 500 #t2 = 600 } // 转换后 var Test = function Test() { _classCallCheck(this, Test); _t.set(this, { writable: true, value: 500 }); _t2.set(this, { writable: true, value: 600 }); }; var _t = new WeakMap(); var _t2 = new WeakMap();</code></code></pre> <h1 id="附录-b-严格模式">附录 B 严格模式</h1> <h2 id="变量">变量</h2> <p>严格模式下不允许:</p> <ol> <li>意外创建全局变量</li> <li>对变量使用<code>delete</code>操作符</li> <li>不能使用保留字作为变量名</li> </ol> <h2 id="对象">对象</h2> <p>严格模式下错误地操作对象更容易报错(如,为只读属性赋值等)。</p> <h2 id="函数">函数</h2> <p>严格模式下不能使用<code>arguments.caller</code>和<code>arguments.callee</code></p> <h2 id="抑制this">抑制<code>this</code></h2> <p>非严格模式下使用函数的<code>call()</code>和<code>apply()</code>方法时<code>null</code>和<code>undefined</code>会转为为全局对象(这非常危险)。</p> <p>严格模式下<code>this</code>就是制定的值</p> <pre><code class="language-javascript"><code>function C1(a){ this.val1 = a; } C1.call(null, 123);// globalThis.val1 变为 123 function C2(a){ "use strict" this.val2 = a; } C.call(null, 456);// 报错</code></code></pre> <h1 id="附录-c-javascript-库">附录 C JavaScript 库</h1> <h2 id="通用库">通用库</h2> <h2 id="互联网应用">互联网应用</h2> <h2 id="动画和特效">动画和特效</h2> <h1 id="附录-d-javascript-工具">附录 D JavaScript 工具</h1> <h2 id="校验器">校验器</h2> <h2 id="模块打包">模块打包</h2> <h2 id="语法转换">语法转换</h2> <h2 id="单元测试">单元测试</h2> <h2 id="压缩器">压缩器</h2> <h2 id="文档生成器">文档生成器</h2> <h2 id="安全执行环境">(安全执行环境?)</h2> <p>ADsafe 和 Caja</p> </div> <p>转载于:https://www.cnblogs.com/jffun-blog/p/10463785.html</p> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1304820362541830144"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(《JavaScript 高级程序设计》)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1943992018892025856.htm" title="JSON 与 AJAX" target="_blank">JSON 与 AJAX</a> <span class="text-muted">Auscy</span> <a class="tag" taget="_blank" href="/search/json/1.htm">json</a><a class="tag" taget="_blank" href="/search/ajax/1.htm">ajax</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>一、JSON(JavaScriptObjectNotation)1.数据类型与语法细节支持的数据类型:基本类型:字符串(需用双引号)、数字、布尔值(true/false)、null。复杂类型:数组([])、对象({})。严格语法规范:键名必须用双引号包裹(如"name":"张三")。数组元素用逗号分隔,最后一个元素后不能有多余逗号。数字不能以0开头(如012会被解析为12),不支持八进制/十六进制</div> </li> <li><a href="/article/1943990125864218624.htm" title="JavaScript 树形菜单总结" target="_blank">JavaScript 树形菜单总结</a> <span class="text-muted">Auscy</span> <a class="tag" taget="_blank" href="/search/microsoft/1.htm">microsoft</a> <div>树形菜单是前端开发中常见的交互组件,用于展示具有层级关系的数据(如文件目录、分类列表、组织架构等)。以下从核心概念、实现方式、常见功能及优化方向等方面进行总结。一、核心概念层级结构:数据以父子嵌套形式存在,如{id:1,children:[{id:2}]}。节点:树形结构的基本单元,包含自身信息及子节点(若有)。展开/折叠:子节点的显示与隐藏切换,是树形菜单的核心交互。递归渲染:因数据层级不固定,</div> </li> <li><a href="/article/1943987101301272576.htm" title="精通Canvas:15款时钟特效代码实现指南" target="_blank">精通Canvas:15款时钟特效代码实现指南</a> <span class="text-muted">烟幕缭绕</span> <div>本文还有配套的精品资源,点击获取简介:HTML5的Canvas是一个用于绘制矢量图形的API,通过JavaScript实现动态效果。本项目集合了15种不同的时钟特效代码,帮助开发者通过学习绘制圆形、线条、时间更新、旋转、颜色样式设置及动画效果等概念,深化对Canvas的理解和应用。项目中的CSS文件负责时钟的样式设定,而JS文件则包含实现各种特效的逻辑,通过不同的函数或类处理时间更新和动画绘制,提</div> </li> <li><a href="/article/1943979785097113600.htm" title="【前端】jQuery数组合并去重方法总结" target="_blank">【前端】jQuery数组合并去重方法总结</a> <span class="text-muted"></span> <div>在jQuery中合并多个数组并去重,推荐使用原生JavaScript的Set对象(高效简单)或$.unique()(仅适用于DOM元素,不适用于普通数组)。以下是完整解决方案:方法1:使用ES6Set(推荐)//定义多个数组constarr1=[1,2,3];constarr2=[2,3,4];constarr3=[3,4,5];//合并数组并用Set去重constmergedArray=[...</div> </li> <li><a href="/article/1943969321717919744.htm" title="日历插件-FullCalendar的详细使用" target="_blank">日历插件-FullCalendar的详细使用</a> <span class="text-muted">老马聊技术</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a> <div>一、介绍FullCalendar是一个功能强大、高度可定制的JavaScript日历组件,用于在网页中显示和管理日历事件。它支持多种视图(月、周、日等),可以轻松集成各种框架,并提供丰富的事件处理功能。二、实操案例具体代码如下:FullCalendar日期选择body{font-family:Arial,sans-serif;margin:20px;}#calendar{max-width:900</div> </li> <li><a href="/article/1943954447797383168.htm" title="javascript高级程序设计第3版——第12章 DOM2与DOM3" target="_blank">javascript高级程序设计第3版——第12章 DOM2与DOM3</a> <span class="text-muted">weixin_30687587</span> <a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/1.htm">数据结构与算法</a><a class="tag" taget="_blank" href="/search/ViewUI/1.htm">ViewUI</a> <div>12章——DOM2与DOM3为了增强D0M1,DOM级规范定义了一些模块。DOM2核心:为不同的DOM类型引入了一些与XML命名空间有关的方法,还定义了以编程方式创建Document实例的方法;DOM2级样式:针对操作元素的样式而开发;其特性总结:1.每个元素都有一个关联的style对象,可用来确定和修改行内样式;2.要确定某个元素的计算样式,可使用getComgetComputedStyle()</div> </li> <li><a href="/article/1943950163496202240.htm" title="JavaScript 基础09:Web APIs——日期对象、DOM节点" target="_blank">JavaScript 基础09:Web APIs——日期对象、DOM节点</a> <span class="text-muted">梦想当全栈</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>JavaScript基础09:WebAPIs——日期对象、DOM节点进一步学习DOM相关知识,实现可交互的网页特效能够插入、删除和替换元素节点。能够依据元素节点关系查找节点。一、日期对象掌握Date日期对象的使用,动态获取当前计算机的时间。ECMAScript中内置了获取系统时间的对象Date,使用Date时与之前学习的内置对象console和Math不同,它需要借助new关键字才能使用。1.实例</div> </li> <li><a href="/article/1943919026744913920.htm" title="JavaScript之DOM操作与事件处理详解" target="_blank">JavaScript之DOM操作与事件处理详解</a> <span class="text-muted">AA-代码批发V哥</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a> <div>JavaScript之DOM操作与事件处理详解一、DOM基础:理解文档对象模型二、DOM元素的获取与访问2.1基础获取方法2.2集合的区别与注意事项三、DOM元素的创建与修改3.1创建与插入元素3.2修改元素属性与样式3.2.1属性操作3.2.2样式操作3.3元素内容的修改四、DOM元素的删除与替换4.1删除元素4.2替换元素五、事件处理:实现页面交互5.1事件绑定的三种方式5.1.1HTML属性</div> </li> <li><a href="/article/1943917893620133888.htm" title="V少JS基础班之第五弹" target="_blank">V少JS基础班之第五弹</a> <span class="text-muted">V少在逆向</span> <a class="tag" taget="_blank" href="/search/JS%E5%9F%BA%E7%A1%80%E7%8F%AD/1.htm">JS基础班</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a> <div>文章目录一、前言二、本节涉及知识点三、重点内容1-函数的定义2-函数的构成1.函数参数详解1)参数个数不固定2)默认参数3)arguments对象(类数组)4)剩余参数(Rest参数)5)函数参数是按值传递的6)解构参数传递7)参数校验技巧(JavaScript没有类型限制,需要手动校验)2.函数返回值详解3-函数的分类1-函数声明式:2-函数表达式:3-箭头函数:4-构造函数:5-IIFE:6-</div> </li> <li><a href="/article/1943917011268595712.htm" title="Javaweb学习之Vue模板语法(三)" target="_blank">Javaweb学习之Vue模板语法(三)</a> <span class="text-muted">不要数手指啦</span> <a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>目录学习资料前情回顾本期介绍(vue模板语法)文本插值Vue的Attribute绑定使用JavaScript表达式综合实例代码:学习资料Vue.js-渐进式JavaScript框架|Vue.js(vuejs.org)前情回顾项目的创建大家可以看这篇文章Vue学习之项目的创建-CSDN博客本期介绍(vue模板语法)首先,找到我们编写代码的地方找到自己项目的src文件夹,打开之后点击component</div> </li> <li><a href="/article/1943907050870337536.htm" title="深入解析 “void(0);” 的用法与作用_void(0);" target="_blank">深入解析 “void(0);” 的用法与作用_void(0);</a> <span class="text-muted"></span> <div>关键要点void(0);是JavaScript中的一个表达式,研究表明它通常用于超链接中,防止页面跳转。它通过void运算符计算表达式并返回undefined,常用于创建“死链接”。证据显示,这种用法简单易用,但现代开发更推荐使用事件监听器。基本概念void(0);的作用void(0);是JavaScript的void运算符的一个实例,void运算符会计算一个表达式但不返回任何值,而是始终返回un</div> </li> <li><a href="/article/1943848300524400640.htm" title="【JS三兄弟谁是谁】搞懂 splice、slice、split,只需一杯奶茶的时间!" target="_blank">【JS三兄弟谁是谁】搞懂 splice、slice、split,只需一杯奶茶的时间!</a> <span class="text-muted">dorabighead</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%85%AB%E8%82%A1%E6%80%BB%E7%BB%93/1.htm">前端八股总结</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>JavaScript有三兄弟,经常一起“切人”。他们名字相似、功能相关,但性格迥异,常被搞混。今天,就带你喝着奶茶,笑着剖析,帮你彻底搞懂:splice、slice、split到底是谁?干了啥?凭啥这么火?一、三兄弟登场:不同对象,不同任务名称作用对象是否修改原对象返回类型功能简述splice数组✅是被删除元素数组原地删除元素并可插入新元素slice数组/字符串❌否副本(子集)复制选中部分,原体不</div> </li> <li><a href="/article/1943847416503529472.htm" title="前端面试题总结——JS篇" target="_blank">前端面试题总结——JS篇</a> <span class="text-muted">又又呢</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>一、说说JavaScript中的数据类型?存储上有什么差别?1、数据类型基本类型number:数值类型十进制:letintNum=55八进制(零开头):letnum1=070十六进制(0x开头):lethexNum1=0xANaN:特殊数值,意为“不是数值”string:字符串类型boolean:布尔值,true或falseundefined:表示未定义null:空值symbol:是原始值,且符号</div> </li> <li><a href="/article/1943844765225250816.htm" title="前端面试题——5.AjAX的缺点?" target="_blank">前端面试题——5.AjAX的缺点?</a> <span class="text-muted">浅端</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">前端面试题</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">前端面试题</a> <div>①传统的web交互是:用户一个网页动作,就会发送一个http请求到服务器,服务器处理完该请求再返回一个完整的HTML页面,客户端再重新加载,这样极大地浪费了带宽。②AJAX的出现解决了这个问题,它只会向服务器请求用户所需要的数据,并在客户端采用JavaScript处理返回的数据,操作DOM更新页面。③AJXA优点:无刷新更新页面异步服务器通信前端后端负载均衡④AJAX缺点:干掉了Back和Hist</div> </li> <li><a href="/article/1943844765648875520.htm" title="JavaScript知识归纳——面试题" target="_blank">JavaScript知识归纳——面试题</a> <span class="text-muted">Dream_Lee_1997</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/js%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">js面试题</a> <div>JavaScript面试题总结JavaScript知识点1、JavaScript中settimeout与setinteval两个函数的区别?2、编写JavaScript脚本生成1-6之间的整数?3、在JavaScript脚本中,isNaN的作用是什么?4、JavaScript中获取某个元素有哪几种方式?5、Ajax的优缺点都有什么?6、简述一下Ajax的工作原理。7、JavaScript中的数据类</div> </li> <li><a href="/article/1943842998383079424.htm" title="2023高薪前端面试题(二、前端核心——Ajax)" target="_blank">2023高薪前端面试题(二、前端核心——Ajax)</a> <span class="text-muted"></span> <div>原生AjaxAjax简介Ajax全程为AsynchronousJavaScript+XML,就是异步的JS和XML通过AJAX可以在浏览器中向服务器发送异步请求,最大的优势是:无刷新获取数据,实现局部刷新Ajax是一种用于创建快速动态网页的技术AJAX不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式Ajax的应用场景页面上拉加载更多数据列表数据无刷新分页表单项离开焦点数据验证搜索框提示</div> </li> <li><a href="/article/1943832156052713472.htm" title="配置Nginx实现静态资源访问" target="_blank">配置Nginx实现静态资源访问</a> <span class="text-muted">Gappsong874</span> <a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/1.htm">网络安全</a><a class="tag" taget="_blank" href="/search/web%E5%AE%89%E5%85%A8/1.htm">web安全</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8%E6%9E%B6%E6%9E%84/1.htm">安全架构</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4%E5%BC%80%E5%8F%91/1.htm">运维开发</a> <div>Nginx是一款高性能的HTTP和反向代理服务器,常用于处理静态资源请求。通过合理配置,可以显著提升静态资源的访问速度和服务器性能。以下内容将详细介绍如何配置Nginx以实现静态资源的高效访问。基本静态资源配置静态资源通常包括HTML文件、CSS样式表、JavaScript脚本、图片、视频等。Nginx通过简单的配置即可处理这些请求。在Nginx的配置文件中,通常位于/etc/nginx/ngin</div> </li> <li><a href="/article/1943808197554925568.htm" title="微信小程序开发:从漫画阅读到商业变现" target="_blank">微信小程序开发:从漫画阅读到商业变现</a> <span class="text-muted">永远的12</span> <div>本文还有配套的精品资源,点击获取简介:微信小程序作为一种轻量级应用平台,在无需下载安装的情况下提供便捷服务,尤其在漫画阅读领域得到广泛应用。本文介绍了微信小程序的基础开发框架,包括WXML、WXSS和JavaScript的使用,以及漫画小程序的核心功能设计,如漫画分类、搜索、详情展示、阅读模式等。同时,探讨了在小程序中加入广告ID以实现商业变现,包括广告组件的集成和广告政策的遵守。最后,强调了漫画</div> </li> <li><a href="/article/1943803653664075776.htm" title="浏览器视角看 —— 消息队列和事件循环、宏任务和微任务" target="_blank">浏览器视角看 —— 消息队列和事件循环、宏任务和微任务</a> <span class="text-muted">DTcode7</span> <a class="tag" taget="_blank" href="/search/HTML%E7%BD%91%E7%AB%99%E5%BC%80%E5%8F%91/1.htm">HTML网站开发</a><a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8%E4%B8%89%E5%A4%A7%E6%A0%B8%E5%BF%83%E4%B9%8Bhtml/1.htm">前端基础入门三大核心之html</a><a class="tag" taget="_blank" href="/search/HTML/1.htm">HTML</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/web/1.htm">web</a><a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E9%A1%B5%E5%BC%80%E5%8F%91/1.htm">网页开发</a> <div>浏览器视角看——消息队列和事件循环、宏任务和微任务1.JavaScript执行模型简介2.消息队列与事件循环2.1消息队列2.2事件循环示例一:基本事件循环3.宏任务与微任务3.1宏任务与微任务的区别3.2微任务的执行时机示例二:宏任务与微任务的执行顺序4.深入理解事件循环4.1事件循环的生命周期4.2事件循环的阶段5.应用技巧与分析5.1使用微任务优化性能示例三:使用微任务优化DOM操作5.2利</div> </li> <li><a href="/article/1943790915952898048.htm" title="前端每周清单第 16 期:JavaScript 模块化现状;Node V8 与V6 真实性能对比" target="_blank">前端每周清单第 16 期:JavaScript 模块化现状;Node V8 与V6 真实性能对比</a> <span class="text-muted"></span> <div>前端每周清单第16期:JavaScript模块化现状;NodeV8与V6真实性能对比;Nuxt.jsSSR与权限验证指南为InfoQ中文站特供稿件,首发地址为这里;如需转载,请与InfoQ中文站联系。从属于笔者的Web前端入门与工程实践的前端每周清单系列系列;部分文章需要自备梯子。前端每周清单第16期:JavaScript模块化现状;NodeV8与V6真实性能对比;Nuxt.jsSSR与权限验证指</div> </li> <li><a href="/article/1943772645237518336.htm" title="NodeJS VM2沙箱逃逸漏洞分析【CVE-2023-29199】" target="_blank">NodeJS VM2沙箱逃逸漏洞分析【CVE-2023-29199】</a> <span class="text-muted">R3s3arcm</span> <a class="tag" taget="_blank" href="/search/NodeJS%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/1.htm">NodeJS漏洞分析</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8%E5%A8%81%E8%83%81%E5%88%86%E6%9E%90/1.htm">安全威胁分析</a> <div>NodeJSVM2沙箱逃逸漏洞分析【CVE-2023-29199】简介Node.js是一个基于V8引擎的开源、跨平台的JavaScript运行环境,它可以在多个操作系统上运行,包括Windows、macOS和Linux等。Node.js提供了一个运行在服务器端的JavaScript环境,使得开发者可以编写并发的、高效的服务器端应用程序。Node.js使用事件驱动、非阻塞I/O模型来支持并发运行。它</div> </li> <li><a href="/article/1943754997023961088.htm" title="前端开发常见问题" target="_blank">前端开发常见问题</a> <span class="text-muted"></span> <div>技术文章大纲性能优化问题页面加载速度慢的常见原因及解决方案渲染阻塞资源的处理方法图片与媒体文件优化策略懒加载与代码分割的实现方式浏览器兼容性问题不同浏览器对CSS特性的支持差异JavaScriptAPI的兼容性处理方案Polyfill的使用场景与实现方法自动化测试工具在兼容性测试中的应用响应式设计挑战移动端与桌面端布局适配问题媒体查询的最佳实践方案视口单位与相对单位的正确使用高DPI屏幕的图像处理</div> </li> <li><a href="/article/1943730926307569664.htm" title="selenium跳转到新页面时如何进行定位" target="_blank">selenium跳转到新页面时如何进行定位</a> <span class="text-muted"></span> <div>在Selenium中,当你跳转到新页面(例如通过点击链接、提交表单或JavaScript重定向)时,通常会遇到页面加载或窗口切换的问题。为了在新页面上继续进行页面定位操作,你需要确保以下几点:✅1.等待页面加载完成Selenium默认不会自动等待页面加载完成。因此,你需要使用显式等待(ExplicitWait)来确保元素存在后再进行操作。示例代码(Python):fromseleniumimpor</div> </li> <li><a href="/article/1943647350308270080.htm" title="极简Vue 3应用:从入门到掌握核心概念" target="_blank">极简Vue 3应用:从入门到掌握核心概念</a> <span class="text-muted">程序猿全栈の董(董翔)</span> <a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a> <div>Vue.js作为一款渐进式JavaScript框架,以其直观的API和灵活的架构受到广泛欢迎。对于初学者而言,Vue官方CLI生成的项目结构可能略显复杂,包含了各种资源文件、样式和示例组件。本文将介绍如何创建一个最简洁的Vue3应用,帮助你专注于核心概念的学习。为什么需要最简Vue应用?VueCLI默认生成的项目包含:assets目录:存放图片、字体等资源components目录:包含多个示例组件</div> </li> <li><a href="/article/1943642561545564160.htm" title="node.js、npm是什么?服务器脚本语言有哪些?" target="_blank">node.js、npm是什么?服务器脚本语言有哪些?</a> <span class="text-muted"></span> <div>文章目录1.node和nodejs有区别吗:2.Node.js是什么3.NPM4.安装Node.js和npm5.使用appium之前为什么安装node.js?6.Vue.js一定要安装node.js吗?7.开发环境:1.node和nodejs有区别吗:node和nodejs之间没有区别,node全称就是nodejs。nodejs是一个基于ChromeV8引擎的JavaScript运行环境,一个让J</div> </li> <li><a href="/article/1943632470217912320.htm" title="前端高频面试题深度解析(JavaScript + Vue + jQuery)" target="_blank">前端高频面试题深度解析(JavaScript + Vue + jQuery)</a> <span class="text-muted"></span> <div>前端高频面试题深度解析(JavaScript+Vue+jQuery)一、JavaScript核心问题解析事件冒泡与捕获机制对比:graphLRA[捕获阶段]-->|Window→父元素|B[目标元素]B-->|子元素→父元素|C[冒泡阶段]阻止方法://阻止冒泡(常用)event.stopPropagation();//阻止捕获+冒泡+默认行为(慎用)event.stopImmediateProp</div> </li> <li><a href="/article/1943597420784971776.htm" title="TypeScript在大型前端项目中的应用与优势" target="_blank">TypeScript在大型前端项目中的应用与优势</a> <span class="text-muted">天天进步2015</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91/1.htm">前端开发</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/typescript/1.htm">typescript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a> <div>引言随着Web应用规模和复杂度的不断增长,JavaScript作为弱类型语言的局限性日益凸显。TypeScript作为JavaScript的超集,为大型前端项目提供了强大的类型系统和先进的开发工具支持,极大地提升了开发效率和代码质量。本文将深入探讨TypeScript在大型前端项目中的应用与优势。TypeScript简介TypeScript是由Microsoft开发的开源编程语言,它是JavaSc</div> </li> <li><a href="/article/1943594645887971328.htm" title="JavaScript 在前端 UI 框架中的应用与选型" target="_blank">JavaScript 在前端 UI 框架中的应用与选型</a> <span class="text-muted">大厂前端小白菜</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E5%AE%9E%E6%88%98/1.htm">前端开发实战</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a><a class="tag" taget="_blank" href="/search/ai/1.htm">ai</a> <div>JavaScript在前端UI框架中的应用与选型关键词:JavaScript、前端UI框架、应用、选型、React、Vue.js、Angular摘要:本文深入探讨了JavaScript在前端UI框架中的应用与选型问题。首先介绍了前端UI框架的背景知识,包括目的、预期读者、文档结构等。接着阐述了核心概念,分析了JavaScript与前端UI框架的联系。通过具体的算法原理和操作步骤,结合Python代</div> </li> <li><a href="/article/1943590865607913472.htm" title="JavaScript 编程精解(Eloquent)第四版(四)" target="_blank">JavaScript 编程精解(Eloquent)第四版(四)</a> <span class="text-muted">绝不原创的飞龙</span> <a class="tag" taget="_blank" href="/search/%E9%BB%98%E8%AE%A4%E5%88%86%E7%B1%BB/1.htm">默认分类</a><a class="tag" taget="_blank" href="/search/%E9%BB%98%E8%AE%A4%E5%88%86%E7%B1%BB/1.htm">默认分类</a> <div>译者:飞龙协议:CCBY-NC-SA4.0第十九章:HTTP与表单超文本传输协议(HyperTextTransferProtocol),在第十三章中介绍,是在万维网上请求和提供数据的机制。本章更详细地描述了该协议,并解释了浏览器JavaScript如何访问它。协议如果你在浏览器的地址栏中输入[eloquentjavascript.net/18_http.xhtml](http://eloquent</div> </li> <li><a href="/article/1943589856324153344.htm" title="react移动端开发" target="_blank">react移动端开发</a> <span class="text-muted">暖阳浅笑-嘿</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/react/1.htm">react</a><a class="tag" taget="_blank" href="/search/native/1.htm">native</a> <div>React在移动端开发中的应用非常广泛,其独特的组件化开发模式和高效的性能使得它成为许多开发者的首选。一、React移动端开发概述React本身是一个用于构建用户界面的JavaScript库,它并不直接针对移动端或Web端,但由于其灵活性和高效性,被广泛应用于移动端开发中。特别是在结合ReactNative等框架时,React能够充分发挥其优势,为移动端应用提供接近原生的性能和体验。二、React</div> </li> <li><a href="/article/72.htm" title="Java开发中,spring mvc 的线程怎么调用?" target="_blank">Java开发中,spring mvc 的线程怎么调用?</a> <span class="text-muted">小麦麦子</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/mvc/1.htm">mvc</a> <div>今天逛知乎,看到最近很多人都在问spring mvc 的线程http://www.maiziedu.com/course/java/ 的启动问题,觉得挺有意思的,那哥们儿问的也听仔细,下面的回答也很详尽,分享出来,希望遇对遇到类似问题的Java开发程序猿有所帮助。 问题:     在用spring mvc架构的网站上,设一线程在虚拟机启动时运行,线程里有一全局</div> </li> <li><a href="/article/199.htm" title="maven依赖范围" target="_blank">maven依赖范围</a> <span class="text-muted">bitcarter</span> <a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a> <div>1.test 测试的时候才会依赖,编译和打包不依赖,如junit不被打包 2.compile 只有编译和打包时才会依赖 3.provided 编译和测试的时候依赖,打包不依赖,如:tomcat的一些公用jar包 4.runtime 运行时依赖,编译不依赖 5.默认compile 依赖范围compile是支持传递的,test不支持传递 1.传递的意思是项目A,引用</div> </li> <li><a href="/article/326.htm" title="Jaxb org.xml.sax.saxparseexception : premature end of file" target="_blank">Jaxb org.xml.sax.saxparseexception : premature end of file</a> <span class="text-muted">darrenzhu</span> <a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a><a class="tag" taget="_blank" href="/search/premature/1.htm">premature</a><a class="tag" taget="_blank" href="/search/JAXB/1.htm">JAXB</a> <div>如果在使用JAXB把xml文件unmarshal成vo(XSD自动生成的vo)时碰到如下错误: org.xml.sax.saxparseexception : premature end of file 很有可能时你直接读取文件为inputstream,然后将inputstream作为构建unmarshal需要的source参数。InputSource inputSource = new In</div> </li> <li><a href="/article/453.htm" title="CSS Specificity" target="_blank">CSS Specificity</a> <span class="text-muted">周凡杨</span> <a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/%E6%9D%83%E9%87%8D/1.htm">权重</a><a class="tag" taget="_blank" href="/search/Specificity/1.htm">Specificity</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a> <div>  有时候对于页面元素设置了样式,可为什么页面的显示没有匹配上呢? because specificity CSS 的选择符是有权重的,当不同的选择符的样式设置有冲突时,浏览器会采用权重高的选择符设置的样式。     规则:   HTML标签的权重是1 Class 的权重是10 Id 的权重是100 </div> </li> <li><a href="/article/580.htm" title="java与servlet" target="_blank">java与servlet</a> <span class="text-muted">g21121</span> <a class="tag" taget="_blank" href="/search/servlet/1.htm">servlet</a> <div>servlet 搞java web开发的人一定不会陌生,而且大家还会时常用到它。 下面是java官方网站上对servlet的介绍: java官网对于servlet的解释 写道 Java Servlet Technology Overview Servlets are the Java platform technology of choice for extending and enha</div> </li> <li><a href="/article/707.htm" title="eclipse中安装maven插件" target="_blank">eclipse中安装maven插件</a> <span class="text-muted">510888780</span> <a class="tag" taget="_blank" href="/search/eclipse/1.htm">eclipse</a><a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a> <div>1.首先去官网下载 Maven: http://www.apache.org/dyn/closer.cgi/maven/binaries/apache-maven-3.2.3-bin.tar.gz 下载完成之后将其解压, 我将解压后的文件夹:apache-maven-3.2.3, 并将它放在 D:\tools目录下, 即 maven 最终的路径是:D:\tools\apache-mave</div> </li> <li><a href="/article/834.htm" title="jpa@OneToOne关联关系" target="_blank">jpa@OneToOne关联关系</a> <span class="text-muted">布衣凌宇</span> <a class="tag" taget="_blank" href="/search/jpa/1.htm">jpa</a> <div>Nruser里的pruserid关联到Pruser的主键id,实现对一个表的增删改,另一个表的数据随之增删改。 Nruser实体类 //***************************************************************** @Entity @Table(name="nruser") @DynamicInsert @Dynam</div> </li> <li><a href="/article/961.htm" title="我的spring学习笔记11-Spring中关于声明式事务的配置" target="_blank">我的spring学习笔记11-Spring中关于声明式事务的配置</a> <span class="text-muted">aijuans</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E4%BA%8B%E5%8A%A1/1.htm">事务</a><a class="tag" taget="_blank" href="/search/%E9%85%8D%E7%BD%AE/1.htm">配置</a> <div>这两天学到事务管理这一块,结合到之前的terasoluna框架,觉得书本上讲的还是简单阿。我就把我从书本上学到的再结合实际的项目以及网上看到的一些内容,对声明式事务管理做个整理吧。我看得Spring in Action第二版中只提到了用TransactionProxyFactoryBean和<tx:advice/>,定义注释驱动这三种,我承认后两种的内容很好,很强大。但是实际的项目当中</div> </li> <li><a href="/article/1088.htm" title="java 动态代理简单实现" target="_blank">java 动态代理简单实现</a> <span class="text-muted">antlove</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/handler/1.htm">handler</a><a class="tag" taget="_blank" href="/search/proxy/1.htm">proxy</a><a class="tag" taget="_blank" href="/search/dynamic/1.htm">dynamic</a><a class="tag" taget="_blank" href="/search/service/1.htm">service</a> <div>dynamicproxy.service.HelloService package dynamicproxy.service; public interface HelloService { public void sayHello(); }   dynamicproxy.service.impl.HelloServiceImpl package dynamicp</div> </li> <li><a href="/article/1215.htm" title="JDBC连接数据库" target="_blank">JDBC连接数据库</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/JDBC%E7%BC%96%E7%A8%8B/1.htm">JDBC编程</a><a class="tag" taget="_blank" href="/search/JAVA%E6%93%8D%E4%BD%9Coracle%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">JAVA操作oracle数据库</a> <div>         如果我们要想连接oracle公司的数据库,就要首先下载oralce公司的驱动程序,将这个驱动程序的jar包导入到我们工程中;   JDBC链接数据库的代码和固定写法;     1,加载oracle数据库的驱动;     &nb</div> </li> <li><a href="/article/1342.htm" title="单例模式中的多线程分析" target="_blank">单例模式中的多线程分析</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/thread/1.htm">thread</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">多线程</a><a class="tag" taget="_blank" href="/search/java%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">java多线程</a> <div>谈到单例模式,我们立马会想到饿汉式和懒汉式加载,所谓饿汉式就是在创建类时就创建好了实例,懒汉式在获取实例时才去创建实例,即延迟加载。 饿汉式: package com.bijian.study; public class Singleton { private Singleton() { } // 注意这是private 只供内部调用 private static</div> </li> <li><a href="/article/1469.htm" title="javascript读取和修改原型特别需要注意原型的读写不具有对等性" target="_blank">javascript读取和修改原型特别需要注意原型的读写不具有对等性</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/prototype/1.htm">prototype</a> <div>        对于从原型对象继承而来的成员,其读和写具有内在的不对等性。比如有一个对象A,假设它的原型对象是B,B的原型对象是null。如果我们需要读取A对象的name属性值,那么JS会优先在A中查找,如果找到了name属性那么就返回;如果A中没有name属性,那么就到原型B中查找name,如果找到了就返回;如果原型B中也没有</div> </li> <li><a href="/article/1596.htm" title="【持久化框架MyBatis3六】MyBatis3集成第三方DataSource" target="_blank">【持久化框架MyBatis3六】MyBatis3集成第三方DataSource</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/dataSource/1.htm">dataSource</a> <div>MyBatis内置了数据源的支持,如:   <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <data</div> </li> <li><a href="/article/1723.htm" title="我程序中用到的urldecode和base64decode,MD5" target="_blank">我程序中用到的urldecode和base64decode,MD5</a> <span class="text-muted">bitcarter</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/MD5/1.htm">MD5</a><a class="tag" taget="_blank" href="/search/base64decode/1.htm">base64decode</a><a class="tag" taget="_blank" href="/search/urldecode/1.htm">urldecode</a> <div>这里是base64decode和urldecode,Md5在附件中。因为我是在后台所以需要解码: string Base64Decode(const char* Data,int DataByte,int& OutByte) { //解码表 const char DecodeTable[] = { 0, 0, 0, 0, 0, 0</div> </li> <li><a href="/article/1850.htm" title="腾讯资深运维专家周小军:QQ与微信架构的惊天秘密" target="_blank">腾讯资深运维专家周小军:QQ与微信架构的惊天秘密</a> <span class="text-muted">ronin47</span> <div>社交领域一直是互联网创业的大热门,从PC到移动端,从OICQ、MSN到QQ。到了移动互联网时代,社交领域应用开始彻底爆发,直奔黄金期。腾讯在过去几年里,社交平台更是火到爆,QQ和微信坐拥几亿的粉丝,QQ空间和朋友圈各种刷屏,写心得,晒照片,秀视频,那么谁来为企鹅保驾护航呢?支撑QQ和微信海量数据背后的架构又有哪些惊天内幕呢?本期大讲堂的内容来自今年2月份ChinaUnix对腾讯社交网络运营服务中心</div> </li> <li><a href="/article/1977.htm" title="java-69-旋转数组的最小元素。把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素" target="_blank">java-69-旋转数组的最小元素。把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> public class MinOfShiftedArray { /** * Q69 旋转数组的最小元素 * 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。 * 例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。 */ publ</div> </li> <li><a href="/article/2104.htm" title="看博客,应该是有方向的" target="_blank">看博客,应该是有方向的</a> <span class="text-muted">Cb123456</span> <a class="tag" taget="_blank" href="/search/%E5%8F%8D%E7%9C%81/1.htm">反省</a><a class="tag" taget="_blank" href="/search/%E7%9C%8B%E5%8D%9A%E5%AE%A2/1.htm">看博客</a> <div>看博客,应该是有方向的:  我现在就复习以前的,在补补以前不会的,现在还不会的,同时完善完善项目,也看看别人的博客.  我刚突然想到的:  1.应该看计算机组成原理,数据结构,一些算法,还有关于android,java的。  2.对于我,也快大四了,看一些职业规划的,以及一些学习的经验,看看别人的工作总结的.    为什么要写</div> </li> <li><a href="/article/2231.htm" title="[开源与商业]做开源项目的人生活上一定要朴素,尽量减少对官方和商业体系的依赖" target="_blank">[开源与商业]做开源项目的人生活上一定要朴素,尽量减少对官方和商业体系的依赖</a> <span class="text-muted">comsci</span> <a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/1.htm">开源项目</a> <div>      为什么这样说呢?  因为科学和技术的发展有时候需要一个平缓和长期的积累过程,但是行政和商业体系本身充满各种不稳定性和不确定性,如果你希望长期从事某个科研项目,但是却又必须依赖于某种行政和商业体系,那其中的过程必定充满各种风险。。。       所以,为避免这种不确定性风险,我</div> </li> <li><a href="/article/2358.htm" title="一个 sql优化 ([精华] 一个查询优化的分析调整全过程!很值得一看 )" target="_blank">一个 sql优化 ([精华] 一个查询优化的分析调整全过程!很值得一看 )</a> <span class="text-muted">cwqcwqmax9</span> <a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a> <div>见   http://www.itpub.net/forum.php?mod=viewthread&tid=239011 Web翻页优化实例 提交时间: 2004-6-18 15:37:49      回复    发消息  环境: Linux ve</div> </li> <li><a href="/article/2485.htm" title="Hibernat and Ibatis" target="_blank">Hibernat and Ibatis</a> <span class="text-muted">dashuaifu</span> <a class="tag" taget="_blank" href="/search/Hibernate/1.htm">Hibernate</a><a class="tag" taget="_blank" href="/search/ibatis/1.htm">ibatis</a> <div>Hibernate  VS  iBATIS 简介 Hibernate 是当前最流行的O/R mapping框架,当前版本是3.05。它出身于sf.net,现在已经成为Jboss的一部分了 iBATIS 是另外一种优秀的O/R mapping框架,当前版本是2.0。目前属于apache的一个子项目了。 相对Hibernate“O/R”而言,iBATIS 是一种“Sql Mappi</div> </li> <li><a href="/article/2612.htm" title="备份MYSQL脚本" target="_blank">备份MYSQL脚本</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a> <div>#!/bin/sh # this shell to backup mysql #1413161683@qq.com (QQ:1413161683 DuChengJiu) _dbDir=/var/lib/mysql/ _today=`date +%w` _bakDir=/usr/backup/$_today [ ! -d $_bakDir ] && mkdir -p </div> </li> <li><a href="/article/2739.htm" title="iOS第三方开源库的吐槽和备忘" target="_blank">iOS第三方开源库的吐槽和备忘</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/ios/1.htm">ios</a> <div>转自 ibireme的博客   做iOS开发总会接触到一些第三方库,这里整理一下,做一些吐槽。   目前比较活跃的社区仍旧是Github,除此以外也有一些不错的库散落在Google Code、SourceForge等地方。由于Github社区太过主流,这里主要介绍一下Github里面流行的iOS库。   首先整理了一份 Github上排名靠</div> </li> <li><a href="/article/2866.htm" title="html wlwmanifest.xml" target="_blank">html wlwmanifest.xml</a> <span class="text-muted">eoems</span> <a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a> <div>所谓优化wp_head()就是把从wp_head中移除不需要元素,同时也可以加快速度。 步骤: 加入到function.php remove_action('wp_head', 'wp_generator'); //wp-generator移除wordpress的版本号,本身blog的版本号没什么意义,但是如果让恶意玩家看到,可能会用官网公布的漏洞攻击blog remov</div> </li> <li><a href="/article/2993.htm" title="浅谈Java定时器发展" target="_blank">浅谈Java定时器发展</a> <span class="text-muted">hacksin</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%B9%B6%E5%8F%91/1.htm">并发</a><a class="tag" taget="_blank" href="/search/timer/1.htm">timer</a><a class="tag" taget="_blank" href="/search/%E5%AE%9A%E6%97%B6%E5%99%A8/1.htm">定时器</a> <div>java在jdk1.3中推出了定时器类Timer,而后在jdk1.5后由Dou Lea从新开发出了支持多线程的ScheduleThreadPoolExecutor,从后者的表现来看,可以考虑完全替代Timer了。 Timer与ScheduleThreadPoolExecutor对比: 1.    Timer始于jdk1.3,其原理是利用一个TimerTask数组当作队列</div> </li> <li><a href="/article/3120.htm" title="移动端页面侧边导航滑入效果" target="_blank">移动端页面侧边导航滑入效果</a> <span class="text-muted">ini</span> <a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/javascirpt/1.htm">javascirpt</a> <div>效果体验:http://hovertree.com/texiao/mobile/2.htm可以使用移动设备浏览器查看效果。效果使用到jquery-2.1.4.min.js,该版本的jQuery库是用于支持HTML5的浏览器上,不再兼容IE8以前的浏览器,现在移动端浏览器一般都支持HTML5,所以使用该jQuery没问题。HTML文件代码: <!DOCTYPE html> <h</div> </li> <li><a href="/article/3247.htm" title="AspectJ+Javasist记录日志" target="_blank">AspectJ+Javasist记录日志</a> <span class="text-muted">kane_xie</span> <a class="tag" taget="_blank" href="/search/aspectj/1.htm">aspectj</a><a class="tag" taget="_blank" href="/search/javasist/1.htm">javasist</a> <div>在项目中碰到这样一个需求,对一个服务类的每一个方法,在方法开始和结束的时候分别记录一条日志,内容包括方法名,参数名+参数值以及方法执行的时间。   @Override public String get(String key) { // long start = System.currentTimeMillis(); // System.out.println("Be</div> </li> <li><a href="/article/3374.htm" title="redis学习笔记" target="_blank">redis学习笔记</a> <span class="text-muted">MJC410621</span> <a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/NoSQL/1.htm">NoSQL</a> <div>1)nosql数据库主要由以下特点:非关系型的、分布式的、开源的、水平可扩展的。 1,处理超大量的数据 2,运行在便宜的PC服务器集群上, 3,击碎了性能瓶颈。 1)对数据高并发读写。 2)对海量数据的高效率存储和访问。 3)对数据的高扩展性和高可用性。 redis支持的类型: Sring 类型 set name lijie get name lijie set na</div> </li> <li><a href="/article/3501.htm" title="使用redis实现分布式锁" target="_blank">使用redis实现分布式锁</a> <span class="text-muted">qifeifei</span> <div>在多节点的系统中,如何实现分布式锁机制,其中用redis来实现是很好的方法之一,我们先来看一下jedis包中,有个类名BinaryJedis,它有个方法如下:   public Long setnx(final byte[] key, final byte[] value) { checkIsInMulti(); client.setnx(key, value); ret</div> </li> <li><a href="/article/3628.htm" title="BI并非万能,中层业务管理报表要另辟蹊径" target="_blank">BI并非万能,中层业务管理报表要另辟蹊径</a> <span class="text-muted">张老师的菜</span> <a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/BI/1.htm">BI</a><a class="tag" taget="_blank" href="/search/%E5%95%86%E4%B8%9A%E6%99%BA%E8%83%BD/1.htm">商业智能</a><a class="tag" taget="_blank" href="/search/%E4%BF%A1%E6%81%AF%E5%8C%96/1.htm">信息化</a> <div>       BI是商业智能的缩写,是可以帮助企业做出明智的业务经营决策的工具,其数据来源于各个业务系统,如ERP、CRM、SCM、进销存、HER、OA等。        BI系统不同于传统的管理信息系统,他号称是一个整体应用的解决方案,是融入管理思想的强大系统:有着系统整体的设计思想,支持对所有</div> </li> <li><a href="/article/3755.htm" title="安装rvm后出现rvm not a function 或者ruby -v后提示没安装ruby的问题" target="_blank">安装rvm后出现rvm not a function 或者ruby -v后提示没安装ruby的问题</a> <span class="text-muted">wudixiaotie</span> <a class="tag" taget="_blank" href="/search/function/1.htm">function</a> <div>1.在~/.bashrc最后加入 [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"  2.重新启动terminal输入:   rvm use ruby-2.2.1 --default  把当前安装的ruby版本设为默</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>