js高级程序设计-读书小记

215 阅读42分钟

参考: 《js高级程序设计》

第三章 基本概念

Number类型

isFinite()

该函数在参数位于最小(Number.MIN_VALUE)与最大数值(Number.MAX_VALUE)之间时返回true。

NAN

任何涉及与NaN的操作都会返回NaN;其次NaN与任何值都不相等。

用于表示本来要返回数值的操作 失败了(而不是抛出错误)

将数值格式化为字符串

toFixed()会按照指定的小数位返回数值的字符串表示;

toExponential()该方法返回以指数表示法表示的数值的字符串形式;

toPrecision();

ES6 新增了 Number.isInteger()方法,用于辨别一个数值是否保存为整数。

安全整数:

为了鉴别整数是否在这个范围内,可以使用 Number.isSafeInteger()方法

console.log(Number.isSafeInteger(-1 * (2 ** 53))); // false
console.log(Number.isSafeInteger(-1 * (2 ** 53) + 1)); // true
console.log(Number.isSafeInteger(2 ** 53)); // false
console.log(Number.isSafeInteger((2 ** 53) - 1)); // true 

数值转换

Number()(可用于任何数值类型)、parseInt(str,指定的基数)、parseFloat()(只用于字符串)

Objec类型

Object的每个实例都具有下列属性和方法:

constructor:保存着用于创建当前对象的函数。

hasOwnProperty():用于检查给定的属性在当前对象实例中是否存在。

isPrototypeOf():用于检查传入的对象是否是当前对象的原型。

propertyIsEnumberable():用于检查给定的属性是否能够使用for-in枚举。

toLocaleString()

toString()

valueOf()

类型检测

typeof 返回值

number、string、booolean、undefined、object、function、symbol

基本类型值, 按值访问,可以操作保存在变量中实际的值。包括:number、string、booolean、undefined、null

引用类型值, 是保存在内存中的对象,不能直接操作对象的内存空间,只能操作对象的引用。包括:Object、Function、Array

instanceOf

如何想知道某个值是什么类型的对象,可以使用instanceOf操作符。如:

alert(person instanceOf Object);

alert(color instanceOf Array);


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

执行环境及作用域

执行环境定义了变量或函数有权访问的其它数据,每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

每个函数都有自己的执行环境

当代码在一个环境中执行时,会创建变量对象的一个作用域。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。

垃圾收集

垃圾回收机制的原理:找出那些不再使用的变量,然后释放其占用的内存。

标记清除

当变量进入环境,就将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。

垃圾回收机制在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数为1。如果同一个值又被赋给另外一个值,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当该值的引用数次为0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。

管理内存

 确保占用最少的内存可以让页面获取更好的性能。而优化内存占用的最佳方式,就是为执行中的内码只保存必要的数据。一旦数据不再有用,最好通过将其值设计为null,来释放其引用---这个方法叫做解除引用。

解除一个值的引用并不意味着自动回收该值占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾回收器下次运行时将其回收。


第五章 引用类型

Array

检测数组

对于一个网页或者一个全局作用域而言,使用instanceOf() 即可;如果网页中包含多个框架,就会存在两个及以上不同的全局执行环境,现在就可以使用Array.isArray() 。该函数的目的是最终确定某个值是不是数组,而不管它是在哪个全局环境作用下创建的。

迭代器方法(es6)

keys()返回数组索引的迭代器

values()返回数组元素的迭代器

entries()返回 索引/值对的迭代器

复制和填充方法

fill():可以向一个已有的数组中插入全部或部分相同的值

copyWithin():会按照指定范围浅复制数组中的部分内容,然后将它们插入到指 定索引开始的位置

转换方法

toLocaleString()(为了创建最后生成的字符串会调用数组每一项的toLocaleString())

toString()(为了创建最后生成的字符串会调用数组每一项的toString())

valueOf()、

join():接收一个用作分隔符的字符串,返回包含所有数组项的字符串

栈方法

push()、pop()。会改变原数组

队列方法

push()、shift()。会改变原数组

重排序方法

sort()、reverse()。会改变原数组

操作方法

concat():将参数添加到原数组中,返回新数组;参数可以是用,分割的值,也可以是另一个数组

打平数组参数的行为可以重写,方法是在参数数组上指定一个特殊的符号:Symbol.isConcatSpreadable。这个符号能够阻止 concat()打平参数数组。相反,把这个值设置为 true 可以强制打平 类数组对象:

slice(start,end):返回的数组包含从起始位置到结束位置的所有元素(不包含结束位置)。参数也可以是负值;如果结束位置小于起始位置,则返回空数组。

splice(开始删除位置[,删除数目,插入元素1,插入元素2...]):插入、删除和替换数组元素,返回被删除的元素。会改变原数组。

搜索和位置方法

ECMAScript 提供两类搜索数组的方法:按严格相等搜索和按断言函数搜索:

  • 严格相等

indexOf(查找的项,[起点位置的索引])

lastIndexOf()

includes()(es7)

  • 断言函数

find()

findIndex()

迭代方法

返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

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

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

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

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

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

归并方法

reduce(在每一项调用的函数,[作为归并基础的初始值])

reduceRight()

转换为数组(es6)

Array.from()用于将 类数组结构转换为数组实例

Array.of()用于将一组参数转换为数组实例

函数声明与函数表达式

函数是Function()类型的实例,所以函数也是对象。

解析器在向执行环境中加载数据时,会率先读取函数声明,并使其在执行任何代码之前可用;至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。

函数内部属性

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

arguments是一个类数组对象,包含着传入函数中的所有参数。该对象有一个callee的属性,是一个指针,指向拥有这个arguments对象的函数。

this引用的是函数执行的环境对象。(哪个对象调用函数,函数中的this就指向谁)

es5也规范化了另一个函数对象的属性:caller。这个属性指向调用当前函数的函数的引用。例:

function outer(){
	inner();
}

function inner(){
	alert(inner.caller);
}

outer();//显示outer函数的源代码

函数的属性和方法

length、prototype;

call()、apply():修改函数体内this的指向(改变函数的作用域)。使用call和apply扩展函数作用域的好处就是对象不需要与方法有任何的耦合关系。

