资讯专栏INFORMATION COLUMN

红宝书笔记

Tonny / 1606人阅读

摘要:红宝书笔记在中使用加载外部脚本文件,通知浏览器立即下载,异步执行脚本可以延迟到文档完全被解析和显示之后在执行浏览器不支持脚本。对数组中的每一项运行给定函数,返回该函数会返回的项组成的数组。

红宝书笔记 1.在HTML中使用JavaScript

async:加载外部脚本文件,通知浏览器立即下载,异步执行

defer:脚本可以延迟到文档完全被解析和显示之后在执行

noscript:
浏览器不支持脚本。
浏览器支持脚本,但是脚本被禁用

2.变量、作用域和内存问题

复制变量值

复制基本类型值,这两个变量相互独立,互不影响。

复制引用类型(对象),值引用是一个指针,改变其中一个对象,会影响另一个对象。

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

在函数重写obj时,这个变量引用就是一个局部对象。而这个对象会在函数执行完毕后立即被销毁。

检测类型

使用typeof检测基本数据类型,但是在检测引用类型的值是,这个操作符的用处不大,因为使用typeof无法知道它是什么类型的对象。为此,ECMAScript提供了 instanceof操作符。

var s = "test";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object()
console.log(typeof s); // string
console.log(typeof b); // boolean
console.log(typeof i); // number
console.log(typeof u); // undefined
console.log(typeof n); // object
console.log(typeof o); // object

延长作用域链

try-catch语句中的catch块
with语句

垃圾回收

标记清除

垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记

引用计数

当它们被赋值为指向某个对象时,要给它们原本持有的引用所指向的对象的引用计数减1,然后给它们新指向的对象的引用计数加1。当它们的生命期结束的时候,要给它们所指向的对象的引用计数减1,当引用计数为0时,则可以将其占用的内存空间进行回收

3.引用类型 Array类型

检测数组

value instanceof Array
Array.isArray(value)

栈方法(后进先出)

push:将参数逐个添加到数据末尾

pop:从数组末尾移除最后一项

队列方法(先进先出)

shift:删除数组的第一项并返回该项

unshift:向数组前添加任意个项并返回新数组的长度

重排序方法

reverse:反转数组

sort:按升序排列数组

操作方法

concat:将参数添加到数组的末尾,并返回新的数组

slice:基于当前数组中的一或多个项创建一个新数组,slice()方法可以接受一或者两个参数,即要返回项的起始和结束为止

var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1)
var colors3 = colors.slice(4)
console.log(colors2); // ["green", "blue", "yellow", "purple"]
console.log(colors3); // ["purple"]

splice:返回新的数组,能够删除、插入和替换多个项

var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,返回的数组中只包含一项
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置1 开始插入两项
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的是一个空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,返回的数组中只包含一项

位置方法

indexOf:从数组开头开始向后查找位置

lastIndexOf:从数组末尾开始向前查找

迭代方法

every(): 对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。

filter(): 对数组中的每一项运行给定函数,返回该函数会返回true 的项组成的数组。

forEach(): 对数组中的每一项运行给定函数。这个方法没有返回值。

map(): 对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。

some(): 对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。

缩小方法

reduce():遍历数组所有项,并返回最终的值,从第一项开始,逐个遍历到最后

reduceRight():从最后一项开始,逐个遍历到第一项

var values = [1, 2, 3, 4, 5];
var sum = values.reduce((prev, cur, index, array) => {
    return prev + cur;
});
console.log(sum); // 15
Function类型

函数内部属性

函数内部有两个特殊的对象:argumens和this

属性和方法

每个函数都包含两个属性

length: 表示函数希望接收的命名参数的个数

prototype: 保存实例方法

每个函数都包含两个非继承而来的方法

call()和apply()这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值

apply()接收两个参数:一个是在其中运行的函数作用域,另一个是参数数组

call()接收的参数,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数

bind()方法会创建一个实例,其this值会被绑定到传给bind()函数的值

function sum (num1, num2) {
    return num1 + num2;
}
function callSum1 (num1, num2) {
    return sum.apply(this, [num1, num2]);
}
function callSum2 (num1, num2) {
    return sum.call(this, num1, num2);
}
callSum1(10, 10); // 20
callSum2(10, 10); // 20
var callSum3 = sum.bind(null)
callSum3(10, 10) // 20
单体内置对象

Global对象

