《javascript高级程序设计》 复习笔记

第一章 JavaScript简介

文档对象模型(DOM) ,提供访问和操作网页内容的方法和接口
浏览器对象模型(BOM), 提供与浏览器交互的方法和窗口,windows对象
:弹出浏览器,缩放浏览器,提供分辨率,提供cookies支持

第二章 JavaScript运用在html中

XHTML中tag可以不用封口,但是html很严格需要封口。

假如把script 的tag放在head里面,浏览器要加载好js才能呈现页面,就很慢,所以我们一般放在body前面。

script里面延迟属性, 加一个属性defer = “defer"。告诉他脚本执行时不会影响页面的构造。

第三章 基本概念

变量名 test和Test是两个不同变量名
JavaScript严格模式"use strict"
良好的代码风格。加上分号和代码块

定义一个var变量,他在局部变量使用后马上销毁

function test(){
	var message = "hi";
}
test();
alert(message);

但是如果不加var,就会变成全局变量。

function test(){
	message = "hi";
}
test();
alert(message);

数据类型

JavaScript五种简单的类型, Undefined, Null, Boolean, Number 和 String.
复杂数据类型 Object。
使用typeof鉴别类型,这是一个操作符不是函数
typeof null 返回 object,特殊值null被认为是一个空对象的引用。

undefined

未初始化变量就是undefined,不用特意把一个变量设置为undefined

//对未声明的变量只能用typeof
// var tmp
alert(tmp);  //报错
typeof tmp;//得到答案,”undefined"
### null
null就是一个空对象指针
对于你不知道初始化什么值得时候,赋予它null,这样你下次也好判定是不是null
```javascript
//事实上这个返回true,因为undefined的值派生自null值
alert(null == undefined);

Boolean

要将一个值转化为其对应的boolean的值,可以调用转型函数Boolean();
string: 非空字符串转化为true, 空字符串false;
number: 任何非零数字值转化为true,0和nan转化为false;

Number

var num1 = 070 //八进制的56
var num2 = 0xA //十六进制的10

浮点数不准,慎用0.1+0.3不等于0.4

NaN 非数值,解决的问题,表示本来要返回数值的操作数未返回数值的情况。
值得注意的是,NaN和任何值都不相等,

alert(NaN == NaN);
alert(isNaN(NaN));//true
alert(isNaN(10));  //false
alert(isNaN("10")); //false
alert(isNaN("blue")); //true
alert(isNaN(true));  //false ,可以被转化为数值1
//任何不是数值的值被转化为数值,实在不行返回false

数值转换

Number()

把任何数据类型转换成数值,字符串空的转化为0,不空而且不是数字的话为NaN

Number(true) = 1;
Number(null) = 0;
Number(undefined) = NaN;
Number("asdasd") =NaN;
Number("") = 0;

重点看p30页

parseInt()

parseInt只认到数字结束

parseInt("1234blue");  //1234
parseInt(""); //NaN
parseInt("0xA"); //10
parseInt("10", 2);   //按照二进制解析
parseInt("10", 8);  //按照八进制解析

parseFloat()

parseFloat(22.34.5); //22.34

String

toString() 方法,null和undefined没有这个方法
num.toString(8) //把数字转成八进制
判定一个数是不是string可以用String()

String(null) = "null";
var tmp;
String(tmp) //undefined

Object

var o = new Object(); //创建新对象

在ECMASript中,Object类型是它的实例的基础。Object类型所具有的任何属性和方法也存在更具体的对象中
Object每个实例都具有下列属性和方法
1,constructor:保存着用于创建当前对象的函数。
2,hasOwnProperty(propertyName):用于检查给定的属性在当前实例中是否存在。
3, isPrototypeOf(object) 用于检查传入的对象是否是当前对象的原型。
4, toString()返回的对象用字符串表示
5, valueOf() 返回的对象的字符串,数值或布尔值来表示。
6, propertyIsEnumerable(propertyName) 用于检查给定的属性是否能够使用for-in语句

操作符

++n 在这个n被真正用上之前就+1了
n++ 在这个n被真正用上之后才+1了
几种有趣的情况

"15"++;  // 16
"asda"++ //NaN
false++ //1
var o = {
	valueOf: function(){
		return -1;
		}
	};
o--; //值变成-2,原因先调用对象的valueOf()方法,如果结果是NaN,调用toString()后再应用前述规则

位操作符

先将64位值转换成32位的整数,然后执行操作,最后将结果转换回64位。
负数二进制补码详情看 p40;
位非 ~, 变成负数还要减1;
位and , &
位或, |
位异或, XOR
左移 <<
右移 >> 第一位符号位不变
无符号右移 >>> 和>>在负数情况下不同

布尔操作符

逻辑非

alert(!"blue"); //false
alert(!NaN); //true
alert(!""); //true
alert(!12345); //false

逻辑与
第一个操作数是null,NaN,undefined分别返回null, NaN,undefined.
属于短路操作,第一个操作如果能决定结果false,不会对第二个操作数求值。
逻辑或
第一个操作数是null,NaN,undefined分别返回null, NaN,undefined.
属于短路操作,第一个操作如果能决定结果true,不会对第二个操作数求值。
利用这一点避免变量null或者undefined值
var myObject = preferredObject || backupObject;

乘性操作符

NaN与其他乘, 结果是NaN
Infinity与0相乘,结果是NaN

加性操作符

一个操作数结果是NaN,则结果是NaN
一个字符串和数字相加,数字串变成字符串串联

console.log(null+10); //10;
console.log(undefined+10);//NaN

减性操作符

var result6 = 5 - null;// 5

关系操作符

字符串比较,比较编码大小
一个值是布尔值,先转化为数值。
"23"<3 //false
因为比较第一个编码

相等操作符

一个操作数是布尔值,将其转换为数值
null 和undefined是相等的

NaN == NaN  //false
NaN !=NaN //true

全等 === //两个操作数未经转换就相等的情况下返回true
不全等!==

条件操作符

var max = (num1 > num2) ?num1:num2;

for语句

for( i = 0; i< count;i++)[
}
//没有var的话,我在外面也可以访问到。

for-in语句

for (property in expression) statement //要防止里里面有null或者undefined

break和continue

break会退出循环,从后面继续执行
continue跳出此段循环,从循环顶重新开始

label 看p59页

with语句 不太用了

###switch语句

函数

1, return之后语句没用
2, 可以有多个return
3, return; 代表return undefined
4,可以通过argument[i]调用参数,显式参数可以不写,可以用argument.length调用。
可以用argument[1]改写第二个参数,假如argument[1]没有指定他的值也是undefined.

没有重载d

两个函数重点只看第二个

变量,作用域和内存问题

基本类型是简单数据段,就是undefined, null, string ,number, boolean
引用类型是指那些可能由多个值构成的对象:object;

复制基本类型:从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本。
引用类型复制: 复制的值的副本是一个指针, 而这个值指向存储在堆中的一个对象,保存在堆内存中。

p70 -71 传递参数
https://www.zhihu.com/question/27114726
传参都是传递值而不是传递引用。 对于object传递的其实也是引用的地址。很经典的例子如下。

var obj1 = {
  value:'111'
};
 
var obj2 = {
  value:'222'
};
 
function changeStuff(obj){
  obj.value = '333';
  obj = obj2;
  return obj.value;
}
var foo = changeStuff(obj1);
console.log(foo);// '222' 参数obj指向了新的对象obj2
console.log(obj1.value);//'333'

作者:苏墨橘
链接:https://www.zhihu.com/question/27114726/answer/35481766
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

确定一个值是哪种基本类型可以使用typeof操作符,而确定一个值是哪种引用类型可以用instanceof操作符。
所有引用类型的值都是Object的实例。function的typeof是function

function amd(){
    alert("");
} //undefined
typeof amd  //"function"

变量对象都有一个作用域链,都是由内向外的。从内部环境到全局环境。内部环境可以用过作用域链访问所有外部环境,外部环境不能访问内部环境任何变量

延长作用域链

p75,with的解释
https://lllt.iteye.com/blog/1246424
”由于with语句块中作用域的‘变量对象’是只读的,所以在他本层定义的标识符,不能存储到本层,而是存储到它的上一层作用域”

没有块级作用域

js只有函数作用域,没有块级作用域。
也就是说if语句和for语句中的变量能被外部访问。
查询标识符, 函数先从内部局部变量开始查找变量,搜索到就停止,不然继续向全局搜索。

垃圾收集

标记清楚

https://zhuanlan.zhihu.com/p/23992332
那什么叫标记清除呢?JavaScript 中有个全局对象,浏览器中是 window。定期的,垃圾回收期将从这个全局对象开始,找所有从这个全局对象开始引用的对象,再找这些对象引用的对象…对这些活着的对象进行标记,这是标记阶段。清除阶段就是清除那些没有被标记的对象。
通俗的来讲,就是定时给所有内存中变量打上标记,然后现在在环境中,或使用中的变量去掉标记,最后删除还有标记的。

引用计数

如果一个变量被引用了,就引用+1.假如引用它的变量被其他引用了,那么就-1.最后定时清楚引用为0的值。
但是有个问题就是有时候两个变量相互引用。
为了避免循环引用,一般用代码消除,element = null;这叫解除引用,也能避免用内存太多。

引用就是按照地址传值

第五章

object

创建object两种方式

//构造函数
var person = new Object();
//对象字面量表示
var person = {
	name: "Nichloas",
	age : 29
	};

访问对象属性

//构造函数
//通常用第二种
person.["name"];
person.name;

array

数组可以动态调整,每一项可以保存任何类型的数据

var colors = new Array();
var colors = [];
var colors = new Array(20);

colors.length = 2;//可以动态缩短数组长度,会移除最后一项。

检测数组

value instanceof Array;
Array.isArray(value);

转换方法

toLocaleString()
toString()
valueOf()
join(“,"); //把数组的字符串用”,“连接在一起。

栈方法与队列方法

//栈方法
pop();
push();
//队列方法
shift(); //移除数组中的第一个项并返回该项;
push();//

//或者
unshift();
pop(); 

重排序方法

[].reverse;  //翻转数组顺序
[].sort(compare); //进行排序,可以接受一个function来声明排序方法。
//比如
function compare(value1, value2){
	return value2 - value1;
}

操作方法

concat();  //将新数组和原来你的数组合并
slice(); //数组切片, 返回新数组,老数组不变
splice(); //很强大的方法,看p95,可以对数组进行删除,插入和替换

位置方法

indexOf() ; //对一个数组查找对应的值, 找到返回位置,没找到返回-1;
lastIndexOf(); //从末尾开始往前找。必须严格相等。

迭代方法

1.every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则最终结果返回true;
2.some():对数组中的每一项运行给定函数,如果该函数对任一项都返回true,则最终结果返回true;
3.filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组;
4.map():对数组中的每一项运行给定函数,返回每次调用的结果组成的数组;
5.forEach:对数组中的每一项运行给定函数。这个方法没有返回值。
图片转自 https://www.cnblogs.com/shuiyi/p/5058524.html
在这里插入图片描述
在这里插入图片描述

Date

var date = new Date();
var someDate = new Date(Date.parse("May 25, 2004"));
//parse帮助你,把人类语言表述的日期创建出来。
var someDate = new Date("May 25, 2004");
var start = Date.now();
//取得现在这个时间点的时间

继承的方法

toLocaleString() ; //返回当地时间,时间用am,pm表示。
toString(); 

还有一些格式化方法
请看p101

RegExp类型

i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。
两种方式

var pattern1 = /[bc]at/i ; 
//构造函数创建
var pattern2 = new RegExp("[bc]at","i");
//使用构造函数,每次都是一个新的实例

RegExp实例属性

实例方法

exec() 专门为捕获组设计的
var matches = pattern.exec(text);
捕获组了解一下 p106

pattern.text() //接受一个字符串参数,匹配返回true,不匹配返回false

RegExp构造函数属性

Function

每个函数都是function类型的实例,函数名实际是指向函数对象的指针。
创造函数两种

//三种创造函数的方法
function sum(num1, num2){
	return num1 + num2;
}
var sum  = function(num1, num2){
	return num1 + num2;
};
var sum = new Function("num1", "num2", "return num1+num2");
//函数是对象,函数名是指针,函数名仅仅是指向函数的指针。

函数声明与函数表达式

//函数声明
function sum(num1, num2){
	return num1 + num2;
}
//代码开始执行之前,解析器通过一个名为函数声明提升的过程, 读取并将函数声明添加到执行环境
//函数表达式不能提前
var sum = function(num1, num2){
	return num1+num2;
};

作为值的函数

function callSomeFunction(someFunction ,someArgument){
	return someFunction(someArgument);
}
可以根据属性来比较
object[propertyName];
function funcName(someFunc, someParam){
    return someFunc(someParam)
}
function sum(num){
    return num + 3;
}
var result = funcName(sum, 5);
alert(result);  //=>8

函数内部属性

为了解耦,在函数内部,特别是递归函数,用arguments.callee代替函数名

另一个特殊对象是this,在全局的this就是windows,在函数内部调用就是函数的执行对象。

var o = { color:"blue" };
o.saycolor() ; //blue。绑定的是o

对于引用类型来说,prototype是保存他们所有实例方法的真正所在。tostring()和valueOf()等方法就是在prototype下面。

apply()和call()

每个函数都有两个非继承的方法,这两个方法用途都是在特定的作用域中调用函数。
最大的用处就是能够扩充函数赖以运行的作用域

window.color = "red";
var  o = { color : "blue" }function saycolor(){
	alert(this.color);
}
saycolor();

saycolor.call(this); //red
saycolor.call(o);  //red
saycolor.call(windows); //blue

bind()

//this值会绑定到传到bind()函数的值
window.color = "red";
var  o = { color : "blue" }function saycolor(){
	alert(this.color);
}
var objectSaycolor = saycolor.bind(o);
objectSaycolor();

基本包装类型

Boolean,Number, String()
基本包装类型就是帮你弄了个对象,提供了你一些方法
自动创建的基本包装类型,只存在于代码执行瞬间,然后立即被销毁
使用new调用基本包装类型的构造函数 (number),与直接调用同名的转型函数是不一样的(object);
每个包装类型都映射到同名的基本类型

number类型

方法
num.toFixed(2);
num.toExponential(1);

String类型

String类型每个实例都有一个length属性
字符方法: charAt(1); charCodeAt(1), stringvalue[1]

字符串拼接

stringvalue.concat();
三种字符串 的方法
slice(), substr(), substring();
字符串位置方法:
indexOf(“o”);
lastIndexOf(“o”)

trim()方法
//创建一个字符串的脚本,删除前置以及后缀的空格。
字符串大小转换方法 toLowerCase(), toLocaleLowerCase(), toUpperCase(), toLocaleUpperCase();

match()
//字符串匹配方法。
search()
//返回字符串第一个匹配项的索引,没有找到匹配项,返回-1
replace();
//替换,捕获组看一看 p128

localeCompare()方法
fromCharCode()方法

Global对象

不属于任何对象的属性和方法,最终就是它的属性和方法
所有在全局中定义的属性和方法,最终都是他的属性和方法。
encodeURI();
encodeURIComponent();

eval() 方法
通过eval()执行的 代码被认为是包含该次调用的执行环境之一。

Math 对象

Math()方法
min()或者max()
Math.ceil() 执行向上舍入, Math.floor() 执行向下舍入 , Math.round() 执行标准舍入
Math.random()方法

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

理解对象

对象定义为: 无序属性的集合,其属性可以包含基本值,对象和函数
每个对象由一个引用类型创建的
创建object

//创建对象实例
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software engineer";
person.sayName = function(){
	alert(this.name);
};
//字面量
var person = {
	name:  "Nicholas",
	age: 29,
	job: "Software Engineeer",
	sayName = function(){
		alert(this.name);
	};
	}
};

属性类型

[[Configurable]]:表示能否通过delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认值为true。
[[Enumerable]]:表示能否通过for-in 循环返回属性。默认值为true。
[[Writable]]:表示能否修改属性的值。默认值为true。
[[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认值为undefined。
修改属性默认的特性,必须使用ECMAScript 5 的Object.defineProperty()方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属性必须是:configurable、enumerable、writable 和value。设置其中的一或多个值,可以修改对应的特性值。

var person = {};
Object.defineProperty(person, "name", {
    configurable: false,
    value: "Nicholas"
});
alert(person.name); //"Nicholas"
delete person.name;
alert(person.name); //"Nicholas"

访问器属性

不包含数据值;它们包含一对儿getter 和setter 函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter 函数并传入新值.

//重写setter,和getter
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;
alert(book.edition); //2

读取属性的特性

Object.getOwnPropertyDescriptor()

var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
alert(descriptor.value); //2004
alert(descriptor.configurable); //false
alert(typeof descriptor.get); //"undefined"

var descriptor = Object.getOwnPropertyDescriptor(book, "year");
alert(descriptor.value); //undefined

创建对象

object构造函数和字面量创造某个对象有个缺点,就是有很多重复的代码

工厂模式

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

虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)

构造函数模式

构造函数可用来创建特定类型的对象。像Object 和Array 这样的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。

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

Person()中的代码除了与createPerson()中相同的部分外,还存在以下不同之处:
没有显式地创建对象;
直接将属性和方法赋给了this 对象;
没有return 语句。
按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。
这两个对象都有一个constructor(构造函数)属性,该属性指向Person

alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true

但是,提到检测对象类型,还是instanceof操作符要更可靠一些。我们在这个例子中创建的所有对象既是Object 的实例,同时也是Person的实例

alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true

创造自定义构造函数意味着它的实例标识为特定的类型

将构造函数当作函数

// 当作构造函数使用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); //"Nicholas"
// 作为普通函数调用
Person("Greg", 27, "Doctor"); // 添加到window->全局变量
//普通函数调用时this对象指向global对象
window.sayName(); //"Greg"
// 在另一个对象的作用域中调用,绑定
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"

构造函数的问题
就是每个对象里面function其实不是一个
主要问题,就是每个方法都要在每个实例上重新创建一遍,即:

this.sayName = new Function("alert(this.name)"); // 与声明函数在逻辑上是等价的
alert(person1.sayName == person2.sayName); //false

因此,大可像下面这样,通过把函数定义转移到构造函数外部来解决这个问题。

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

新问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让人无法接受的是:如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。

原型模式

理解原型对象

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

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.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

在这里插入图片描述
只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype 属性所在函数的指针。就拿前面的例子来说,Person.prototype. constructor 指向Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。
当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。

虽然在所有实现中都无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,如果[[Prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true
ECMAScript 5 增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值。

alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nichola

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

var person1 = new Person();
var person2 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person2.name); //"Nicholas"——来自原型

使用delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性
delete person1.name;
alert(person1.name); //"Nicholas"——来自原型

使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法(不要忘了它是从Object 继承来的)只在给定属性存在于对象实例中时,才会返回true。

var person1 = new Person();
var person2 = new Person();
alert(person1.hasOwnProperty("name")); //false
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person1.hasOwnProperty("name")); //true
alert(person2.name); //"Nicholas"——来自原型
alert(person2.hasOwnProperty("name")); //false
delete person1.name;
alert(person1.name); //"Nicholas"——来自原型

原型与in操作符

在单独使用时,in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。
同时使用hasOwnProperty()方法和in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中,如下所示

function hasPrototypeProperty(object, name){
    return !object.hasOwnProperty(name) && (name in object);
}

for-in循环,返回的是所有能够通过对象访问的,可枚举的属性
要取得对象上所有可枚举的实例属性,可以使用的Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); //"name,age" .只包含了实例属性
```javascript