bind(this,......):该方法会创建一个函数的实例,this将会绑定到传入bind函数的第一个值。

String

  • charAt():返回字符串中指定位置的字符

charCodeAt():返回字符串中指定位置的Unicode编码

String.fromCharCode():可接受一个指定的 Unicode 值,然后返回一个字符串    

  • concat():连接字符串

slice(起始位置,[结束位置之前]):如果参数为负值,则与长度相加

substring(起始位置,[结束位置之前]):如果参数为负值,所有参数都会转化为0

substr(起始位置,[截取的长度]):如果参数为负值,则把第一个参数与长度相加,第二个参数转化为0

  • indexOf()

lastIndexOf()

  • 字符串包含方法(es6新增)

startsWith(subStr,[num表示开始搜索的位置])检查开始于索引 0 的匹配项

endsWith(subStr,[num字符串末尾的位置]) 检查开始于索 引(string.length - substring.length)的匹配项

includes(subStr,[num表示开始搜索的位置])检查整个字符串

  • repeat()
  • padStart()和 padEnd()方法
  • trim()

trimLeft()、trimRight()

  • toLowerCase()、toLocaleLowerCase()

toUpperCase()、toLocaleUpperCase()

  • match():返回一个数组

search():返回字符串中第一个匹配项的索引

replace()

split():基于指定的分隔符将字符串分隔并存入到数组中

  • localeCompare():返回1、-1、0

Global

encodeURI、decodeURI() 用于对URI 进行编码/解码,不会处理属于 URL 组件的特殊字符,比如冒号、斜杠、问号、 井号;

encodeURIComponent()   decodeURIComponent() 用于对查询字符串编码/解码它发现的所有非标准字符

Math

min、max

ceil():向上取整

floor():向下取整

round():四舍五入

random():随机函数,


第六章 面向对象

属性类型

ES中有两种属性类型,数据属性和访问器属性。

数据属性

数据属性包含一个数据值的位置,在这个位置上可以读取和写入值。

[[configurable]]:表示能否通过delete删除属性

[[Enumerable]]:能否通过for-in循环返回属性

[[Writable]]:表示能否修改属性的值

[[Value]]:包含这个属性的值

访问器属性

[[configurable]]:表示能否通过delete删除属性

[[Enumerable]]:能否通过for-in循环返回属性

[[Get]]:读取属性时调用

[[Set]]:定入属性时调用

修改属性的默认特性,可以使用Object.defineProperty(对象,属性名称,特性);

一次定义多个属性,使用Object.defineProperties(对象,属性);

读取属性的特性,使用Object.getOwnPropertyDescriptor(对象,属性名称)

es8新增:Object.getOwnPropertyDescriptors() 静态方法。这个方法实际上 会在每个自有属性上调用Object.getOwnPropertyDescriptor()并在一个新对象中返回它们。

合并对象

Object.assign(): // 浅复制意味着只会复制对象的引用

对象标识及相等判定

Object.is()

创建对象

工厂模式

function Student(name,age,sex){
			var obj=new Object();
			obj.name=name;
			obj.age=age;
			obj.sex=sex;
			obj.study=function(){
				alert("好好学习");
			}
			return obj;//特别重要
		}
		var tom=Student("tom",20,"男");
		var lida=Student("linda",19,"女");

构造函数模式

function Student(name,age,sex){
			this.name=name;
			this.age=age;
			this.sex=sex;
			this.study=function(){
				alert("好好学习,天天向上");
			},
			this.showInfo=function(){
				alert("显示个人信息:"+this.name+";"+this.age+";"+this.sex);
			}
		}
		var tom=new Student("tom",20,"男");
		tom.study();
		tom.showInfo();
		var linda=new Student("linda",19,"女");
		linda.study();
		linda.showInfo();

原型模式

每个函数都有一个prototype属性,指向一个对象。这个原型对象的用途是包含可以由特定类型的所有实例共享属性和方法。

每当创建一个函数,就会根据特定的规则为函数创建一个prototype属性,这个属性指向函数的原型对象;所有的原型对象会自动获得一个constructor属性,这个属性指向prototype属性所在函数的指针;当用构造函数创建一个实例之后,该实例的内部包含一个指针(proto),指向构造函数的原型对象。

isPrototypeOf():可以检测一个对象是否在另一个对象的原型链上;

Object.getPrototypeOf():可以获取一个对象的原型;

hasOwnProperty():可以检测属性是存在实例中还是原型中,当属性存在于实例中返回true,否则返回false;

in:无论属性是存在于实例中还是原型中始终返回true;

Object.key():获取对象上所有可枚举的实例属性

Object.getOwnPropertyNames():获取对象上所有的实例属性,无论是否可枚举。