encodeURI()和encodeURIComponet()方法可以对URI进行编码,以便发送给浏览器

encodeURI()编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了%20,对应decodeURI()方法

encodeURIComponet()方法则会使用对应的编码替换所有非字母数字字符,这也正式可以对整个URL使用encodeURI(),而只能对附加在现有URL后面的字符串使用encodeURIComponet()的原因所在。对应decodeURIComponet()方法

4.面向对象的程序设计 理解对象

属性类型

ECMAScript中有两种属性:数据属性和访问器属性

数据属性

[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为true

[[Enumerable]]:表示能否通过for-in循环返回属性。默认值为true

[[Writable]]:表示能否修改属性的值。默认值为true

[[Value]]:包含这个属性的数据值,默认值为undefined

要修改属性默认的特性,必须使用Object.defineProperty()方法。这个方法接收三个参数:属性所在对象、属性的名字和一个描述符对象。其中,描述符对象的属性必须是:configurabel、enumerable、writable、和value。设置其中的一个或多个值。
var person = {}
Object.defineProperty(person, "name", {
    writable: false,
    configurable: false,
    value: "Nicholas"
});
console.log(person.name); // Nicholas
person.name = "Greg";
console.log(person.name); // Nicholas
delete person.name
console.log(person.name); // Nicholas

Object.defineProperty()方法会对configurable为false的属性修改做限制

访问器属性

[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。默认值为true

[[Enumerable]]:表示能否通过for-in循环返回属性。默认值为true

[[Get]]:在读取属性时调用的函数。默认值为undefined

[[Set]]:在写入属性时调用的函数。默认值为undefined

访问器属性不能直接定义,必须使用Object.defineProperty()来定义

var book = {
    _year: 2004,
    edition: 1
};
Object.defineProperty(book, "year", {
    get: function() {
        return this._year
    },
    set: function(newValue) {
        if (newValue > 2004) {
            this._year = newValue;
            this.edition += newValue - 2004
        }
    }
});
book.year = 2005;
console.log(book.edition); // 2

定义多个属性

ECMAScript5定义了一个Object.defineProperties()方法。利用这个方法可以通过描述符一次定义多个属性。这个方法接收两个对象参数。
var book = {};
Object.defineProperties(book, {
    _year: {
        value: 2004
    },
    edition: {
        value: 1
    },
    year: {
        get: function() {
            return this._year
        },
        set: function(newValue) {
            if (newValue > 2004) {
                this._year = newValue;
                this.edition += newValue - 2004
            }
        }
    }
});

读取属性的特性

使用Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符
var descriptor = Object.getOwnPropertyDescriptor(book, "_year")
console.log(descriptor.value); // 2004
console.log(descriptor.configurable); // false
创建对象

工厂模式

function createPerson (name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        console.log(this.name);
    };
    return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

构造函数模式

function Person (name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        console.log(this.name);
    };
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

原型模式

理解原型对象

function Person () {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
    console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();

在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向一个prototype属性所在函数的指针。例如,Person.prototype.constructor指向Person

我们可以通过isPrototypeof()方法来确定对象之间是否存在原型关系。从本质上讲,如果[[Prototype]]指向调用isPrototypeof()方法的对象(Person.prototye),那么这个方法就返回true。

console.log(Person.prototype.isPrototypeOf(person1)); // true
console.log(Person.prototype.isPrototypeOf(person2)); // true

ECMAScript5增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值。例如:

console.log(Object.getPrototypeOf(person1) === Person.prototype); // true

虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。

function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
    alert(this.name);
};
var person1 = new Person();
person1.name = "Greg";
console.log(person1.name); //"Greg" — 来自实例
delete person1.name;
console.log(person1.name); //"Nicholas" — 来自原型

通过delete操作符删除实例的属性,就恢复了对原型中name属性的连接。因此接下来再调用person1.name是,就返回了原型中name属性的值了。
Object.hasOwnProperty()方法可以检测一个属性是否存在于实例中,还是存在于原型中。

原型与in操作符

in操作符只要通过对象能够访问到属性就返回true,Object.hasOwnProperty()只在属性存在于实例中才返回true,因此只要in操作符返回true而Object.hasOwnProperty()返回false,就可以确定属性是原型中的属性。

function hasPrototypeProperty (object, name) {
    if (name in object) {
         return object.hasOwnProperty(name) // true:属性在实例中,false:属性在对象中
    } else {
        console.log("没有该属性");
    }
}

要取得对象上所有可枚举的实例属性,可以使用ECMAScript5的Object.keys()方法

function Person () {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
    console.log(this.name);
};
Object.keys(Person.prototype); //  ["name", "age", "job", "sayName"]
var person1 = new Person();
person1.name = "Rob";
person1.age = 31;
Object.keys(person1); // ["name", "age"]

Object.getOwnPropertyNames()可以得到所有的属性,无论它是否可枚举

Object.getOwnPropertyNames(Person.prototype); //  ["constructor", "name", "age", "job", "sayName"]

更简单的原型方法

function Person () {}
Person.prototype = {
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function () {
        console.log(this.name);
    }
}

我们将Person.prototype设置为一个新的对象,本质上是完全重写了默认的prototype对象。但是这样有一个例外,constructor属性不再指向Person了,而是指向Object。所以我们需要将他的constructor属性设置成Person

function Person () {}
Person.prototype = {
    constructor: Person,
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function () {
        console.log(this.name);
    }
}

但是这种方式重设constructor属性会导致它的[[Enumerable]]的特性被设置为true,默认情况下,原生的constructor属性是不可枚举的。

function Person () {}
Person.prototype = {
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function () {
        console.log(this.name);
    }
}
Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
});