如果你想要得到所有实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法。
```javascript
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys); //"constructor,name,age,job,sayName"

更简单的语法,每次用prototype你烦不烦

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

例外:constructor 属性不再指向Person 了。前面曾经介绍过,每创建一个函数,就会同时创建它的prototype 对象,这个对象也会自动获得constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的prototype 对象,因此constructor 属性也就变成了新对象的constructor 属性(指向Object 构造函数),不再指向Person 函数。此时,尽管instanceof操作符还能返回正确的结果,但通过constructor 已经无法确定对象的类型了

var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); //true

原型的动态性

如果是重写整个原型对象,那么情况就不一样了。我们知道,调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型,而不指向构造函数。

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

在这里插入图片描述

原生对象的原型

所有原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法。在Array.prototype 中可以找到sort()方法,而在String.prototype 中可以找到substring()方法

原型的问题

原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说得过去,毕竟(如前面的例子所示),通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值的属性来说,问题就比较突出了。

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

person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true

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

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

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

动态原型模式

function Person(name, age, job){
  //属性
    this.name = name;
    this.age = age;
    this.job = job;
    //方法
    if (typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    }
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName()

通过if来判断,不能直接用function,否则会new function的实例。

寄生构造函数模式

在前述的几种模式都不适用的情况下,可以使用寄生(parasitic)构造函数模式。

function Person(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(this.name);
    };
    return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"

除了使用new 操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的。构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个return 语句,可以重写调用构造函数时返回的值。
不要使用这种模式

稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this 的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this 和new),或者在防止数据被其他应用程序(如Mashup程序)改动时使用。稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new 操作符调用构造函数。按照稳妥构造函数的要求,可以将前面的Person 构造函数重写如下。

function Person(name, age, job){
    //创建要返回的对象
    var o = new Object();
    //可以在这里定义私有变量和函数
    //添加方法
    o.sayName = function(){
        alert(name);
    };
    //返回对象
    return o;
}·

继承

原型链

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。

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();
alert(instance.getSuperValue()); //true

以上代码定义了两个类型:SuperType 和SubType。每个类型分别有一个属性和一个方法。它们的主要区别是SubType 继承了SuperType,而继承是通过创建SuperType 的实例,并将该实例赋给SubType.prototype 实现的。实现的本质是重写原型对象,代之以一个新类型的实例。
在这里插入图片描述
instance 指向SubType的原型, SubType 的原型又指向SuperType 的原型。getSuperValue() 方法仍然还在SuperType.prototype 中,但property 则位于SubType.prototype 中。这是因为property 是一个实例属性,而getSuperValue()则是一个原型方法。既然SubType.prototype 现在是SuperType的实例,那么property 当然就位于该实例中了。

所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。
一句话,SubType 继承了SuperType,而SuperType 继承了Object。当调用instance.toString()时,实际上调用的是保存在Object.prototype 中的那个方法。

确定原型和实例的关系

alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true

第二种方式是使用isPrototypeOf()方法。同样,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型,因此isPrototypeOf()方法也会返回true

alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true

谨慎地定义方法

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

还有一点需要提醒读者,即在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链

//继承了SuperType
SubType.prototype = new SuperType();

//使用字面量添加新方法,会导致上一行代码无效
SubType.prototype = {
    getSubValue : function (){
        return this.subproperty;
    },
    someOtherMethod : function (){
        return false;
    }
};

原型链的问题

最主要的问题来自包含引用类型值的原型。想必大家还记得,我们前面介绍过包含引用类型值的原型属性会被所有实例共享;

function SuperType(){
    this.colors = ["red", "blue", "green"];
}
function SubType(){
}

//继承了SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();

instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green,black"

原型链的第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。

借用构造函数

子类型构造函数的内部调用超类型构造函数。
函数只不过是在特定环境中执行代码的对象,因此通过使用apply()和call()方法也可以在(将来)新创建的对象上执行构造函数

function SuperType(){
    this.colors = ["red", "blue", "green"];
}
function SubType(){
    //继承了SuperType
    SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(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();
alert(instance.name); //"Nicholas";
alert(instance.age); //29

方法都在构造函数中定义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的。

组合继承

使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
    //继承属性
    SuperType.call(this, name);
    this.age = age;
}

//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

最常用的方法,instanceof和isPrototypeOf()也能识别。

原型式继承

他的想法是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}

在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。从本质上讲,object()对传入其中的对象执行了一次浅复制。
ES5 通过新增的Object.create()方法规范了原型式继承
var anotherperson = Object.create(person);

问题

引用类型的属性还是会共享。

寄生式继承

寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

function createAnother(original){
    var clone = object(original); //通过调用函数创建一个新对象
    clone.sayHi = function(){ //以某种方式来增强这个对象
        alert("hi");
    };
return clone; //返回这个对象
}
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"

没法做到函数复用,效率低

寄生组合式继承

组合继承是JavaScript 最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
    SuperType.call(this, name); //第二次调用SuperType()
    this.age = age;
}
SubType.prototype = new SuperType(); //第一次调用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};

在第一次调用SuperType 构造函数时,SubType.prototype 会得到两个属性:name 和colors;它们都是SuperType 的实例属性,只不过现在位于SubType 的原型中。当调用SubType 构造函数时,又会调用一次SuperType 构造函数,这一次又在新对象上创建了实例属性name 和colors。于是,这两个属性就屏蔽了原型中的两个同名属性。
有两组name 和colors 属性:一组在实例上,一组在SubType 原型中。这就是调用两次SuperType 构造函数的结果。好在我们已经找到了解决这个问题方法——寄生组合式继承。
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

function inheritPrototype(subType, superType){
    var prototype = object(superType.prototype); //创建对象
    prototype.constructor = subType; //增强对象
    subType.prototype = prototype; //指定对象
}

这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加constructor 属性,从而弥补因重写原型而失去的默认的constructor 属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我们就可以用调用inherit-Prototype()函数的语句,去替换前面例子中为子类型原型赋值的语句了

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
    SuperType.call(this, name);
    this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
    alert(this.age);
};

这个例子的高效率体现在它只调用了一次SuperType 构造函数,并且因此避免了在SubType.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof 和isPrototypeOf()。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

第七章 函数表达式

函数两种: 函数声明, 函数表达式
函数声明有函数提升, 即使先用函数再声明也没关系。但是函数表达式不行 ,它必须先声明再使用。

递归

arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用。
不过这种方式在严格模式不能使用arguments.callee,需要借助命名函数表达式

//创建一个f 命名的函数表达式,然后将它赋值给变量factorial
    var factorial = (function f(num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * f(num - 1); //函数f 依然有效
        }
    });
    console.log(factorial(4));//返回24

闭包

闭包是指有权访问另一个函数作用域中的变量的函数, 创建闭包的常见方式,就是在一个函数中创建另一个函数。

**当某个函数被调用时, 会创建一个执行环境以及相应的作用域链,使用arguments和其他命名参数 的值来初始化函数的活动对象。**作用域链中,外部函数活动对象总处于第二位。

无论什么时候在函数中访问 一个变量时,会从作用域中搜索具有相应名字的变量,当函数执行完成后,局部活动对象就会被销毁,内存中仅保存全局作用域。

function compare(value1, value2) {
        if (value1 < value2) {
            return -1;
        } else if (value1 > value2) {
            return 1;
        } else {
            return 0;
        }
    }

在这里插入图片描述

闭包作用链细节:

在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到他的作用域链中

内部定义的匿名函数的作用域链实际上会包含外部函数的活动对象

即使外部函数执行完毕,内部匿名函数依然不会被销毁,然后他依然引用这个活动对象,直至这个匿名函数被销毁后才会全部销毁

闭包缺点

闭包只能取得包含函数中任何变量的最后一个值,因为闭包保存的是整个变量对象,而不是某个特殊的变量

function createFunctions() {
        var result = new Array();
        for (var i = 0; i < 10; i++) {
            result[i] = function () { //这是一个闭包
                //因为闭包保存的是整个createFunctions变量对象,所以当他执行完成的时候(for循环结束),
                //i是等于10的,所以就会是10,由始至终,闭包里面引用的都是一整个变量对象,而不是一个变量
                return i;
            };
        }
        return result; //返回的是一个数组,数组里面每一个项目都是一个function
    }
    var test = createFunctions();
    for (var i = 0; i < 10; i++) {
        //需要执行这个 function 才能够获取里面的值
        console.log(test[i]());//都是10 
    }

关于this对象

var name = "the window";
    var object = {
        name: "my object",
        //返回一个匿名函数
        getNameFunc: function () {
            //匿名函数里面又返回一个匿名函数(闭包)
            //当返回的时候,当前环境已经变成了全局环境了,搜索的话直接在当前活动对象(全局)找到了
            return function () {
                return this.name; //所以返回的是全局环境的 name
            }
        }
    };
    //object.getNameFunc()返回一个函数,然后再加上(),这个函数就会执行
    console.log(object.getNameFunc()());//返回 the window

var name = "the window";
    var object = {
        name: "my object",
        //返回一个匿名函数
        getNameFunc: function () {
            //将当前对象(当前这个function)引用保存起来
            var that = this;
            //匿名函数里面又返回一个匿名函数(闭包)
            //当返回的时候,使用的是保存起来的这个对象引用,所以会返回这个对象的属性
            return function () {
                return that.name;
            }
        }
    };
    //object.getNameFunc()返回一个函数,然后再加上(),这个函数就会执行
    console.log(object.getNameFunc()());//返回 my object

模仿块级作用域

相当于弄个匿名函数。匿名函数中定义的任何变量都会在执行结束时被销毁。

(function () {
      //块级作用域
    }());
---------分解一下
    ( //第一对圆括号
    function () {
      //块级作用域  
    }
    ()//第二对圆括号
    );
//将函数声明包含在第一对中,表示他实际上是一个函数表达式
//而紧随其后的第二对圆括号会立即调用这个函数

私有变量

有权访问私有变量的方法叫做特权属性

构造函数法

function MyObject() {
        //私有变量和私有函数
        var privateVariable = 10; 
        function privateFunction() {
            return false;
        }
        //特权方法
        //因为这里作为闭包有权访问在构造函数定义的所有变量和函数
        //另外设置了this可以被调用
        this.publicMethod = function () {//外部只能通过这个方法访问私有属性和函数
            privateVariable++;
            return privateFunction();
        }
    }

使用构造函数模式的缺点就是针对每个实例都会创建同样一组新方法

静态私有变量

这种方式
私有变量和函数是由实例共享(因为被闭包引用着这个块级作用域)
由于特权方法是在构造函数的原型上定义的,所以所有实例都使用同一个特权方法

(function () {
        //私有变量和私有函数
        var privateVariable = 10;

        function privateFunction() {
            return false;
        }

        //构造函数
        MyObject = function () { //没有函数声明,使用函数表达式创建
            //因为函数声明只能构建局部函数,现在创建了一个全局函数
            //因为这是全局函数,所以也相对来说就是一个静态了.
        };
        //公有/特权方法
        //在原型上定义方法,所有这个原型的实例都能共享
        //特权方法是一个闭包,保存着对包含作用域的引用(当前这个块级作用域)
        MyObject.prototype.publicMethod = function () { //这个是一个闭包
            privateVariable++;
            return privateFunction();
        }
    })();

模块模式

//需要一个单例来管理应用程序级的信息
    //创建一个application对象(返回单例对象)
    var application = function () {
        //初始化一个数组
        var components = new Array();
        components.push(new BaseComponent());
        //返回的单例可以使用方法,然后获取数组的长度或者添加数组内容
        return {
            getComponentCount: function () {
                return components.length;
            },
            registerComponent: function (component) {
                if (typeof component == "object") {
                    components.push(component);
                }
            }
        }
    }();

第八章

bom的核心对象是windows,既是通过javascript访问浏览器的一个接口,又是ECMAScript规定的global对象
所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法。

窗口关系及框架

如果页面中包含框架,则每个框架都拥有自己的window对象,并且保存在frames集合中。

<html>
  <head>
    <title> Frameset Example</title>
  </head>
  <frameset rows="160, *">
    <frame src="frame.htm" name="topFrame">
    <frameset cols="50%, 50%">
      <frame src="anotherframe.htm" name="leftFrame">
      <frame src="yetanotherframe.htm" name="rightFrame">
    </frameset>
  </frameset>
</html>

top对象始终指向最高(最外层)的框架,也就是浏览器窗口。
与top相对的另一个window对象是parent。parent(父)对象始终指向当前框架的直接上层框架。在某些情况下parent有可能等于top,但在没有框架的情况下,parent一定等于top。此时他们都是window

窗口位置

// 确定 screenLeft 和 screenTop 属性是否存在
// 存在,是在 IE, Safari, Opera, Chrome
// 否则,是在 Firefox中

var leftPos = (typeof window.screenLeft == "number") ?
                window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ?
                window.screenTop : window.screenY;

窗口大小

IE9+ Firefox Safari Opera Chrome 均为此提供了4个属性:innerwidth innerHeight outerWidth outerHeight。
IE9 Safari Firefox 中 outerWidth outerHeight 返回浏览器窗口本身的尺寸(无论是从最外层的window对象还是从某个框架访问)。
Opera中这两个属性的值表示页面视图容器的大小。
innerwidth innerHeight则表示容器中页面视图区的大小(减去边框宽度)。
Chrome 中 四个值返回的相同,即视口(viewport)大小而非浏览器窗口大小。

先调用最基本的值,假如不是数字说明不支持,再调用document.documentElement.clientWidth和document.documentElement.clientHeight
var pageWidth = window.innerWidth;
var pageHeight = window.innerHeight;

// document.compatMode 这个属性将在第10章讨论
if (typeof pageWidth != "number") {
  if (document.compatMode == "CSS1Compat") {
    pageWidth = document.documentElement.clientWidth;
    pageHeight = document.documentElement.clientHeight;
  } else {
    pageWidth = document.body.clientWidth;
    pageHeight = document.body.clientHeight;
  }
}

导航和打开窗口

window.open() 方法既可以导航到一个特定的URL,也可以打开一个新的浏览器窗口。接受四个参数:

要加载的URL
窗口目标 (窗口或者框架的名字以及特殊窗口名称: _self, _parent, _top, _blank)
特性字符串(新窗口的特性设置,逗号分隔)
表示新页面是否取代浏览器历史记录中当前加载页面的布尔值
window.open("http://www.wrox.com", "topFrame");

安全性屏蔽

浏览器拓展程序(插件)或其他程序阻止弹窗,那么 window.open()通常会抛出错误。因此不但要检测返回值,还要将对 window.open()的调用封装在一个 try-catch 块中

var bloacked = false;

try {
  var wroxWin = window.open("http://www.wrox.com", "_blank");
  if (wroxWin == null) {
    bloacked = true;
  }
} catch (ex) {
  bloacked = true;
}

if (blocked) {
  console.log("The popup was blocked!");
}

间歇调用和超时调用

间歇调用和超时调用
JavaScript是单线程语言,但它允许通过设置超时值和间歇值来调度代码在特定的时刻执行。

setTimeout(() => {
  alert("Hello World!");
}, 1000);

JavaScript是一个单线程解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JavaScript任务队列。这些任务会按照将他们添加到队列中的顺序执行。
第二个参数是告诉JavaScript再过多久把当前任务添加到队列中。之后如果队列是空的,那么添加的代码会立即执行,如果不是空的,那么就要等前面的代码执行完了以后再执行。
该方法会返回一个数值ID,这是计划执行代码的唯一标识符,可以通过它来取消超时调用。

var timeoutId = setTimeout(() => {
  alert("Hello World!");
}, 1000);

// 取消执行
clearTimeout(timeoutId);

超时调用的代码都是在全局作用域中执行的,因此函数中 this 的值在非严格模式下 指向 window ,在严格模式下是 undefined。
setInterval()用法与上述类似

系统对话框

alert(), confirm(), prompt() 方法可以调用系统对话框向用户显示消息。样式css没关系就是浏览器样式
alert不必多说
confirm会显示ok 还是cancel。 有if(confirm(“are you sure”)) 来完成
prompt() 返回一个字符串或者null,输入了值并确定,返回字符串,其他方法关闭返回null

var result = promt("what is you name", "");
if( result !== null){
	alert("welcome,"+ result);
	}

location 对象

location就是url相关
location是最有用的BOM对象之一,它提供了与当前窗口中加载的文档有关的信息,还提供一些导航功能。
location 对象既是window对象的属性,也是document对象的属性;换言之,window.location document.location引用的是同一个对象。
location.hostname “www.wrox.com” 返回服务器名称不带端口号
location.pathname “/WileyCDA/” 返回URL中的目录或者文件名

查询字符串参数

funcction getQueryStringArgs() {

  // 取得查询字符串并去掉开头的问号
  var qs = (location.search.length > 0 ? location.search.substring(1) : "");

  // 保存数据的对象
  var args = {};

  // 取得每一项
  var items = qs.length ? qs.split("&") : [];
  var item = null;
  var name = null;
  var value = null;

  // 在for循环中使用
  var i = 0;
  var len = items.length;

  // 逐个将每一项添加到args对象中
  for (var i = 0; i < len; i++) {
    item = items[i].split("=");
    name = decodeURIComponent(item[0]);
    value = decodeURIComponent(item[1]);

    if (name.length) {
      args[name] = value;
    }
  }

  return args;
}

位置操作 location

都可以改变location的地址。
首先也是最常用的方式 assign()方法,并为其传递一个URL。立即打开新的URL并在浏览器的历史记录中生成一条记录。
location.assign("http://www.wrox.com");
window.location = "http://www.wrox.com";
location.href = "http://www.wrox.com";

// 将URL修改为 http://www.wrox.com/wileyCDA/#section1
location.hash = "#section1";

// 将URL修改为 http://www.wrox.com/wileyCDA/?q=javascript
location.search = "?q=javascript";

// 将URL修改为 http://www.wrox.com/wileyCDA/
location.hostname = "www.yahoo.com"

通过上述任何一种方式修改URL之后,浏览器的历史记录中就会生成一条新纪录,因此用户通过单击“后退”按钮都会导航到前一个页面。要禁用这种行为,可以使用replace()方法。
调用replace()方法后用户不能回到上一个页面。
reload

location.reload();//重新加载(有可能从缓存中加载)
location。reload(ture);// 重新加载(从服务器中加载)

Navigator

这个就是浏览器相关,有浏览器一些属性。
##检查插件
Navigator.plugins
一般来说name属性中会包含检测插件必需的所有信息,但有时候也不完全如此。在检测插件时,需要像下面这样循环代码每个插件并将插件的name与给定的名字进行比较

// 检测插件(在IE中无效)
function hasPlugin(name) {
  name = name.toLowerCase();
  for (var i=0; i < navigator.plugins.length; i++) {
    if (navigator.plugins[i].name.toLowerCase().indexOf(name) > -1) {
      return true;
    }
  }
  return false;
}

//  检测flash
console.log(hasPlugin("Flash"));

//  检测QuickTime
console.log(hasPlugin("QuickTime"));

对于ie浏览器要用new ActiveXObject(name),创建一个com对象的实例。如果实例化成功,说明插件可用。

注册处理程序

registerContentHandler() 和 registerProtocolHandler() 方法可以让一个站点知名它可以处理特定类型的信息。随着RSS阅读器和在线电子邮件程序的信息,注册处理程序就为像使用桌面应用程序一样默认使用这些在线应用程序提供了一种方式。

// 要将一个站点注册为处理RSS源的处理程序
navigator.registerContentHandler("application/rss+xml",
  "http://www.somereader.com?feed=%s", "Some Reader");
// 要将一个应用程序注册为默认的邮件客户端
navigator.registerProtocolHandler("mailto",
"http://www.somemailclient.com?cmd=%s", "Some Mail Client");

history

history对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。因为history是window对象的属性
使用go()方法可以在用户的历史记录中任意跳转,可以向前向后,接受一个参数整数值或者字符串。

history.go(-1);
history.go(1);
history.forward();
history.back();

总结一下

location主要针对url
navigator主要针对浏览器
screen没啥用

第九章 客户端检测

能力检测

通常就用if语句

if (object.propertyInQuestion) {
//使用object.propertyInQuestion
}

更好的能力检测

有时候尽量用typeOf检测

function isSortable(object){
  return typeof object.sort == 'function';
}

浏览器检测

检测某几个特性不能确定是不是哪个浏览器,下面两个例子就是用力过猛了,浏览器特性可能会变。

var isFirefox = !!(navigator.vendor && navigator.vendorSub); // 不够具体
var isIE = !!(document.all && document.uniqueID); // 假设过度
重温一下双非操作符
双逻辑非操作,会把一个值(数字,字符串…..)转换为布尔值。第一次逻辑非操作取反的布尔,第二次获得最初元素本身对应的布尔。

应该将能力检测作为确定下一步解决方案的依据,而不是用它来判断用户使用的是什么浏览器。

怪癖检测

没啥好讲的,针对一些浏览器特有的怪癖。

用户代理检测

用户代理检测通过检测用户代理字符串来确定实际使用的浏览器。它通过JavaScript的navigator.userAgent属性访问。
chrome输入后得到,为了提高兼容性不被主流网站屏蔽,所以用了mozilla,这是另一个浏览器公司的标识符
“Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36”

检测技术

确切知道浏览器型号没啥意义,不如知道他的呈现引擎,主要5大呈现引擎:IE,Gecko,WebKit,KHTML,Opera.
完整的客户端,浏览器,和系统检测

var client = function(){
    // 呈现引擎
    var engine = {
        ie:0,
        gecko:0,
        webkit:0,
        khtml:0,
        opera:0,

        // 完整的版本号
        ver:null
    };

    // 浏览器
    var browser = {
        // 主要浏览器
        ie:0,
        firefox:0,
        safari:0,
        konq:0,
        opera:0,
        chrome:0,

        // 具体的版本号
        ver:null
    };

    // 检测呈现引擎和浏览器
    var ua = navigator.userAgent;
    if (window.opera) {
        engine.ver = browser.ver = window.opera.version();
        engine.opera = browser.opera = parseFloat(engine.ver);
    } else if (/AppleWebKit\/(\S+)/.test(ua)) {
        engine.ver = RegExp["$1"];
        engine.webkit = parseFloat(engine.ver);

        // 确定是Chrome还是Safari
        if (/Chrome\/(\S+)/.test(ua)) {
            browser.ver = RegExp["$1"];
            browser.chrome = parseFloat(engine.ver);
        } else if (/Version\/(S+)/.test(ua)) {
            browser.ver = RegExp["$1"];
            browser.safari = parseFloat(browser.ver);
        } else {

            // 近似地确定版本号
            var safariVersion = 1;
            if (engine.Webkit <100) {
                safariVersion = 1;
            } else if (engine.webkit < 312) {
                safariVersion = 1.2;
            } else if (engine.webkit < 412) {
                safariVersion = 1.3;
            } else {
                safariVersion = 2;
            }

            browser.safari = browser.ver = safariVersion;
        }
    } else if (/KHTML\/(S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) {
        engine.ver = browser.ver = RegExp["$1"];
        engine.khtml = browser.kong = parseFloat(engine.ver);
    } else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){
        engine.ver = RegExp["$1"];
        engine.gecko = parseFloat(engine.ver);

        // 确定是不是firefox
        if (/Firefox\/(S+)/.test(ua)) {
            browser.ver = RegExp["$1"];
            browser.firefox = parseFloat(browser.ver);
        }
    } else if (/MSIE ([^;]+)/.test(ua)) {
        engine.ver = browser.ver = RegExp["$1"];
        engine.ie = browser.ie = parseFloat(engine.ver);

    }

    // 检测浏览器
    browser.ie = engine.ie;
    browser.opera = engine.opera;

    // 返回这些对象
    return {
        engine:engine,
        browser: browser
    }
}();

console.log(client.engine);
console.log(client.browser);

还要识别系统,windows,mac,linux.windows不同版本号挺麻烦的,要用正则表达式
还要识别手机浏览器,游戏浏览器。

总结

一般情况下,先用能力检测和怪癖检测,实在不行再用用户代理检测。
能力检测:在编写代码之前先检测特定浏览器的能力。例如,脚本在调用某个函数之前,可能要先检测该函数是否存在。
怪癖检测:怪癖实际上是浏览器中的bug,怪癖检测通常涉及到运行一小段代码,然后确定浏览器是否存在某个怪癖,由于怪癖检测与能力检测相比效率更低,因此应该只在某个怪癖会干扰脚本运行时才使用。
用户代理检测:通过检测用户代理字符串来识别浏览器。用户代理字符串包含大量与浏览器有关的信息,包括浏览器、浏览器版本、平台、操作系统等。

DOM

这章转自
https://www.kancloud.cn/crossken/professional_js_web_developers/207410
1.文档节点是每个文档的根节点。在HTML页面,文档节点始终都是元素,因此我们也称其为文档元素。

2.DOM1级定义了一个Node接口,该接口将由DOM中的所有节点类型实现。这个Node接口在JavaScript中是作为Node类型实现的。除了IE之外,在其他所有浏览器中都可以访问到这个类型。JavaScript中的所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法。

3.每个节点都有一个nodeType属性,用于表明节点的类型。节点类型由在Node类型中定义的12个数值常量来表示。其中元素节点属性值为1,特性节点的属性值为2,文本节点的属性值为3。将nodeType属性与数字值比较可以检测其节点类型:

if (someNode.nodeType == 1){
    alert('Node is an element');
}

4.要了解节点的具体信息,可以使用nodeName和nodeValue属性。其中nodeName保存着节点的具体名称(如标签的nodeName值为大写的‘P’,nodeValue始终为null)。

5.每个节点都有一个childNodes属性,其中保存着一个NodeList对象。NodeList对象是一种类数组对象,用于保存一组有序的节点。虽然可以使用方括号语法来访问其子项,而且这个对象也有length属性,但它并不是Array的实例。NodeList对象的独特之处在于,它实际上是基于DOM结构动态执行查询的结构,因此DOM结构的变化能够自动反映在NodeList对象中。将childNodes转换成数组的方法:

function convetToArray(nodes){
    var array = null;
    try {
        array = Array.prototype.slice.call(nodes,0);//针对非ie浏览器
    } catch(error) {
        array = new Array();
        for (var i=0;i<nodes.length;i++) {
            array.push(nodes[i]);
        }
    }
    return array;
}

元素节点内的空白符也会作为文本标签被childNodes所包含。

6.描述节点间关系的属性还有:parentNode、nextSibling、previousSibling、firstChild、lastChild

7.**hasChildNodes()**方法在节点包含一或多个子节点的情况下返回true。所有节点都有的最后一个属性是ownerDocument,该属性指向表示整个文档的文档节点,通过这个属性我们可以不必在节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点。

8.**appendChild()**向节点末尾添加一个节点,并返回新增节点。如果传入到appendChild()中的节点已经是文档的一部分了,那相当于将节点剪切了。

9.**insertBefore()**方法将一个新节点插到指定节点前面,并返回这个新节点。这个方法接收两个参数:要插入的节点和作为参照的节点。如果参照节点是null则与appendChild()执行相同结果。insertBefore()同样可以实现剪切。

10.**replaceChild()**方法将一个新节点替换掉原节点,并返回原节点。

11.**removeChild()**方法删除一个节点。

12.以上四个方法操作的都是某个节点的子节点,也就是说,要使用这几个方法必须先取得父节点(使用parentNode属性)。

13.**cloneNode()**方法用于创建节点副本。若传入参数为true,则会进行深复制,即复制节点及其整个子节点树。否则只会复制原节点本身。cloneNode()方法不会复制添加到DOM节点中的JavaScriipt属性,例如事件处理程序等。

15.document.documentElement指向元素。document.body指向元素。

var html = document.documentElement;
var body = document.body;

16.通过document.title属性可以取得当前页面的标题(并非取得完整的title元素DOM对象),也可以修改当前页面的标题并反映在浏览器的标题栏中。修改title属性的值会改变

  1. document.domain
    如果URL 中包含一个子域名,例如p2p.wrox.com,那么就只能将domain设置为"wrox.com",不能将这个属性设置为URL 中不包含的域。
//假设页面来自p2p.wrox.com 域
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出错!

**当页面中包含来自其他子域的框架或内嵌框架时,由于跨域安全限制,来自不同子域的页面无法通过JavaScript通信。而通过将每个页面的document.domain设置为相同的值,这些页面就可以互相访问对方包含的JavaScript对象了。比如有个页面加载自www.wrox.com,其中包含一个内嵌框架,框架内的页面加载自p2p.wrox.com 由于document.domian字符串不一样,内外两个页面之间无法相互访问对方的javascript对象,但如果将这两个页面的ducument.domain都设置成wrox.com,它们之间就能相互通信了
**
18. 查找元素
getElementById();
getElementsByTagName();
getElementsByName();

  1. 文档写入
document.write("<strong>" + (new Date()).toString() + "</strong>");
  1. element 类型,每个属性比如dic,有div.id, div.classname, div.title, div.lang,
  2. 取得特性,三种方式, getAttribute(), setAttribute(), removeAttribute().
  3. element.attributes可以得到所有attributes
  4. 创建元素,document.createElement(“div”) div.id = “myNewDiv” div.className = “box”
  5. 子节点 , childNodes 属性
  6. text 类型 nodeType的值为3 nodeValue为所包含的文本 createTextNode() 创建文本节点
  7. normalize()方法,将所有文本节点合成一个节点,splitText()将一个节点分成两个节点
  8. comment类型 注释
  9. CDATASection 类型 DocumentType类型 DocumentFragment类型(作为一个仓库转移文档)
  10. 动态脚本 一种
<script type="text/javascript" src="client.js"></script>

另一种

var script = document.createElement("script");
script.type = "text/javascript";
script.src="client.js";
document.body.appendChild(script);
  1. 动态样式
<link rel = "stylesheet" type = "text/css" href = "styles.css">

还有一种方法

function loadCss(url){
var link=document.createElement("link");
link.rel="stylesheet";
link.href=url;
var head=document.getElementsByTagName("head")[0];
head.appendChild(link);
}
loadCss("http://www.damifanli.com/data/css/index_index_914724003.css")
  1. 操作表格
// 创建 table
var table = document.createElement("table");
table.border = 1;
table.width = "100%";

// 创建tbody
var tbody = document.createElement("tbody");
table.appendChild(tbody);

// 创建第一行
tbody.insertRow(0);
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild(document.createTextNode("cell 1,1"));
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild(document.createTextNode("cell 2,1"));


// 创建第二行
tbody.insertRow(01);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode("cell 1,2"));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode("cell 2,2"));

document.body.appendChild(table);
  1. NodeList的动态性(还有NameNodeMap和HTMLCollection)
  2. DOM会带来多次渲染和查询,非常影响性能,所以应该尽量减少操作

第十一章 DOM扩展

querySelector()方法

// 取得body元素
var tbody = document.querySelector('body');

// 取得ID为"myDIV"的元素
var myDIV = document.querySelector("#myDiv");

// 取得类为"selected"的第一个元素
var selected = document.querySelector(".selected");

// 取得类为"button"的第一个图像元素
var img = document.body.querySelector("img.button");

querySelectorAll()方法

// 取得某<div>中的所有<em>元素(类似于getElementsByTagName("em"))
var ems = document.getElementById("myDiv").querySelectorAll("em");

// 取得类为"selected"的所有元素
var selecteds = document.querySelectorAll(".selected");

// 取得所有<p>元素中的所有<strong>元素
var strongs = document.querySelectorAll("p strong");

html5

getElementsByClassName()方法

该方法可以通过document对象及所有HTML元素调用该方法。

classlist属性

classlist有add, contains, remove,toggle等方法。可以对类名进行处理

焦点管理

HTML5也添加了辅助管理DOM焦点的功能。首先就是document.activeElement属性,这个属性始终会引用DOM中当前获得了焦点的元素。

var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button); // true

默认情况下,文档刚刚加载完成时,document.activeElement中保存的是document.body元素的引用。文档加载期间,docuemnt.activeElement的值为null。
另外就是新增了document.hasFocus()方法,这个方法用于确定文档是否获得了焦点。

var button = document.getElementById("myButton");
botton.focus();
alert(document.hasFocus()); // true

HTMLDocument的变化

document的readystate 分loading complete
document.head属性
document.charset
自定义数据属性 data-appId html5可以为元素添加非标准的属性
https://segmentfault.com/a/1190000010995841

innerHTML

innerHTML就是一个属性可以获得一段html代码 div.innerHTML =

outerHTML

在读模式下,outerHTML 返回调用它的元素及所有子节点的 HTML 标签。在写模式下,outerHTML 会根据指定的 HTML 字符串创建新的 DOM 子树。然后用这个 DOM 子树完全替换调用元素。

insertAdjacentHTML()方法

语法:insertAdjacentHTML(插入位置,要插入的HTML文本)
插入位置必须是下列四个值之一:

“beforebegin”,在当前元素之前插入一个紧邻的同辈元素
“afterend”,在当前元素之后插入一个紧邻的同辈元素
“afterbegin”,给当前元素插入第一个子元素(不管当前元素是否有无子元素)
“beforeend”,给当前元素插入最后一个子元素(不管当前元素是否有无子元素)

用以上方法的一些问题

会造成内存泄漏,比如某个元素以及与事件处理函数绑定了,你假如删除或者替换了他,但它还在内存中
并且innerhtml之类的不能太多,因为它运行时要调用html解析器,耗内存的。

scrollIntoView

所有HTML元素均可调用该方法,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。
该方法参数的取值有两种可能:
true:窗口滚动之后会让调用元素的顶部与视口顶部尽可能平齐(不传入参数时也会出现这种效果)
false:调用元素会尽可能全部出现在视口中,调用元素的底部会与视口顶部平齐。

其他拓展功能

文档模式

document.documentMode

children属性

返回调用元素的元素子节点

contains()方法

检测某节点是不是另一个节点的后代

插入文本

1 innerText属性
得到所有文本值
2 outerText属性
替换指定文本的值

第十二章 DOM2和DOM3

这章没必要看
有些笔记转载
https://blog.csdn.net/crystal6918/article/details/52911787
https://www.jianshu.com/p/3814c40083bd?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
遍历
“DOM2 级遍历和范围”模块定义了两个用于辅助完成顺序遍历 DOM 结构的类型:NodeIterator 和 TreeWalker。这两个类型能够基于给定的起点对 DOM 结构执行深度优先(depth-first)的遍历操作。

第十三章 事件

事件流

事件流描述的是从页面中接受事件的顺序,ie的事件流是事件冒泡流,而netscape的事件流是事件捕获流

事件冒泡

事件冒泡即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。从具体那个div沿着dom树向上传播,每一个节点都会发生,直到传播到document对象。

事件冒泡

正好相反。他一般是从document到html到body到div

dom事件流

『DOM2级事件』规定的事件流流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。使"DOM2级事件"规范明确要求捕获阶段不会涉及事件目标,但现代浏览器都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。

事件处理程序

HTML事件处理程序

就是把事件加在html里面,比如这样

<input type="button" value = "Click me" οnclick="alert('I was clicked')" /input>

这个动态创建的函数,另一个有意思的地方是它扩展作用域的方式。在这个函数内部,可以像访问局部变量一样访问document及该元素本身的成员。如果当前元素是一个表单输入元素,则作用域中还会包含访问表单元素(父元素)的入口。

function(){
	with(document){
    	with(this.form){
        	with(this){
            	//元素属性值
            }
        }
    }
}

用这个方法可能存在一个问题,刚把html加载出来,js还没加载用户就点击了,这导致了无效。”时差问题“

DOM0

on开头的称位DOM0级事件处理程序,因为简单和跨浏览器现在还在使用

var btn = document.getElementById("app")l
btn.onclick = function(){
  console.log(this)
}

DOM0级事件处理程序中的this引用当前元素。以这种方式添加的事件处理程序会**在事件冒泡阶段被处理。**将事件处理程序设置为null可以删除通过DOM0级方法指定的事件处理程序。(此方法也可以删除HTML指定的事件处理程序)

DOM2事件处理程序

DOM中采用了addEventListener()和removeEventListener()来分配和移除事件处理函数。与IE不同的是这些方法有三个参数,第三个参数标识是用于冒泡阶段还是捕获阶段。用于捕获阶段为true,用于冒泡阶段则为false。移除时第三个参数要跟添加时保持一致

var fnClick=function(){
      alert("Clicked");
}

var fnClick2=function(){
      alert("Click2");
}

var oDiv=document.getElementById("div");
oDiv.addEventListener("onclick",fnClick,false);
oDiv.addEventListener("onclick",fnClick2,false)

oDiv.removeEventListener("onclick",fnClick,false);
oDiv.removeEventListener("onclick",fnClick2,false);

与DOM0级方法相同,DOM2级方法添加的事件处理程序也是在其依附的元素的作用域中运行。使用DOM2级方法的主要好处是可以添加多个事件处理程序,事件处理程序会按照添加它们的顺序触发。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除,移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventListener()添加的匿名函数将无法移除。

IE事件处理

IE在监听事件必须用他自己的方法:attachEvent(),detachEvent()他们都接受2个参数:事件类型,和事件处理函数。但是必须用具有特色的IE on名称 类型。
1.顺序和DOM不同的是相反,先添加后执行
2.代码中的this指向不同,IE指向window、DOM指向目标元素作用域

跨浏览器处理

var EventUtil = {
    addEvent: function (el, eventType, fn) {
      if (el.addEventListener) {
        el.addEventListener(eventType, fn, false);
      } else if (el.attachEvent) {
        el.attachEvent('on' + eventType, fn);
      } else {
        el['on' + eventType] = fn;
      }
    },
    removeEvent: function (el, eventType, fn) {
      if (el.removeEventListener) {
        el.removeEventListener(eventType, fn, false);
      } else if (el.detachEvent) {
        el.detachEvent('on' + eventType, fn);
      } else {
        el['on' + eventType] = null;
      }
    }
  }

事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着所有与事件有关的信息。只有在事件处理程序执行期间,event对象才会存在,一旦事件处理程序执行完成,event对象就会被销毁。

currentTarget:正在处理事件的那个元素
target:事件的目标
type:事件类型
cancelable:可以阻止特定事件的默认行为
preventDefault():阻止特定事件的默认行为
stopPropagation():停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡
eventPhase:事件出于事件流的阶段 捕获阶段为1 处于目标对象为2 冒泡阶段为3

阻止特定事件的默认行为,使用preventeDefault()方法

var link  = document.getElementById("myLink");
link.onclick = function(event){
	event.preventDefault();
}

阻止冒泡

 var btn = document.getElementById("myBtn");
        btn.onclick = function(event){
            alert("Clicked");
            event.stopPropagation();
        };
        //click事件根本不会传播到document.body,因为阻止冒泡
        document.body.onclick = function(event){
            alert("Body clicked");
        };

ie事件对象

IE了,阻止默认行为 returnValue = false,阻止捕获或者冒泡 cancelBubble。

跨浏览器事件对象

结合一般事件对象和ie进行封装

var EventUtil = {
    addEvent: function (el, eventType, fn) {//绑定事件
      if (el.addEventListener) {
        el.addEventListener(eventType, fn, false);
      } else if (el.attachEvent) {
        el.attachEvent('on' + eventType, fn);
      } else {
        el['on' + eventType] = fn;
      }
    },
    removeEvent: function (el, eventType, fn) {//移除事件
      if (el.removeEventListener) {
        el.removeEventListener(eventType, fn, false);
      } else if (el.detachEvent) {
        el.detachEvent('on' + eventType, fn);
      } else {
        el['on' + eventType] = null;
      }
    },
    getEvent: function (ev) {//获取事件对象
      return ev ? ev : window.ev;
    },
    getTarget: function (ev) {//获取元素
      return ev.target || ev.srcElement;
    },
    preventDefault: function (e) {//阻止默认行为
      if (ev.preventDefault) {
        ev.preventDefault();
      } else {
        ev.returnValue = false;
      }
    },
    stopPropagation: function (ev) {//阻止冒泡或捕获
      if (ev.stopPropagation) {
        ev.stopPropagation();
      } else {
        ev.cancelBubble = true;
      }
    }
  }

事件类型

UI事件:不一定与用户操作有关的事件

DOMActive 不建议使用
load
当页面完全加载后再window上面触发,当作有框架都加载完毕时在框架集上触发,当图像加载完毕时在元素上触发,或者当嵌入内容加载完毕时在元素上触发。
图片load事件应用(最重要的是要在指定src属性之前先指定事件。)

window.onload = function(){
	var image = new Image();
    image.onload = function(){
    	alert('Image loaded!');
    }
    image.src = 'smile.gif';
}

unload
事件被完全卸载后触发
abort
error
resize

//一般事件就是这样触发的
EventUtil.addHandler(windows, "resize", function(event){
	alert("Resized");
});

scroll

焦点事件

blur 失去焦点时 不会冒泡
DOMFocusIn 获得焦点 冒泡
DOMFocusOut 失去焦点
focus 获得焦点 不会冒泡
focusin
focusout

鼠标与滚轮事件

鼠标事件包含click、dblclick、mousedown、mouseout、mouseover、mouseup、mousemove。
click事件顺序: mousedown、mouseup、click
事件顺序:dblclick事件会先后触发以下事件:mousedown、mouseup、click、mousedown、mouseup、click、dblclick。
通过event.clientX 和event.clientY取得鼠标事件客户端坐标信息
还有pageX和pageY ,在页面不滚动情况下与上面client一样
screenX和screenY

键盘与文本事件

keydown
keypress
keyup
事件顺序:用户按一次某字符按键时,会先后触发以下事件:keydown、keypress、keyup。如果按一次某非字符按键时,会先后触发以下事件:keydown、keyup。
keypress按下之后,event里面又两个属性 charCode 和keyCode,前者是accii码,后者有规定

Dom3 新事件 textoutput

复合事件

变动事件

dom树中某一部分变化时触发不同事件
比如removeChild, appendChild

HTML5 事件

关闭页面时出发的是event.beforeunload
readystatechange事件 readystate属性 uninitialized loading loaded interactive complete

readystatechange 事件,支持readystatechange事件的每个属性 uninitialized loading loaded interactive complete

pageshow和pagehide事件
往返缓存(back-forward cache,或bfcache),用户使用浏览器的“后退”、“前进”按钮加快页面的转换速度。将整个页面保存在内存里。

html5新增了hashchange事件,在url发生变化时通知开发人员

EventUtil.addHandler(window, "hashchange", function(event){
	alert("Old URL:" + event.oldURL+"\nNew URL: " + event.newURL);
});

设备事件

触摸与手势事件

键盘 与文本事件

内存和性能

事件处理程序是函数。在JavaScript中,每个函数都是对象,都会占用内存。内存中的对象越多,性能就越差。(dom访问次数上去了,延迟了整个页面的交互事件)

事件委托

转自 https://segmentfault.com/a/1190000018826803
目的:解决“事件处理程序过多”问题。
实现:利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
使用事件委托,只需要在 DOM 树中尽量最高的层次上添加一个事件处理程序。
如下例子,由于所有列表项都是这个元素"myLinks"的子节点,而且它们的事件会冒泡,所以单击事件最终会被这个函数处理。 这样只操作了一次dom

<!DOCTYPE html>
<html>
<head>
    <title>Event Delegation Example</title>
    <script type="text/javascript" src="EventUtil.js"></script>
</head>
<body>
    <ul id="myLinks">
        <li id="goSomewhere">Go somewhere</li>
        <li id="doSomething">Do something</li>
        <li id="sayHi">Say hi</li>
    </ul>
    <script type="text/javascript">
    (function(){
        var list = document.getElementById("myLinks");
        
        EventUtil.addHandler(list, "click", function(event){
            event = EventUtil.getEvent(event);
            var target = EventUtil.getTarget(event);
        
            switch(target.id){
                case "doSomething":
                    document.title = "I changed the document's title";
                    break;
        
                case "goSomewhere":
                    location.href = "http://www.wrox.com";
                    break;
        
                case "sayHi":
                    alert("hi");
                    break;
            }
        });

    })();
    </script>
</body>
</html>

移除事件处理程序

有时候我们 操作dom直接removeChild()或者.innerHTML。 那么原来绑定在元素中的事件处理程序没法垃圾回收。
所以如下

var btn = document.getElementById("myBtn");
btn.onclick = function(){
	btn.onclick = null;
	document.getElementById("myDiv").innerHTML = "processing...";
};

卸载页面是也会造成”空事件处理程序“
所以有onload的地方,都要用onunload处理了。

模拟事件

模拟事件就是用js操作模拟鼠标或者键盘的行为
用createEvent来创建, dispatchEvent(event);来执行

对于鼠标 用event.initMouseEvent来初始化

var btn = document.getElementById('myBtn');
//创建事件对象
var event = document.createEvent('MouseEvents');
//初始化对象
event.initMouseEvent('click',true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);
//触发事件
btn.dispatchEvent(event);

ie模拟事件
document.createEventObject();
btn.fireEvent()

表单脚本

在HTML中,表单是由 元素来表示的,而在 JavaScript 中,表单对应的则是 HTMLFormElement 类型。

提交表单

创建提交按钮

<!-- 通用提交按钮 -->
<input type="submit" value="Submit Form">
<!-- 自定义提交按钮 -->
<button type="submit">Submit Form</button>
<!-- 图像按钮 -->
<input type="image" src="graphic.gif" >

以这种方式提交表单时,浏览器会在将请求发送给服务器之前触发 submit 事件。

重置表单

<form action="https://github.com/">
  <input type="text" value="用户名" name="username">
  <input type="password" value="pwd" name="password">
  <!-- 以下两种方式都可以定义重置按钮 -->
   <input type="reset" value="重置表单">  
  <!-- <button>重置表单</button>   -->
</form>

表单字段

var field1 = form.element[0];//取得表单第一个字段
var field2 = form.element["textbox1"];//取得名为”textbox1“的字段
var fieldCount = form.elements.length;//取得表单中包含的字段的数量

共有表单字段方法

focus() 方法:用于将浏览器的焦点设置到表单字段,即激活表单字段,使其可以响应键盘事件。

EventUtil.addHandler(window, "load", function(event){
	document.forms[0].elements[0].focus();
}

blur() 方法:作用是从元素中移走焦点。
注意:HTML5为表单字段新增了一个autofocus属性,不用JavaScript就能自动把焦点移动到相应字段。

<input type = "text" autofocus>

blur:当前子弹失去焦点时触发
change:对于input和textarea元素来说,在他们是去焦点并且值改变时触发,对于select元素,在其选项改变时触发,
focus:当前字段获得焦点时触发

文本框脚本

对于input元素来说可以通过size特性来设置能够显示的字符数,通过value特性,可以设置初始值。,而maxlength则可以指定能够接受的最大字符数。如果要创建一个文本框,让他能够显示25个字符,单输入不能超过50个字符。可以用如下代码。

<input type="text" size="25" maxlength="50" value="initial value">

但是对于而言,元素始终会呈现为一个多行文本,要指定文本框的大小可以通过rows和cols,rows表示行数,cols表示列数。与元素的区别在于其初始值需要放在initial value之间。并且不能指定最大字符数。

选择脚本

var testbox = document.forms[0].elements["textbox1"];
textbox.select();

取得选择的文本
HTML5为取得选择的文本,添加了两个属性:selectionStart和selectionEnd

function getSelectedText(textbox){
return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
}

HTML5提供了选择部分文本的方法setSelectionRange():接收两个参数:要选择的第一个字符的索引和要选择的最后一个字符之后的字符的索引。

过滤输出

如果只想屏蔽特定的字符,则需要检测 keypress 事件对应的字符编码,然后再决定如何响应。


EventUtil.addHandler(textbox, "keypress", function(event){
event = EventUtil.getTarget(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
if (!/\d/.test(String.fromCharCode(charCode))) {
	EventUtil.preventDefault(event);
}
})

操作剪贴板

下列就是6个剪贴板事件:

beforecopy:在发生复制操作前触发;
copy:在发生复制操作时触发;
beforecut:在发生剪切操作前触发;
cut:在发生剪切操作时触发;
beforepaste:在发生粘贴操作前触发;
paste:在发生粘贴操作时触发;

访问剪贴板中的数据
clipboardData

自动切换焦点

使用js可以从多个方面增强表单的易用性,其中,最常见的一种方式就是在用户填写完当前字段的时候,自动将焦点切换到下一个字段。通常在自动切换焦点之前,必须知道用户已经输入了既定的长度的数据(比如电话号码)。

html5新增表单功能

必填字段:required属性;

<input type = "text" name = "username" requied>

其他输入类型:email和url等;

<input type = "email" name = "email" >

数值范围:number、range等;
输入模式:pattern属性;

<input type = "text" pattern  = "\d+" name = "count">

检测有效性:checkValidity()方法,validity属性则会告诉你为什么字段有效或无效;
禁用验证:novalidate属性;
通过设置novalidate(在y=元素上使用),可以告诉表单不进行验证;
如果一个表单有多个提交按钮,对其中的一个按钮添加formnovalidate属性,可以使相应的提交按钮不必验证表单

选择框脚本

选择框是通过 和 元素创建的。

var selectbox = document.forms[0].elements["location"];
var text = selectbox.options[0].text;  //选项的文本
var value = selectbox.options[0].value; //选项的值

选择选项

对于只允许选择一项的选择框,访问选中项的最简单方式,就是使用选择框的 selectedIndex 属性。

var selectedOption  = selectbox.options[selectbox.selectedIndex];

另一种选择选项的方式,就是取得对某一项的引用,然后将其 selected 属性设置为 true 。

添加选项

采用DOM方法;
使用 Option 构造函数来创建新选项,它接受两个参数:文本(text) 和值 (value)(可选);
使用选择框的 add() 方法,接受两个参数:要添加的新选项和将位于新选项之后的选项。

var newOption = new Option("Option text", "Option value");
selectbox.add(newOption, undefined);             // 最佳方案

移除选项

使用DOM的removeChild() 方法,为其传入要移除的选项;

selectbox.removeChild(selectbox.options[0]);

使用选择框的 remove() 方法,接受一个参数,即要移除选项的索引;

selectbox.remove(0);

将相应选项设置为 null;

selectbox.options[0] = nul;

移动和重排选项

使用 DOM 的 appendChild() 方法,可以将第一个选择框中的选项直接移动到第二个选择框中。
要将选择框中的某一项移动到特定位置,最合适的 DOM 方法就是 insertBefore()。

表单序列化

ajax出现后, 你要把表单数据给服务器得先把他序列化了吧,比如不发送submit得按钮, 只发送选中得select
1 对表单字段的名称和值进行URL(encodeURIComponent())编码,并使用(&)进行分割
2 不发送禁用的表单字段
3 只发送勾选的复选框和单选按钮
4 不发送type为reset和button的按钮
5 多选框中每个选中的值单独一个条目
6 在单击提交按钮提交表单的情况下,也会发送提交按钮;否则不会发送,也包括type为image的input元素
7 元素的值就是选中的 元素的value值,如果元素没有value�特性则 的文本值

富文本编辑

使用创建富文本编辑区域

设置其designMode属性,可以使这个空白的HTML页面可以被编辑

使用contenteditable属性

把contenteditable属性应用给页面中的任何元素,用户立即就可以编辑该元素。

操作富文本

document.execCommand()方法:对文档执行预定义的命令,接收3个参数:要执行的命令名称、表示浏览器是否应该为当前命令提供用户界面的一个布尔值(通常设为false)和执行命令必须的一个值(如果不需要值,则传递null)。

与命令相关的方法:

queryCommandEnabled()方法:用来检测是否针对当前选择的文本,或者当前插入字符所在位置执行某个命令,接收一个参数即要检测的命令。
queryCommandState()方法:用于确定是否已经将指定命令应用到了选择的文本。
queryCommandValue()方法:用于取得执行命令时传入的值。

富文本选区

在富文本编辑器中,使用框架的(iframe)的getSelection()方法,可以确定实际选择的文本,返回一个表示当前选择文本的Selection对象。

表单与富文本

由于富文本编辑器是使用iframe而非表单控件实现的,因此在将其内容提交给服务器之前,必须将iframe或contenteditable元素中的HTML复制到一个表单字段中。

第16章 HTML5脚本编程

跨文档消息传递

www.wrox.com域中的页面与位于一个内嵌框架中的p2p.wrox.com域中的页面通信。
目的: 向另一个地方传递数据,对于XDM而言,“另一个地方”指的是包含在当前页面中的或者由当前页面弹出的窗口
postMessage方法接收两个参数,一条消息和一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全通信非常重要,可以防止浏览器把消息发送到不安全的地方。来看下面的例子

let iframeWindow = document.getElementById('myframe').contentWindow
iframeWindow.postMessage('A secret', 'http://www.wrox.com')

原生拖放

1,按住鼠标不动拖放一些图片和选中的文字。
依次触发
dragstart 触发这个之后,随即触发drag事件
drag
dragend

2,当某个元素被拖动到一个有效的放置目标上,依次发生
dragenter
dragover
dragleave/drop

3,dataTransfer对象是拖放事件的属性,所以只能在拖放事件处理程序中访问它
dataTransfer对象有两个主要方法(在drop事件处理程序中):getData()和setData()
//设置和接受文本数据

event.dataTransfer.setData("text","some text");
var text=event.dataTransfer.gatData("Text");
//设置和接受URL
event.dataTransfer.setData("URL","http://www.wrox.com/");
var text=event.dataTransfer.gatData("url")||event.dataTransfer.gatData("text/uri-list");

4, dataTransfer对象不光可以传输数据,还能通过它来确定被拖动的元素以及作为放置目标的元素能够接受什么操作
dropEffect属性(在ondragenter事件处理程序中设置)可以知道被拖动的元素能够执行哪种放置行为:
“none”–不能把拖动的元素放在这里(这是除文本框之外所有元素的默认值)
“move”–应该把拖动的元素移动到放置目标
“copy”–应该把拖动的元素复制到放置目标
“link”–表示放置目标会打开拖动的元素(拖动的元素必须是一个链接,有URL)

5,
draggable属性表示元素是否可以拖动(默认情况下,图像、链接和文本是可以拖动的)

<img src="smile.gif" draggable="false" alt="Smiley face"> //使图像不可拖动
<div draggable="true"></div> //使这个元素可以拖动

6,媒体元素
<audio> and <video>

7,历史状态管理
解决用户难在不同状态之间切换的问题,这种切换不仅仅是整个页面,而是不同页面状态的切换用栈的思想
当hashchange事件发生的时候,使用history.pushState()方法;
history.pushState(状态对象,新状态的标题,可选的相对URL) //新的状态信息加入到历史状态栈,浏览器地址栏也会变成新的相对URL;
点击“后退”按钮,会触发window对象的popstate事件,对应的事件对象有一个state属性,包含着当初以第一个参数传递给pushState()的状态对象
更新当前状态,可以调用replaceState(),传入的参数与pushState()的前两个参数相同,会重写当前状态:
history.replaceState({name:“Greg”},“Greg’s page”);

第二十章 JSON

利用了javascript中的一下模式来结构化数据,但并不是只有js才能使用json.
JSON可以用来表示 简单值,对象和数组。
Json中的属性名必须加双引号 “name”:“Nicholas”

解析和序列化很简单

直接用books[2].title

解析早期用eval();

全局对象 JSON ,有两个方法:stringify() 和 parse ()。
stringify 用于将 JavaScript 值类型(除undefined)或引用类型(对象和数组)转换为保存了 JSON 格式的 JavaScript 字符串。
JSON格式就是一行字符串但是是json的框架,但是不包含任何字符或者缩进。
parse 用于将保存了 JSON 格式的 JavaScript 字符串转换为 JavaScript 中的值类型或引用类型(对象和数组)。

Json.stringify()

JSON.stringify()方法有三个参数:
第一个参数是要序列化的JavaScript对象;
第二个参数是过滤器,可以是一个数组或者函数;
第三个参数是一个布尔值,表示是否在JSON字符串中保留缩进.

如果缩进参数传入的是数值,表示每个json字段缩进的空格数,但是最大缩进空格数不超过10.

var jsonText=JSON.stringify(book,null,4)

如果缩进参数传入的是字符串,则表示JSON字符串中每个级别都使用该字符串作为缩进字符.不过该字符串也不能超过10个字符长.

var jsonText=JSON.stringify(book,null,"--")

tojson()

toJSON()方法 就是按照自己格式自定义输出的function
如果JSON.stringify()不能满足某些对象的序列化需求,可以给对象自定义toJSON方法,返回其自身的JSON数据格式。
综合以上几种情况,JSON.stringify()序列化对象的顺序如下:

①如果对象存在toJSON方法且能返回有效值,则调用该方法;否则,仍然按照默认顺序执行序列化。
②如果stringify()存在第二个参数,应用这个过滤器;
③对第②步返回的每个值进行序列化;
④如果存在第三个参数,执行相应的格式化。

parse()

JSON.parse()用来将JSON字符串解析成JavaScript对象。

该方法第一个参数要解析的JSON字符串;

第二个参数是一个函数还原函数。还原函数有两个参数key和value。如果还原函数返回undefined,则表示将该属性从结果中删除;如果返回其它值,则将该值插入到结果当中。在将日期字符串转换为Date对象时,经常用到还原函数.

有些比如date格式的东西, 不好直接parse,要弄个function来parse

第21章 Ajax与Comet

转自 http://jiaochunxiao.github.io/2016/12/04/JavaScript高级程序设计笔记-15/
Ajax(Asynchronous Javascript + XML)技术的核心是XMLHttpRequest对象,即: XHR。虽然名字中包含XML,但它所指的仅仅是这种无须刷新页面即可从服务器端获取数据的技术,其通信与数据格式无关,并不一定是XML数据。

XMLHttpRequest对象
IE7+、Firefox、Opera、Chrome 和 Safari 都支持原生的XHR对象。我们可以直接使用XMLHttpRequest构造函数来创建XHR对象。

var xhr = new XMLHttpRequest();

虽然,IE7之前版本的浏览器中,创建xhr的方法与此有所不同,但是,前端技术发展到今天,已经很少有业务需求是要支持IE7之前的版本了。因此,这里我略过这一情况。
XHR的用法
使用 XHR对象的时候,要调用的第一个方法是open(),它接受3个参数:
要发送请求的类型,如: get/post
请求的url
是否异步发送请求,这个参数是一个布尔值

xhr.open('get', 'example.php', false)

注意:open()方法的调用并不会真正发送请求,仅仅是启动一个请求以备发送!
另外,只能向同一个域中使用相同端口和协议的URL发送请求,否则,会出现错误。
在执行open()方法之后,必须再调用send()方法,才会真正发起ajax请求。

xhr.open('get', 'example.txt', false);
xhr.send(null);

send()方法接收一个参数,即:要作为请求主体发送的数据。如果不需要发送数据,那么必须传入null,因为该参数对于部分浏览器而言是必需的。
本例中的请求是同步的,Javascript代码会等到服务器响应之后再执行。 收到响应后,响应的数据会自动填充XHR对象的属性,相关的属性有:
responseText: 作为响应主体被返回的文本。
responseXML: 如果响应的内容类型是"text/xml"或者"application/xml",那么这个属性中将保存着包含响应数据的XML DOM文档。
status: 响应的HTTP状态
statusText: HTTP状态的说明
无论内容类型是什么,响应主体的内容都会保存到responseText属性中,而对于非XML数据而言,responseXML 属性的值将会是null。

收到响应后,一般来说,会先判断 status 是否为200,这是此次请求成功的标志。此时,responseText属性的内容已经就绪,而且在内容类型正确的情况下,responseXML也能够访问了。 另外,状态码status如果是304,那么表示请求的资源没有被修改,可以直接使用浏览器中的缓存,当然,这样的响应也是有效的。

if( (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 ){
	alert(xhr.responseText);
}
else{
	alert('fail! status:' + xhr.status);
}

有的浏览器会错误地报告 204 的状态代码。IE中 XHR 的ActiveX版本会将204设置为1223,而IE中原生的 XHR 则会将 204 规范化为 200。Opera会在取得204时报告 status的值为0。

检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段。属性可取值如下:

0:未初始化,尚未调用open()方法
1:启动,已经调用open()方法,但尚未调用send()方法
2:发送,已经调用send()方法,但尚未接到响应
3:接收,已经接收到部分数据
4:完成,已经接收到全部响应数据,而且已经可以在客户端使用了。
只要readyState属性的值由一个值变成另一个值,都会触发一次readystatechange事件。必须在调用open()方法之前指定onreadystatechange事件处理程序才能确保跨浏览器兼容性。

在接收到响应之前可以调用abort()方法来取消异步请求。

通过setRequestHeader()方法可以设置自定义的头部信息。

xhr.setRequestHeader("MyHeader". "Myvalue");

Get请求

xhr.open("get", "example.php?name1 = value1&name2=value2",true);

Post请求

xhr.open("post", "example",true);

可以使用XHR来模仿表单提交:将Content-Type头部信息设置为application/x-www-form-urlencoded,也就是表单提交时的内容类型,其次是以适当的格式创建一个字符串(第14章介绍的serialize()函数)。
FormData类型用于便捷地将表单数据序列化。

超时设定用timeout属性

progress事件常用于创建进度指示器。

Cors

CORS(跨域资源共享)定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是应该失败。

IE对CORS实现-XDR(XDomainRequest)
cookie不会随请求发送,也不会随响应返回
只能设置请求头部信息中的Content-Type字段
不能访问响应头部信息
只支持 GET和POST请求
所有的XDR请求都是异步的,不能用来创建同步请求。

其他浏览器原生就支持CORS

其他跨域技术

图片Ping

var img = new Image();
img.onload = img.onerror = function(){
	alert("Done!");
}
img.src='http://www.example.com/test?name="test"';

缺点:
只能发送get 请求
无法访问服务的响应文本

JSONP(JSON with padding,填充式JSON或参数式JSON)

function handleResponse(response){
	alert("You're at IP address " + response.ip);
}
var script = document.createElement('script');
script.src='http://freegeoip.net/json/?callback=handleResponse';
document.body.insertBefore(script, document.body,firstChild);

优点:能够直接访问响应文本,支持在浏览器与服务器之间双向通信。
缺点:
JSONP是从其他域中加载代码执行,如果其他域不安全,很可能会在响应中夹带一些恶意代码
要确定JSONP请求是否失败并不容易

Comet

Ajax是一种从页面向服务器请求数据的技术,而Comet则是一种服务器向页面推送数据的技术。两种实现Comet的方法:长轮询和流。
短轮询:浏览器定时向着服务器发送请求,看有没有更新的数据。
长轮询:页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送,发送完数据之后,浏览器关闭连接,随机又发起一个到服务器的新请求。

流: 它在整个http生命周期只有一个http连接,具体来说就是浏览器向服务器发送一个请求,服务器一直保持打开,然后周期性地向浏览器发送数据。

服务器发送事件

SSE

Web Sockets

是一种与服务器进行全双工,双向通信的信道。
不使用http,是一种自定义的协议。

要创建Web Socket,先实例一个WebSocket对象并传入要连接的URL:

var socket = new WebSocket("ws://www.example.com/server.php");

注意,必须给WebSocket构造函数传入绝对URL。同源策略对Web Socket不实用,因此可以通过它打开任何站点的连接。至于是否会与某个域中的页面通信,则完全取决于服务器。

第22章

高级函数

一般工作闭包实现

惰性载入函数

每次做检测都有大量if else很消耗事件,我们先把结果返回进 一个function(之后会覆盖)就好了。

函数绑定

可以在特定的this环境中以指定参数调用另一个函数
bind()函数接受一个函数和环境

函数柯里化

他就是方便在任何情况下用一个参数或者多个参数做function
比如 curry(add,3)curriedAdd(3) //8

原理:使用闭包
可以一个个参数给进去。

高级定时器

JavaScript 是运行在单线程环境中的。
setTimeout() 和setInterval() 其实不是马上执行,而是把任务放进一个队列里。
当使用setInterval(),仅当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中。

函数节流

背景:浏览器中某些计算和处理比其他的昂贵很多,例如DOM操作,消耗更多的内存和CPU时间
基本思想:某些代码不可以在没有间断的情况连续重复执行
实现:定时器
应用:只要代码是周期性执行,都应该使用节流,但是你不能控制请求执行的频率。
中心思想就比如resize每一瞬间都要调用一些事件,你现在用定时器,让它控制在每100ms


function throttle(method, scope) {
            clearTimeout(method.tId);//清除之前的id,重新开始
            method.tId= setTimeout(function(){
                method.call(scope);  //没有传入scope时,那么就在全局作用域内执行该方法
            }, 100);
        }

自定义事件

应用观察者模式,使用自定义事件有助于将不同部分代码相互之间解耦,让维护更加容易,减少引入错误的机会。

拖放

第23章 离线应用与客户端存储

离线检测

使用navigator.online属性判断是否离线与否

应用缓存

HTML5应用缓存,简称appcache

Cookie

在客户端存储会话信息的,该标准要求服务器对任意http请求发送set-cookie http头作为响应的一部分。
cookie浏览器中一般4mb,键值对,有失效时间。所有名字和值都通过URL编码,必须使用decodeURIComponent()来解码。

在javascript中通过document.cookie可以访问cookie。

子cookie

就是把很多cookie放在一条cookie里面,节省空间。
获取子cookie方法两个,get()和getall();
尽可能在cookie中少存储信息,避免影响性能。

IE用户数据

userdata

web存储机制

当数据需要被严格控制在客户端上,无需持续地将数据发回给服务器。就需要sessionStorage和globalStorage出马了。他们是存储大量可以跨会话存在的数据的机制。

sessionstorage对象

sessionStorage对象存储特定于某个会话的数据。也就是该数据只保存到浏览器关闭。
主要应用于仅针对会话的小段数据的存储,跨越会话用globalstorage更好。

globalStorage

跨越会话存储数据,有特定的访问限制。
要使用的话,首先要指定哪些域可以访问该数据。

localStorage

不能给它指定任何访问规则,因为已经实现设定好了。 要访问同一个localStorage对象, 页面必须来自同一个域名,使用同一种协议,在同一个端口上。
一般大小给5mb

IndexedDB

是在浏览器中保存结构化数据的一种数据库,indexedDB设计的操作完全是异步进行的。
最大特色是使用对象保存数据,而不是使用表来保存数据。
首先要indexDB.open()打开、

猜你喜欢

转载自blog.csdn.net/weixin_39285712/article/details/89792675