Object.getOwnPropertySymbols():与 Object.getOwnPropertyNames()类似,只是针对符号而已(es6

propertyIsEnumerable(..) 会检查给定的属性名是否直接存在于对象中(而不是在原型链 上)并且满足 enumerable:true。

混合模式

构造函数模式定义实例属性,而原型模式定义方法和共享的属性。

		function Student(name,sex,age){
			this.name=name;
			this.sex=sex;
			this.aga=age;
		}
		Student.prototype.study=function(){
			alert("好好学习,天天向上");
		}
		Student.prototype.showInfo=function(){
			alert("显示个人信息:"+this.name+";"+this.sex+";"+this.age);
		}
		var tom= new Student("tom","男",19);
		tom.study();
		tom.showInfo();
		var linda= new Student("linda","女",20);
		linda.study();
		linda.showInfo();
		alert(tom.study==linda.study);

动态原型模式

function Person(name,sex,age){
			//属性
			this.name=name;
			this.age=age;
			this.sex=sex;
			//方法:
			if(typeof this.sayName != "function"){
				Person.prototype.sayName=function(){
					alert(this.name);
				}
			}
		}

		var friend = new Person("tom","男",20);
		friend.sayName();

寄生构造函数模式

说明:寄生构造函数返回的对象与构造函数或者与构造函数的原型属性之前没有关系;也就是说,构造函数返回的对象在与构造函数外部创建的对象没有什么不同。不能依赖instanceOf操作符来确定对象类型。由于存在上述问题,不要使用这种模式。

	function Person(name,sex,age){
		 	var o = new Object();
		 	o.name=name;
		 	o.sex=sex;
		 	o.age=age;
		 	o.sayName=function(){
		 		alert(this.name);
		 	}
		 	return o;
		 }
		 var friend = new Person("tom","男",20);
		 friend.sayName();

//例:
//这个模式可以在特殊的情况下用来为对象创建构造函数。
//如果我们想创建一个具有额外方法的特殊数组。
//由于不能直接修改Array构造函数,因此可以使用下面的方法。无特殊情况下,不使用这种模式。
		function SpecialArray(){
			var values = new Array();
			//添加值 
			values.push.apply(values,arguments);
			//添加方法
			values.toPipedString = function(){
				return this.join("|");
			}
			return values;
		}
		var colors= new SpecialArray("red","yellow","blue");
		alert(colors.toPipedString());

稳妥构造函数模式

与寄生构造函数模式类似,有两点不同:新创建对象的实例方法不引用this;不使用new来调用构造函数。只适合在安全的执行环境下使用。

function Person(name,sex,age){
			var o = new Object();
			o.name=name;
			o.sex=sex;
			o.age=age;
			o.sayName=function(){
				alert(name);
			}
			return o;
		}
		var friend = Person("tom","男",20);
		friend.sayName();

继承

原型链

juejin.cn/post/723542…

构造函数、原型与实例之间的关系:每个构造函数都有一个原型对象,每个原型对象都包含着指向构造函数的指针,而每个实例包含着指向原型对象的指针。让原型对象等于另一个类型的实例,此时原型对象会包含着指向另一个原型的指针,而另一个原型也包含着指向另一个构造函数的指针。这样的关系层层递进,构成实例与原型的链条,称为“原型链”。

function SuperType(){
			this.property=true;
		}
		SuperType.prototype.getSuperValue=function(){
			return this.property;
		}

		function subType(){
			this.subProperty=false;
		}

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

		subType.prototype.getSubValue = function(){
			return this.subProperty;
		}

		var instance = new subType();
		console.log(instance.getSuperValue());
		console.log(instance.getSubValue());

存在的问题:

    • 原型中包含的引用值会在所有实例间共享
    • 子类型在实例化时不能给父类型的构造函数传参

借用构造函数

这种技术的基本思想,即在子类型构造函数中调用超类型构造函数。

函数是在特定环境中执行代码的对象,所以通过调用call、apply可以在新创建的对象上执行构造函数。

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


		var instance2 = new SubType();
		instance2.colors.push("white");
		console.log(instance2.colors);
    • 相比于使用原型链,盗用构造函数的一个优点就是可以在子类构造函数中向父类构造函数传参。
    • 必须在构造函数中定义方法,因此函数不能重用。此外,子类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模 式。由于存在这些问题,盗用构造函数基本上也不能单独使用。

组合继承

使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。这样既可以把方 法定义在原型上以实现重用,又可以让每个实例都有自己的属性。

	  function SuperType(name){
			this.name=name;
			this.colors=['red','green','blue'];
		}
		SuperType.prototype.sayName = function(){
			console.log(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(){
			console.log(this.age);
		}

		var instance1 = new SubType("tom",20);
		instance1.colors.push("white");
		console.log(instance1.colors);
		instance1.sayName();
		instance1.sayAge();

		var instance2 = new SubType("jack",25);
		instance2.colors.push("black");
		console.log(instance2.colors);
		instance2.sayName();
		instance2.sayAge();

原型式继承


	//	原型式继承:
	//		借用原型可以基于已有对象来创建新对象,
	//		同时还不必因此创建自定义类型。
	//		只想让一个对象与另一个对象保持类似的情况下使用

	//		Object.create(一个用作新对象原型的对象,为新对象定义的额外属性):
	//		此方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
	//		在使用了第二个参数之后,会覆盖原型上的属性

	//    原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。
	//    属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。

		function object(o){
			function F(){}
			F.prototype = o; //新对象的原型
			return new F();
		}

		var person = {
			name:"tom",
			friends:["aaa",'bbb','ccc']
		}
    
		var p1 = object(person);
		p1.name="jarry";
		p1.friends.push("ddd");
		var p2 = object(person);
		p2.name="jack";
		p2.friends.push("tanglaoya");

		console.log(person.friends);

寄生式继承

function object(o){
			function F(){}
			F.prototype = o;
			return new F();
		}//任何返回新对象的函数都适用于此模式

		function createAnother(person){
			var clone = object(person);
			clone.sayHi = function(){
				console.log("Hi");
			}
			return clone;
		}
		var person = {
			name:"tom",
			colors:['red','green','blue']
		}
		var p1 = createAnother(person);
		p1.sayHi();

寄生组合式继承

组合式继承的最大问题是会调用两次超类的构造函数。

寄生组合式继承即使用借用构造函数模式来继承属性,使用原型链的混成形式来继承方法。主要思路是:不必为了指定子类型的原型而调用超类型的构造函数,所需要的其实是超类型原型的一个副本而已。本质上,使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型


		function object(o){
			function F(){}
			F.prototype = o;
			return new F();
		}//任何返回新对象的函数都适用于此模式

		function inheritPrototype(SubType,SuperType){
			//1.创建超类型原型的一个副本
			var prototype = object(SuperType.prototype);//创建对象   
			//2.为创建的原型添加constructor属性,为了弥补因重写原型而失去的constructor属性
			prototype.constructor=SubType;//增强对象	  
			SubType.prototype=prototype;//指定对象
      //	SubType.prototype = new SuperType();
      // 不必为了指定子类型的原型而调用超类型的构造函数,所需要的其实是超类型原型的一个副本而已
      // 本质上,使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型
		}

		function SuperType(name){
			this.name = name;
			this.colors = ['red','green','blue'];
		}
		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);
		}

		var instance1 = new SubType("tom",20);
		instance1.colors.push("white");
		alert(instance1.colors);
		instance1.sayName();
		instance1.sayAge();

		var instance2 = new SubType("jack",25);
		instance2.colors.push("black");
		alert(instance2.colors);
		instance2.sayName();
		instance2.sayAge();

对象冒充实现继承

function Parent(firstname){
		    this.fname=firstname;
		    this.age=40;
		    this.sayAge=function(){
		        console.log(this.age);
		        console.log(this.fname)
		    }
		}

		function Child(firstname){
		    this.parent=Parent;
		    this.parent(firstname);//子对象冒充父对象    相当于es6中的super
		    delete this.parent;
		    this.saySomeThing=function(){
		        console.log(this.fname);
		        this.sayAge();
		    }
		}

		var mychild=new Child("李");
        mychild.saySomeThing();
        var myChild = new Parent("王");
        myChild.sayAge()

第七章 函数表达式

函数声明最重要的特征就是函数声明提升。 意思在执行代码之前会先读取函数声明。

同一个标识符的情况下,变量声明与函数声明都会提升;函数声明会覆盖变量声明,但不会覆盖变量赋值,即:如果声明变量的同时初始化或赋值那么变量优先级高于函数。

闭包

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

当某个函数被调用时,会创建一个执行环境与相应的作用域链。然后,使用arguments对象及其它命令参数来初始化函数的活动对象。

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

闭包的作用域链包含着自己的作用域链、包含函数的作用域和全局作用域。

包含函数在执行完毕后,其活动对象不会销毁,因为内部的匿名函数的作用域链还在引用它。所以这个匿名函数(闭包)就可以访问到包含函数中的所有变量。

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

关于this对象

每个函数的调用时都会自动获得this和arguments对象。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。


第八章 BOM

全局变量不能通过delete操作符删除,而直接在window上定义的属性可以。

Window

窗口关系及框架

top对象始终指向最高(最外)层框架,也就是浏览器窗口。

parent始终指向当前框架的直接上层框架。

self始终指向window。

scrollBy()//按照指定的像素值来滚动内容

scrollTo()//把内容滚动到指定的坐标。

窗口位置

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

窗口大小

outerWidth 和 outerHeight 返回浏 览器窗口自身的大小;

innerWidth 和 innerHeight 返回浏览器窗口中页面视口的大小(不包含浏览器边框和工具栏);

document.documentElement.clientWidth 和 document.documentElement.clientHeight 返回页面视口的宽度和高度。

//虽然最终无法确定浏览器窗口本身的大小,但可以取得页面视口的大小
var pageWidth = window.innerWidth, 
    pageHeight = window.innerHeight; 

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.pageXoffset/window. scrollX 和 window.pageYoffset/window.scrollY;

可以使用 scroll()、scrollTo()和 scrollBy()方法滚动页面;

间歇调用和超时调用

setTimeout()第二个参数是表示等待多长时间的毫秒数,但经过该时间后指定的代码不一定执行。JS是一个单线程的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JS任务队列。这些任务会按照将它们添加到队列的顺序执行。第二个参数告诉JS再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;否则等待前面的代码执行完之后执行。

Location

属性名

hash(#...)                    

host(服务器名称和端口号)                             

hostname (服务器名称)                     

href(URL)   

pathname(URL中的目录或文件名)  

port(端口号)  

protocol(协议)  

search(查询字符串)  

查询字符串参数

		var qs = (location.search.length > 0) ? location.search.substring(1) : '';
		var args = {};//保存数据的对象
		var item = null,name = "",value = "";
		var items = qs.length ? qs.split('&') : [];
		for(var i=0; i<items.length; i++){
			item = items[i].split("=");
			name = decodeURIComponent(item[0]);
			value = decodeURIComponent(item[1]);
			if(name){
				args[name] = value;
			}
		}
		console.log(args)

URLSearchParams

URLSearchParams 提供了一组标准 API 方法,通过它们可以检查和修改查询字符串。给 URLSearchParams 构造函数传入一个查询字符串,就可以创建一个实例。这个实例上暴露了 get()、 set()和 delete()等方法,可以对查询字符串执行相应操作。

let qs = "?q=javascript&num=10";
let searchParams = new URLSearchParams(qs);
alert(searchParams.toString()); // " q=javascript&num=10"
searchParams.has("num"); // true
searchParams.get("num"); // 10
searchParams.set("page", "3");
alert(searchParams.toString()); // " q=javascript&num=10&page=3"
searchParams.delete("q");
alert(searchParams.toString()); // " num=10&page=3"

大多数支持 URLSearchParams 的浏览器也支持将 URLSearchParams 的实例用作可迭代对象

let qs = "?q=javascript&num=10";
let searchParams = new URLSearchParams(qs);
for (let param of searchParams) {
 console.log(param);
}
// ["q", "javascript"]
// ["num", "10"] 

位置操作

立即打开新的URL并在浏览器历史记录中生成一条记录:

window.location = location.href = location.assign()

可以导航到新的URL,并不会生成历史记录,用户不能回到前一个页面

location.replace()

重新加载页面

location.reload()  // 有可能 从缓存中加载

location.reload(true)  // 从服务器重新加载

navigator

navigator 对象的属性通常用于确定浏览器的类型

检测插件

navigator.plugins: 通过数组来确定

注册处理程序

navigator.registerProtocolHandler()

history

go()   back()   forward();

如果页面 URL 发生变化,则会在历史记录中生成一个新条目。对于 2009 年以来发 布的主流浏览器,这包括改变 URL 的散列值(因此,把 location.hash 设置为一个新 值会在这些浏览器的历史记录中增加一条记录)。这个行为常被单页应用程序框架用来模 拟前进和后退,这样做是为了不会因导航而触发页面刷新。

历史状态管理

hashchange 会在页面 URL 的散列变化时被触发,开发者可以在此时执行某些操作;

状态管理 API 则可以让开发者改变浏览器 URL 而不会加载新页面:history.pushState(stateObj, 'title', url);

因为 pushState()会创建新的历史记录,所以也会相应地启用“后退”按钮。此时单击“后退” 按钮,就会触发 window 对象上的 popstate 事件。popstate 事件的事件对象有一个 state 属性,其 中包含通过 pushState()第一个参数传入的 state 对象:

window.addEventListener("popstate", (event) => {
   let state = event.state;
   if (state) { // 第一个页面加载时状态是 null
   processState(state);
 }
});

可以通过 history.state 获取当前的状态对象,也可以使用 replaceState()并传入与 pushState()同样的前两个参数来更新状态。更新状态不会创建新历史记录,只会覆盖当前状态。


第十章 DOM

  • nodeType、nodeName、nodeValue
  • 节点关系
    • childNodes 更改为子节点的集合
    • children 更改为子元素的集合     *没有兼容性
    • parentNode 更改对象为当前节点的父节点
    • parentElement 父元素节点
    • previousSibling 、nextSibling        ie6/7/8使用
    • firstChild 、lastChild
    • previoueElementSibling 、nextElementSibling   高版本浏览器使用
    • firstElementChild 、lastElementChild
    • hasChildNodes()  节点拥有子节点返回true
    • ownerDocument 该属性指向表示整个文档的文档节点
  • 操作节点
    • 创建节点
      • createElement()、createTextNode()
    • 添加节点
      • appendChild():
        • 添加新节点到当前节点内部的后面;移动已有节点到当前节点内部的后面
      • insertBefore(要添加或移动的节点,参考节点)
        • 添加新节点到当前节点内部的前面;移动已有节点到当前节点内部的前面
    • 替换节点
      • replaceChild()
    • 删除节点
      • removeChild()、
    • 以上方法必须先获取到父节点
    • 克隆节点
      • cloneNode(true/false):
        • true深复制: 即复制节点及其整个子 DOM 树。
        • false浅复制(默认值): 则只会复制调用该方法的节点
    • normalize():处理文档树中的文本节点

Document

  • 文档子节点
    • documentElement、body
  • 文档信息
    • title
    • url
    • domain  不能设置成url中不包含的域
    • referrer  保存着链接到当前页面的那个页面的URL
  • 查找元素
    • document.getElementById()
    • document.getElementsByTagName()  返回一个HTMLCollection对象
      • item()
      • length
      • namedItem() 使用这个方法可以通过元素的name取得集合中的项
    • document.getElementsByName()
  • 特殊集合
  • 文档写入
    • write()    writeln() 

Element、Text、Attr

  • 取得特性
    • getAttribute()  setAttribute()   removeAttribute() 
  • 分割文本节点
    • splitText(指定位置)
  • 创建
    • document.createAttribute()

DocumentFragment

创建方式:document.createDocumentFragement()

文档片段继承了Node的所有方法,通常用于执行那些针对文档的DOM操作。如果将文档中的节点添加到文档片段中,就会从文档树中移除该节点,也不会从浏览器中看到该节点。添加到文档片段中的新节点同样也不属于文档树。可以通过appendChild()添加方法将文档片段中的内容添加到文档中。实际上只会将文档片段的所有子节点添加到相应位置上,文档片段永远不会成为文档树的一部分。


第十一章 DOM扩展

选择符API

  • 可以基于Document类型、Element类型进行调用
    • querySelector("css选择符")  
    • querySelectorAll

元素遍历

  • 对于元素间的空格,IE9之前的版本不会返回文本节点,但是其它浏览器里面都会返回文本节点。有了以下属性,不必担心空白文本节点
    • childElementCount
    • firstElementChild
    • lastElementChild
    • previousElementSibling
    • nextElementSibling

HTML5

  • 与类相关的扩充
    • getElementsByClassName()
    • classList
      • add(value)
      • contains(value)
      • remove(value)
      • toggle(value) 
  • 焦点管理
    • document.activeElement   此属性始终会引用dom中当前获得了焦点的元素
    • document.hasFocus()   用于确定文档是否获取和焦点
  • HTMLDocument的变化
    • document.readyState
      • loading
      • complete
    • 兼容模式
if(document.compatMode == "CSS1Compat"){
  alert("标准模式")
}else{
    alert("混杂模式")
}
    • document.head
  • 自定义数据属性
    • 添加了自定义属性之后,可以通过元素的dataset属性来访问,是一个名值儿对的映射
  • 插入标记
    • innerHTML  返回与调用元素的所有子节点对应的标记
    • outerHTML  返回调用元素及它的所有子节点对应的标记
    • insertAdjacentHTML()\ insertAdjacentText()
  • scrollIntoView() 可以滚动浏览器窗口或容器元素以便包含元 素进入视口
  • 专有扩展
    • 文档模式
    • contains()
    • children()
  • 插入文本
    • innerText
    • outerText

第十三章 事件

事件流

事件流描述的是从页面上接收事件的顺序。

事件冒泡即事件开始时由最具体的元素接收(文档中嵌套最深层的那个节点),然后逐级向上传播到较为不具体的节点(文档)。

事件捕获即由不具体的节点应该更早接收事件,而最具体的节点应该最后接收到事件。 事件捕获实际上是为了在事件到达最终目标 前拦截事件。

DOM2级事件流:事件捕获阶段、处于目标阶段、事件冒泡阶段

DOM2级事件处理程序

addEventListener(事件名,处理函数名,true/false)    //true:在捕获阶段调用事件处理程序;false:冒泡阶段

removeEventListener(事件名,处理函数名,true/false)

attachEvent("on"+事件名,处理函数名)

detachEvent("on"+事件名,处理函数名)

两种添加事件的方法区别主要在于事件处理程序的作用域。在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;而在IE中使用attachEvent()方法的情况下,事件处理程序会在全局作用域下运行,此时this等于window。

DOM中的事件对象

阻止特定事件的默认行为:preventDefault()

停止事件在DOM层中的传播:stopPropagation()

在事件处理程序内部,this始终等于currentTarget的值(当前事件处理程序所在的元素);而target只包含事件的实际目标

IE中的事件对象

IE 事件对象可以基于事件处理程序被指定的方式以不同方式来访问。如果 事件处理程序是使用 DOM0 方式指定的,则 event 对象只是 window 对象的一个属性;

使用 attachEvent()时,event 对象仍然是 window 对象的属性(像 DOM0 方式那样),只是出 于方便也将其作为参数传入 ;

事件目标:event.srcElement(与DOM中的target相同);

阻止默认行为:returnValue = false

阻止冒泡:cancleBubble = true

鼠标与滚轮事件

  • 客户区坐标位置
    • clientX、clientY  表示在事件发生时指针在视口中的水平与垂直位置
  • 页面坐标位置
    • pageX、pageY  在页面没有滚动的情况下,与clientX、clientY值相等
    • 在IE8及之前
var div = document.getElementById('div');
div.onclick = function(e){
	var pageX = e.pageX;
  var pageY = e.pageY;
  if(pageX == undefined){
  	pageX = e.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft);
  };
  if(pageY == undefined){
  	pageY = e.clientY + (document.body.scrollTop || document.documentElement.scrollTop);
  }
  console.log(pageX,pageY);
}
  • 屏幕坐标位置
    • screenX 、screenY
  • 修改键
    • shiftKey 、altKey、ctrlKey、metaKey
    • getModifierState()

键盘与文本事件

  • 键盘事件
    • keydown、keyup
      • keyCode
    • keypress
      • charCode
    • textInput:在文本插入文本框之前会触发textInput事件
      • inputMethod
      • DOM3 Events 规范增加了一个名为 textInput 的事件,其在字符被输入到可编辑区域时触发。textInput 事件的行为有些不一样。
        • 一个区别是 keypress 会在任何可以获 得焦点的元素上触发,而 textInput 只在可编辑区域上触发。
        • 另一个区别是 textInput 只在有新字 符被插入时才会触发,而 keypress 对任何可能影响文本的键都会触发(包括退格键)。
      • 因为 textInput 事件主要关注字符,所以在 event 对象上提供了一个 data 属性,包含要插入的 字符(不是字符编码)。data 的值始终是要被插入的字符,因此如果在按 S 键时没有按 Shift 键,data 的值就是"s",但在按 S 键时同时按 Shift 键,data 的值则是"S"。

H5事件

  • contextmenu事件   通过鼠标右键调出上下文菜单
  • beforeunload         浏览器卸载页面前触发
  • DomContentLoaded    DOM 树构建完成后立即触发
  • readystatechange    这个事件的目的是提供与文档或元素的加载状态有关的信息
    • readystate
      • uninitialized:对象存在并尚未初始化。
      • loading  正在加载
      • loaded    加载完毕
      • interactive  交互
      • complete   完成
  • pageshow (页面显示时触发)pagehide(卸载页面时触发 unload事件之前)
    • event.persisted
  • 历史状态管理
    • hashchange   以便在url的参数列表发生变化时触发
      • event.oldURL\newURL         /   location.hash
    • history.pushState()   会创建新的历史状态,从而触发window的popstate事件
    • popstate事件
    • replaceState()   会重写当前状态

触摸与手势事件

  • 触摸事件
    • touchstart
    • touchmove
    • touchend
    • touchcancel
      • touchs
      • targetTouchs
      • changeTouches

内存与性能

  • 事件委托:利用事件冒泡,只需在最高层上的dom上添加一个事件处理程序,就可以管理某一类型的所有事件。
  • 事件委托具有如下优点。
    • document 对象随时可用,任何时候都可以给它添加事件处理程序(不用等待 DOMContentLoaded 或 load 事件)。这意味着只要页面渲染出可点击的元素,就可以无延迟地起作用。
    • 节省花在设置页面事件处理程序上的时间。只指定一个事件处理程序既可以节省 DOM 引用,也 可以节省时间
    • 减少整个页面所需的内存,提升整体性能。

模拟事件

  1. 模拟鼠标事件
let btn = document.getElementById("myBtn");
// 创建 event 对象
let event = document.createEvent("MouseEvents");
// 初始化 event 对象
event.initMouseEvent("click", true, true, document.defaultView,
 0, 0, 0, 0, 0, false, false, false, false, 0, null);
// 触发事件
btn.dispatchEvent(event); 

第十四章  表单

  • 选择文本
    • select()方法    用于选择文本框中的所有文本
    • select   事件   在选择了文本框中的文本时触发
    • 取得选择的文本
      • selectionStart、selectionEnd
 var text = document.getElementById('text');
    text.onselect = function(){
      console.log(text.value.substring(text.selectionStart, text.selectionEnd))
    }
    • setSelectionRange()     选择部分文本     
		var text = document.getElementById('text');
     var a = text.setSelectionRange(0,4);
     text.focus()
     console.log(a)
  • H5约束验证API
    • required  必填字段
    • 其它输入类型(type)
      • email
      • url
    • 输入模式    
      • pattern 属性
    • checkValidity()  方法可以检测表单中的某个字段是否有效
    • novalidate    禁用验证
    • formnovalidate    如果表单中有多个提交按钮,利用该属性可以指定按钮不必验证表单
  • 选择框脚本
      • add(newOption,relOption) 向控件中插入新的
      • multiple
      • options
      • remove
      • selectedIndex   基于0的选中项的索引
      • 每个都有以下属性
        • index、label、value、selected、text
    • 选择选项
      • 使用selectedIndex属性
      • 取得对某一项的引用,将其selected设置为true:selectedBox.options[0].selected = true;
    • 添加选项
      • 使用传统的DOM方法
      • 使用Option构造函数
      • 使用选择框的add()方法
    • 移除选项
      • 使用removeChild()
      • 使用选择框的remove()
      • 将相应的选择设置为null:   selectBox.options[0] = null

第十五章  使用Canvas绘图

基本用法

var drawing = document.getElementById("drawing");
// 确定浏览器支持canvas
if(drawing.getContext){
  	var context = drawing.getContext("2d");
  //......
}

2D上下文

填充和描边

  • fillStyle
  • strokeStyle
var drawing = document.getElementById("drawing");
// 确定浏览器支持canvas
if(drawing.getContext){
  	var context = drawing.getContext("2d");
 		context.strokeStyle = 'red';
  	context.fillStyle = "#ff00ff";
}

绘制矩形

  • fillRect()
  • strokeRect()
  • clearRect()
  • 以上方法都接收四个参数:矩形的X坐标、Y坐标、矩形宽度、高度
  • clearRect()    清除画布上的矩形区域
var drawing = document.getElementById("drawing");
// 确定浏览器支持canvas
if(drawing.getContext){
  	var context = drawing.getContext("2d");
  	//绘制红色矩形
 	context.fillStyle = "#ff0000";
  	context.fillRect(10,10,50,50);
   //绘制红色描边矩形
   context.strokeStyle = "#ff0000";
  	context.strokeRect(10,10,50,50);
}
  • lineWidth  描边线条的宽度
  • lineCap     线条末端形状
    • butt平头、round圆头、square方头
  • lineJoin     线条相交的方式
    • round圆交、bevel斜交、miter斜接

绘制路径

  • beginPath()   表示开始绘制路径
  • arc(x,y,radius,startAngle,endAngle,counterclockwise)
  • arcTo(x1,y1,x2,y2,radius)
  • bezierCurveTo(c1x,c1y,c2x,c2y,x,y)
  • lineTo(x,y)
  • moveTo(x,y)
  • quadraticCurveTo(cx,cy,x,y)
  • rect(x,y,width,height)
  • closePath()

绘制文本

  • fillText()
  • strokeText()
  • 以上方法接收四个参数:文本、x坐标、y坐标、可选的最大像素;并且以下面三个属性为基础
    • font
    • textAlign
    • textBaseLine

变换

  • rotate(angle)
  • scale(x,y)
  • translate(x,y)
  • transform(x,y)
  • setTransform

绘制图像

  • drawImage()

阴影

  • shadowColor
  • shadowOfffsetX
  • shadowOfffsetY
  • shadowBlur    模糊的像素数

线性渐变

  • createLinearGradient(startX,startY,endX,endY)
  • addColorStop(色标位置,颜色值)

径向渐变

  • createRadialGradient(startX,startY,startRadius,endX,endY,endRadius)

模式(重复的图像)

  • createPattern(img元素,重复字符串) 第二个参数与background-repeat属性值相同

使用图像数据

  • getImageData(X,Y,width,height)

第二十章  JSON

  • JSON.stringify() 除了要序列化的js对象外,还可以接收另外两个参数
    • 第二个参数是个过滤器,可以是数组;也可以是函数
      • 函数接收两个参数:属性名和属性值
    • 第三个参数用于控制结果中的缩进和空白符
      • 如果是数值表示每级缩进的空格数
      • 如果是字符串会被当作结果中的缩进字符串
  • JSON.parse()也可以接收另一个参数,该参数是函数
    • 函数接收两个参数:属性名和属性值
      • 如果这个函数返回undefined,则结果中删除相应的键;如果返回其它值,则将它插入到结果中

第二十一章  Ajax与Comet

Ajax技术的核心是XMLHttpRequest对象,XHR为向服务器发送请求和解析服务器响应都提供了流畅的接口,能够以异步方式从服务器获取得多信息。意味着用户单击后,可以不必刷新页面也能取得新数据。也就是说,可以XHR对象取得新数据,然后再通过DOM将新数据插入到页面中。

XHR用法

  • 在使用XHR对象时,调用的第一个方法就是open(要发送请求的类型,url,表示是否异步的布尔值)。
  • 要发送特定的请求,使用send(要发送的数据)方法。
  • 收到响应后,会自动填充XHR对象,相关属性如下:
    • responseText:作为响应主体被返回的文本
    • responseXML
    • status:http状态
    • statusText:http状态说明
  • 在异步请求过程中,检测XHR对象的readyState属性,可以获取响应当前的活动阶段
    • 0:未初始化。没调用open()
    • 1:启动。调用open(),没有调用 send()
    • 2:发送。调用了send(),但没收到响应
    • 3:接收。收到部分响应
    • 4:完成。接收全部响应
      • 最好是在调用open()之前指定onreadystatechange事件处理程序以确保跨浏览器兼容性
  • 在收到响应之前,可以使用abort()方法取消异步请求

HTTP头部信息

  • 使用setRequestHeader()设置头部请求信息

GET请求

  • 查询字符串中的名和值必须使用encodeURIComponent()进行编码

进度事件

  • loadstart:当接收到服务器响应数据第一个字节时触发
  • progress:当接收相应期间不断触发(open()之前调用此方法)
    • 事件处理程序会接收event对象
      • target
      • lengthComputable
      • position
      • totalSize
  • error:当请求发生错误的时候触发
  • abort:在因为调用abort()方法而终止连接时触发
  • load:接收到完整响应数据时触发
  • loadend:当通信完成或触发了onerror、onabort、onload事件后触发

跨域资源共享

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

浏览器在发送请求时,检测到跨域时,会自动加上一个额外的 Origin 头部,其中包含请求页面的原信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。

Origin: http://www.nczonline.net

如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的原信息(如果是公共资源,可以回发“*”)。

Access-Control-Allow-Origin: http://www.nczonline.net

 

各浏览器对CORS的实现

IE8除外,其它浏览器在尝试在打开不同源的资源时,无需额外编写代码就可以触发这个行为。要请求位于另一个域中的资源时,使用标准的XHR对象并在open()方法中传入绝对的URL即可。

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
	if(xhr.readystate == 4){
		if((xhr.status >=200 && xhr.status < 300) || xhr.status == 304){
      	alert(xhr.responseText);
		}else{
			alert('Request was unsuccessful:'+xhr.status)
		}
	}
}
xhr.open("get","http://www.somewhere-else.com/page/",true);
xhr.send(null)