原型的动态性

重写整个原型对象会切断构造函数与最初原型之间的联系。记住:实例中的指针仅指向原型,而不指向构造函数

function Person () {}
var friend = new Person();
Person.prototype = {
    constructor: Person,
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function () {
        console.log(this.name);
    }
}
friend.sayName(); // error

原生对象的原型

原型模式的重要性不仅体现在创建自定义类型方面,就连所有的原生的引用类型,都是采用这种模式创建的。所有原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法。

原型对象的问题
原型模式的所有实例在默认情况下都将取得相同的属性值,最大的问题是其共享的本性所导致的。

function Person () {}
Person.prototype = {
    constructor: Person,
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    friends: ["Shelby", "Court"],
    sayName: function () {
        console.log(this.name);
    }
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
console.log(person1.friends); // ["Shelby", "Court", "Van"]
console.log(person2.friends); //  ["Shelby", "Court", "Van"]

组合使用构造函数和原型模式

创建自定义类型的最常见的方式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

每个实例都会有自己的一份实例属性的副本,但同事又共享着对方法的引用,最大的节省了内存

function Person (name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}
Person.prototype = {
    constructor: Person,
    sayName: function () {
        console.log(this.name);
    };
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
console.log(person1.friends); // ["Shelby", "Court", "Van"]
console.log(person2.friends); //  ["Shelby", "Court"]

动态原型模式
通过在构造函数中初始化原型(仅在必要的情况下),又保持同时使用构造函数和原型模式的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

function Person (name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
    if (typeof this.sayName === "function") {
        Person.prototype.sayName = function () {
            console.log(this.name);
        };
    }
}
继承

原型链

构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
function SuperType () {
    this.property = true;
}
SuperType.prototype.getSuperValue = function () {
    return this.property
};
function SubType () {
    this.subproperty = false
}
// 继承了SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function () {
    return this.subproperty;
};
var instance = new SubType();
console.log(instance.getSuperValue()); // true

别忘记默认原型

所用引用类型默认都继承Object,而这个继承也是通过原型链实现的。所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。

确定原型与实例的关系

instanceof操作符

console.log(instance instanceof Object); // true
console.log(instance instanceof SuperType); // true
console.log(instance instanceof SubType); // true

isPrototypeOf()方法,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型

console.log(Object.isPrototypeOf(instance)); // true
console.log(SuperType.isPrototypeOf(instance)); // true
console.log(SubType.isPrototypeOf(instance)); // true

谨慎的定义方法

子类型有时需要重写超类型中的某个方法,或者需要添加超类型中不存在的某个方法。但不管怎样,给原型添加方法的代码一定要放在替换原型语句之后。

原型链的问题

通过原型来实现继承时,原型实际上会变成另一个类型的实例,包含引用类型值的原型属性会被所有实例共享。

在创建子类型的实例时,不能向超类型的构造函数中传递参数。

借用构造函数

在解决原型中包含引用类型值所带来问题的过程中,开始使用借用构造函数的技术。即在子类型构造函数的内部调用超类型构造函数

function SuperType () {
    this.colors = ["red", "blue", "green"];
}
function SubType () {
    // 继承了SuperType
    SuperType.call(this);
    // SuperType.apply(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // ["red", "blue", "green", "black"]
var instance2 = new SubType();
console.log(instance2.colors); // ["red", "blue", "green"]

传递参数

相对于原型链而言,借用构造函数有一个很大的优势,既可以在子类型构造函数中向超类型构造函数传递参数

function SuperType (name) {
    this.name = name;
}
function SubType () {
    // 继承了SuperType
    SuperType.call(this, "Nicholas");
    this.age = 29
}
var instance = new SubType();
console.log(instance.name); // "Nicholas"
console.log(instance.age); // 29

借用构造函数的问题

方法都在构造函数中定义,因为函数复用就无从谈起

组合继承

既能通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性

function SuperType (name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
};
function SubType (name, age) {
    // 继承了SuperType
    SuperType.call(this, name);
    this.age = age
}
// 继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function () {
    console.log(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); // ["red", "blue", "green", "black"]
instance1.sayName(); // "Nicholas"
instance1.sayAge(); // 29
var instance2 = new SubType("Greg", 27);
console.log(instance2.colors); // ["red", "blue", "green"]
instance2.sayName(); // "Greg"
instance2.sayAge(); // 27
5.函数表达式
定义函数的方式有两种:一种是函数声明,另一种是函数表达式。
函数声明的特征是函数声明提升,意思是在执行代码之前会先读取函数声明。
闭包

函数作用域链

当某个函数第一次被调用时,会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的内部属性(即[[Scope]])。然后,使用this.arguments和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位,......直至作为作用域链终点的全局执行环境。

闭包与变量

// i 最终为10
function createFunctions () {
    var result = new Array();
    for (var i = 0; i < 10; i++) {
        result[i] = function () {
            return i
        }
    }
    return result;
}
// i 为 0,1,2...9
function createFunctions () {
    var result = new Array();
    for (var i = 0; i < 10; i++) {
        result[i] = function (num) {
            return function (arguments) {
                return num;
            };
        }(i)
    }
    return result;
}

关于this对象

this对象是在运行中基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过匿名函数的执行环境具有全局性,因此其this对象通常指向window。当然,再通过call()和apply()改变执行函数执行环境的情况下,this就会指向其他对象

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        return function () {
            return this.name
        }
    }
}
console.log(object.getNameFunc()()); // "The Window"