带凭据的请求

默认情况下,跨域请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。如果服务器接受带凭据的请求,会用下面的HTTP头部做响应:

Access-Control-Allow-Credentials:true

其它跨域技术

图像Ping

通过使用标签。因为,一个网页可以从任何网页中加载图像,不用担心跨不跨域。

图像Ping有两个缺点:一是只能发送GET请求,二是无法访问服务器的响应文本。

JSONP   

JSONP(JSON with padding),是被包含在函数调用中的JSON。

callback({"name":"lei"})

JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数;而数据是传入回调函数中的JSON数据。

http://freegeoip.net/json/?callback=handleResponse

JSONP是通过动态创建

function handleResponse(response){
	alert(response.id,response.city,response.region_name)
}
var script = document.createElement('script[');
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script,document.body.firstChild)

JSONP的优点在于能够直接访问响应文本,支持在浏览器与服务器之间的双向通信。缺点:

它只支持GET请求而不支持POST等其它类型的HTTP请求;

它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题;

       jsonp在调用失败的时候不会返回各种HTTP状态码;

      安全性。万一假如提供jsonp的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。那么结果是什么?所有调用这个 jsonp的网站都会存在漏洞。于是无法把危险控制在一个域名下…所以在使用jsonp的时候必须要保证使用的jsonp服务必须是安全可信的。