模仿块级作用域

匿名函数可以用来模仿块级作用域并避免这个问题。用块级作用域(通常称为私有作用域)的匿名函数的语法如下所示。

(function(){

})()

私有变量

function Person(name) {
    this.getName = function() {
        retirm name;
    }
    this.setName = function(value) {
        name = value
    }
}
var person = new Person("Nicholas");
console.log(person.getName()); // "Nicholas"
person.setName("Greg");
console.log(person.getName()); // "Greg"

以上代码的构造函数中定义了两个特权方法:getName()和setName()。这两个方法都可以在构造函数外部使用,而且都有权访问私有变量name。但在Person构造函数外部,没有任何方法访问name。由于这两个方法是在构造函数内部定义的,它们作为闭包能够通过作用域链访问name。
静态私有变量

6.BOM window对象

全局作用域

抛开全局变量会成为window对象的属性不谈,定义全局变量与在window对象上直接定义属相还是有一点差别:全局变量不能通过delete属性操作符删除,而直接在window对象上的定义的属性可以。

var age = 29;
window.color = "red";
delete window.age; // 不会报错
delete window.color // 不会报错 返回true
var newValue = oldValue; // 会抛出错误,因为oldValue未定义
var newValue = window.oldValue; // 不会报错,因为这是一次属性查询

窗口关系及框架

了解frameset和frame
窗口位置

下列代码可以跨浏览器取得窗口左边和上边的位置
Opera支持screenX,screenY。其他浏览器支持screenLeft,screenTop

var leftPops = (typeof window.screenLeft === "number") ? window.screenLeft : window.screenX;
var topPops = (typeof window.screenTop === "number") ? window.screenLeft : window.screenY;
localtion对象

locatoin对象的属性

replace方法替换当前浏览窗口,reload方法刷新浏览器窗口

navigator对象
识别浏览器的信息

7.DOM
DOM是针对HTML和XML文档的一个API。DOM描绘了一个层次的节点树。
节点层次

NODE类型

每个节点都有一个nodeType属性,用于表明节点的类型。

Node.ELEMENT_NODE(1)

Node.ATTRIBUTE_NODE(2)

Node.TEXT_NODE(3)

Node.CDATA_SECTION_NODE(4)

Node.ENTITY_REFERENCE_NODE(5)

Node.ENTITY_NODE(6)

Node.PROCESSING_INSTRUCTION_NODE(7)

Node.COMMENT_NODE(8)

Node.DOCUMENT_NODE(9)

Node.DOCUMENT_TYPE_NODE(10)

Node.DOCUMENT_FRAGMENT_NODE(11)

Node.NOTATION_NODE(12)

if (someNode.nodeType == 1) {
    console.log("Node is an element");
}

nodeName和nodeValue属性

nodeName返回节点的标签名,如p,div,span等
nodeValue的值始终是null

节点关系

操作节点

appendChild():向childNodes列表的末尾添加一个节点

insertBefore():向childNodes列表某个特定位置添加一个节点。该方法接收两个参数:要插入的节点和作为参照的节点,并返回要插入的节点,如果参照节点是null,则和appendChild()执行相同的操作

replaceChild():替换节点。接收要插入的节点和要替换的节点两个参数。要替换的节点将被返回并从文档书中被移除。

removeChild():移除节点。接收一个参数,就是需要移除的节点

cloneNode():创建调用这个方法节点的一个完全相同的副本。接受一个布尔值参数,表示是否执行深复制

normalize()

createElement():创建元素

createTextNode():创建文本节点

createComment():创建注释节点

8.DOM拓展 选择符API

getElementById()方法:通过id获取

getElementsByName()方法:通过name属性,一般使用它获取表单元素,少用

getElementsByTagName()方法:通过标签名获取元素

getElementsByClassName()方法:通过类名获取元素

querySelector()方法:接收一个CSS选择符,返回与该模式匹配的第一个元素,没有则返回null

querySelectorAll()方法:接收一个CSS选择符,返回一个NodeList实例

macthsSelector()方法:接收一个CSS选择符,如果调用元素与该选择符匹配,返回true否则返回false

9.事件

事件流

事件冒泡
IE的事件流叫做事件冒泡,即事件开始由最具体的元素接收,然后逐级向上传播到较为不具体的节点
事件捕获
Netscape的事件流叫事件捕获,即不太具体的节点应该更早接收事件,而最具体的节点应该最后接收事件
DOM事件流
包括三个阶段:事件捕获阶段。处于目标阶段和事件冒泡阶段

事件处理程序

DOM2级时间处理程序
addEventListener
removeEventListener
定义了两个方法用于处理指定和删除事件处理程序的操作。所有的DOM节点中都包含这两个方法,接受三个参数:事件名、事件处理程序和布尔值。最后这个布尔值如果是true,表示在捕获阶段调用事件处理程序;false表示在冒泡阶段调用事件处理程序,默认是false。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除。如果通过addEventListener()添加的匿名函数将无法移除。传入的函数要相同,否则无法移除
attachEvent
detachEvent
这两个方法接受两个参数:事件名(带on)和事件处理函数。
var btn = document.getElementById("myBtn");
var handler = function(){ console.log("clicked") };
btn.attachEvent("onclick", handler);
btn.detachEvent("onclick", handler); // 有效

事件对象

DOM的事件对象

事件类型

UI事件
load:当页面完全加载后在window上面触发,img图片加载完
unload:当页面完全卸载
abort:当用户停止下载过程
error:当发生JavaScript错误时在window触发
select:当用户选择文本框中的一个或者多个触发
resize:当窗口大小变化是触发
scroll:用户滚动时触发

内存和性能
事件委托利用了时间冒泡,指定一个事件处理程序,就可以管理某一个类型的所有事件

HTML5脚本编程

跨文档消息传递
核心方法是postMessage()方法,接受两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。
// 注意:所有支持XDM的浏览器也支持iframe的contentWindow属性

var iframeWindow = document.getElementById("myframe").contentWindow;
iframeWindow.postMessage("A secret", "http://www.wrox.com");

高级技巧

高级函数
安全的类型检测

function isArray (value) {
    return Object.prototype.toString.call(value) === "[object Array]";
}
function isFunction (value) {
    return Object.prototype.toString.call(value) === "[object Function]";
}
function isRegExp (value) {
    return Object.prototype.toString.call(value) === "[object RegExp]";
}

作用域安全的构造函数
防止this指向window对象