Comet

Ajax是一种从页面向服务器请求数据的技术,而Comet则是一种上服务器向浏览器推送数据的技术。有两种实现Comet的方式:

  • 长轮洵
    • 短轮洵:浏览器定时向服务器发送请求,看有没有更新的数据。
    • 长轮洵:页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送。发送完数据之后 ,浏览器关闭连接,随即又发起一个请求到服务器。这一过程在页面打开时持续不断。
    • 区别:在于服务器如何发送数据。短轮洵是服务器立即发送响应,无论数据是否有效;长轮洵是等待发送响应。
  • HTTP流
    • 在页面的整个生命周期内只使用一个http连接。就是浏览器向服务器发送一个请求,而服务器保持打开,然后周期性的向浏览器发送数据。

Web Sockets

Web Sockets的目标是在一个单独的持久连接上提供全双工、双向通信。在js中创建了Web Sockets之后,会有一个http请求发送到浏览器以发起连接。在取得服务器响应后,建立的连接会使用Web Sockets协议。

  • 创建Web Sockets
var socket = new WebSocket("ws://www.example.com/server.php");
  • 使用socket.close()关闭连接
  • 使用send()发送数据
    • 只能发送文本数据,传入对象时先将其序列化为json字符串
  • 当服务器向客户端发来消息时,WebSocket对象会触发message事件,把返回的数据保存在event.data中