function Person (name, age, job) {
    if (this instanceof Person) {
        this.name = name;
        this.age = age;
        this.job = job;
    } else {
        return new Person(name, age, jon);
    }
}

惰性载入函数
function createXHR(){
    if (typeof XMLHttpRequest != "undefined"){
        return new XMLHttpRequest();
    } else if (typeof ActiveXObject != "undefined"){
        if (typeof arguments.callee.activeXString != "string"){
            var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                i,len;
            for (i=0,len=versions.length; i < len; i++){
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (ex){
                    //跳过
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error("No XHR object available.");
    }
}
第一种实现方法:
function createXHR () {
    if (typeof XMLHttpRequest != "undefined") {
        createXHR = function () {
            return new XMLHttpRequest();
        };
    } else if (typeof ActiveXObjext != "undefined") {
        createXHR = function () {
            if (typeof arguments.callee.activeXString != "string") {
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                    i,len;
                for (i = 0, len = versions.length; i < len; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch (e) {
                        // skip
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        };
    } else {
        createXHR = function () {
            throw new Error("No XHR object available.");
        }
    }
    return createXHR();
}

第二种改法:
var createXHR = (function () {
    if (typeof XMLHttpRequest != "undefined") {
        return function () {
            return new XMLHttpRequest();
        };
    } else if (typeof ActiveXObjext != "undefined") {
        return function () {
            if (typeof arguments.callee.activeXString != "string") {
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                    i,len;
                for (i = 0, len = versions.length; i < len; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch (e) {
                        // skip
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        };
    } else {
        return function () {
            throw new Error("No XHR object available.");
        }
    }
})();

函数绑定

bind函数:
function bind (fn, context) {
    return function () {
        fn.call(context, arguments)
    }
}
函数柯里化
function curry (fn) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function () {
        var innerArgs = Array.prototype.slice.call(arguments)
        var finalArgs = args.concat(innerArgs)
        return fn.apply(null, finalArgs);
    }
}
function bind (fn, context) {
    var args = Array.prototype.slice.call(arguments, 2);
    return function () {
        var innerArgs = Array.prototype.slice.call(arguments)
        var finalArgs = args.concat(innerArgs)
        return fn.apply(context, finalArgs);
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106478.html

相关文章

  • JavaScript宝书笔记(三)---函数特性

    摘要:接下来的文章更新以红宝书章节更新,内容过多分若干篇文章来写,标题会清晰表明是哪一章节,大家一起学习进步,谢谢大家的支持 1.函数参数的特性: function doAdd(num1, num2) { arguments[1] = 10; alert(arguments[0] + num2); } arguments对象中的值会自动反映到对应的命名参数,所以修改 argu...

    沈建明 评论0 收藏0
  • JavaScript宝书笔记(七)---Function类型

    摘要:函数实际上是对象。所以需要消除这种紧耦合。函数内部属性引用的是函数据以执行的环境对象或者也可以说是值函数的名字仅仅是一个包含指针的变量而已。因此,即使是在不同的环境中执行,全局的函数与函数指向的仍然是同一个函数。 1.函数实际上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法 2.由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变...

    cyrils 评论0 收藏0
  • 宝书笔记-第3章-基本概念

    showImg(https://segmentfault.com/img/bVbwbVl?w=1649&h=2769);

    _ivan 评论0 收藏0
  • JavaScript宝书笔记(四)---变量、作用域和内存问题

    摘要:在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。标记清除是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存 1.在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。 2.当从一个变量向另一个变量复制引用类型的值时,两个变量实际上将引用同一个对象,因此,改变其中一个变量,就会...

    imtianx 评论0 收藏0
  • JavaScript宝书笔记(二)---操作符及语句

    摘要:与相乘,结果为与非数值相乘,结果为或者除以,结果为除以,结果为如果是非零的有限数被零除,则结果是或总的来说,得出的结果为不可预测的基本都为任何操作数与进行关系比较,结果都是。实际开发不建议使用语句,会造成调试的困难增加 1.负数二进制存储使用的格式是二进制补码,计算一个数值的二进制补码步骤如下: (1)取该值的绝对值的二进制(2)取反码(3)反码+1,从而得出补码 2.位操作: (...

    AlphaWatch 评论0 收藏0
  • 宝书笔记-第5章-引用类型

    showImg(https://segmentfault.com/img/bVbwihz?w=1558&h=6145);

    ZoomQuiet 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<