socket.message = function(event){
	var data = event.data;
}
  • 其它事件
    • open()
    • error()
    • close()
      • 只有close事件中的event对象有额外的信息
        • wasClean   表示是否关闭
        • code   服务器返回的状态码
        • reason   包含服务器发回的消息

第二十二章  高级技巧

高级函数

安全的类型检测

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 RegeEXP]"
}
...

作用域安全的构造函数

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

var p1 = Person('let',20,'worker');
var p2= new Person('tao',20,"workder")

函数绑定

函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧经常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。

一个简单的bind函数接收一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并将所有参数传递过去。

//创建bind函数
function bind(fn,context){
	return function(){
		return fn.apply(context,arguments)
	}
}

//使用
var handler = {
	message: 'hello world',
  handleClick:function(){
		alert(this.message)
	}
}

var btn = document.getElementById('btn');
addEventListener(btn,'click',bind(handler.handleClick,handler));
//原生bind
addEventListener(btn,'click',handler.handleClick.bind(handler));

函数柯里化

Currying概念其实很简单,是将多参数的函数转换成单参数的形式。和使用函数绑定的方法一样:使用一个闭包返回一个函数。

//是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,
//并且返回接受余下的参数而且返回结果的新函数的技术。
		function curry(fn){
			var args=Array.prototype.slice.call(arguments,1);
			//alert("外部数组:"+args);
			return function(){
				var innerArgs=Array.prototype.slice.call(arguments);
				//alert("内部数组:"+innerArgs);
				var finalArgs=args.concat(innerArgs);
				//alert(finalArgs);
				return fn.apply(null,finalArgs);
			}
		}
		function add(num1,num2){
			return num1+num2;
		}
		var curriedAdd=curry(add,5,12);
		alert(curriedAdd());

函数柯里化作为函数绑定的一部分包含在其中,构造出更为复杂的bind函数

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);
			}
}

var handler = {
	message: 'hello world',
  handleClick:function(){
		alert(this.message)
	}
}

var btn = document.getElementById('btn');
addEventListener(btn,'click',bind(handler.handleClick,handler,"my-btn"));
//原生bind实现函数柯里化,只需在this的值后再传入参数即可
addEventListener(btn,'click',handler.handleClick.bind(handler,"my-btn"));

防篡改对象

不可扩展对象

  • Object.preventExtensions(obj)
  • Object.isExtensible(obj)   

密封的对象

密封对象不可扩展,而且已有成员的[[Configurable]]特性被设置为false,也就是不能删除属性和方法。

  • Object.seal(obj)
  • Object.isSeal(obj)

冻结的对象

冻结的对象既不可扩展,也是密封的,对象的[[Writable]]特性被设置为false。

  • Object.freeze()

高级定时器

javascript是运行于单线程环境中的,而定时器仅仅只是计划在未来的某个时间执行。执行时机是不能保证的。

除了js执行进程外,还有一个需要在进程下一次空闲时执行的代码队列。随着页面在其生命周期中的推移,代码会按照顺序加入队列。

定时器的工作方式是:在特定时间后将代码插入到队列。指定的时间间隔表示何时将代码添加到队列,而不是何时执行。

重复的定时器

重复定时器会有以下缺点:

  • 某些间隔会被跳过
  • 多个定时器的代码执行之间的间隔可能会比预期小
  • 解决办法:
setTimeout(function(){
  //处理中...
	setTimeout(arguments.calee,interval)
},interval)

Yielding Processes

数组分块技术,小块小块的处理数组。基本的思想是为要处理的项目创建一个队列,然后使用定时器取出下一次要处理的项目进行处理,接着再设置另一个定时器。

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>数组分块</title>
	<script type="text/javascript">
		//基本模式:
		// setTimeout(function(){
		// 	//取出下一个条目并处理
		// 	var item = array.shift();
		// 	process(item);

		// 	//若还有条目,再设置另一个定时器
		// 	if(array.length>0){
		// 		setTimeout(arguments.callee,100);
		// 	}
		// },100);
		//数组分块函数:
		function chunk(array,process,context){
			setTimeout(function(){
				var item = array.shift();
				//alert(item);
				process.call(context,item);

				if(array.length>0){
					setTimeout(arguments.callee,100);
				}
			},100)
		}
		//demo:
		
		window.onload=function(){
			var data=[12,123,33,432,54,65,777,4444,4322,5454,65656,43242,2434,22,555];
			function printvalue(item){
				var div=document.getElementById("div1");
				div.innerHTML+=item+"<br>";
			}
			chunk(data,printvalue);
			//传递给chunk的数组是用作一个队列的,因此当处理数据 的同时,数组中的条目也在改变。如果想保持原数据不变,则将原数组的克隆传递给chunk()
			//chunk(data.concat(),printValue);
		}
	</script>	
</head>
<body>
	<div id="div1"></div>
</body>
</html>