async:加载外部脚本文件,通知浏览器当即下载,异步执行javascript
defer:脚本能够延迟到文档彻底被解析和显示以后在执行java
noscript:
浏览器不支持脚本。
浏览器支持脚本,可是脚本被禁用node
复制变量值编程
function setName(obj) { obj.name = "Nicholas"; obj = new Object(); obj.name = "Greg"; } var person = new Object(); setName(person); alert(person.name); //"Nicholas"
在函数重写obj时,这个变量引用就是一个局部对象。而这个对象会在函数执行完毕后当即被销毁。数组
检测类型浏览器
使用typeof检测基本数据类型,可是在检测引用类型的值是,这个操做符的用处不大,由于使用typeof没法知道它是什么类型的对象。为此,ECMAScript提供了 instanceof操做符。安全
var s = 'test'; var b = true; var i = 22; var u; var n = null; var o = new Object() console.log(typeof s); // string console.log(typeof b); // boolean console.log(typeof i); // number console.log(typeof u); // undefined console.log(typeof n); // object console.log(typeof o); // object
延长做用域链闭包
try-catch语句中的catch块
with语句app
垃圾回收框架
标记清除
引用计数
检测数组
value instanceof Array Array.isArray(value)
栈方法(后进先出)
队列方法(先进先出)
重排序方法
操做方法
var colors = ['red', 'green', 'blue', 'yellow', 'purple']; var colors2 = colors.slice(1) var colors3 = colors.slice(4) console.log(colors2); // ["green", "blue", "yellow", "purple"] console.log(colors3); // ["purple"]
var colors = ["red", "green", "blue"]; var removed = colors.splice(0,1); // 删除第一项 alert(colors); // green,blue alert(removed); // red,返回的数组中只包含一项 removed = colors.splice(1, 0, "yellow", "orange"); // 从位置1 开始插入两项 alert(colors); // green,yellow,orange,blue alert(removed); // 返回的是一个空数组 removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项 alert(colors); // green,red,purple,orange,blue alert(removed); // yellow,返回的数组中只包含一项
位置方法
迭代方法
缩小方法
var values = [1, 2, 3, 4, 5]; var sum = values.reduce((prev, cur, index, array) => { return prev + cur; }); console.log(sum); // 15
函数内部属性
属性和方法
每一个函数都包含两个属性
每一个函数都包含两个非继承而来的方法
function sum (num1, num2) { return num1 + num2; } function callSum1 (num1, num2) { return sum.apply(this, [num1, num2]); } function callSum2 (num1, num2) { return sum.call(this, num1, num2); } callSum1(10, 10); // 20 callSum2(10, 10); // 20 var callSum3 = sum.bind(null) callSum3(10, 10) // 20
Global对象
encodeURI()编码后的结果是除了空格以外的其余字符都原封不动,只有空格被替换成了%20,对应decodeURI()方法
属性类型
ECMAScript中有两种属性:数据属性和访问器属性
数据属性
要修改属性默认的特性,必须使用Object.defineProperty()方法。这个方法接收三个参数:属性所在对象、属性的名字和一个描述符对象。其中,描述符对象的属性必须是:configurabel、enumerable、writable、和value。设置其中的一个或多个值。
var person = {} Object.defineProperty(person, 'name', { writable: false, configurable: false, value: 'Nicholas' }); console.log(person.name); // Nicholas person.name = 'Greg'; console.log(person.name); // Nicholas delete person.name console.log(person.name); // Nicholas
Object.defineProperty()方法会对configurable为false的属性修改作限制
访问器属性
访问器属性不能直接定义,必须使用Object.defineProperty()来定义
var book = { _year: 2004, edition: 1 }; Object.defineProperty(book, 'year', { get: function() { return this._year }, set: function(newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004 } } }); book.year = 2005; console.log(book.edition); // 2
定义多个属性
ECMAScript5定义了一个Object.defineProperties()方法。利用这个方法能够经过描述符一次定义多个属性。这个方法接收两个对象参数。
var book = {}; Object.defineProperties(book, { _year: { value: 2004 }, edition: { value: 1 }, year: { get: function() { return this._year }, set: function(newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004 } } } });
读取属性的特性
使用Object.getOwnPropertyDescriptor()方法,能够取得给定属性的描述符
var descriptor = Object.getOwnPropertyDescriptor(book, '_year') console.log(descriptor.value); // 2004 console.log(descriptor.configurable); // false
工厂模式
function createPerson (name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function () { console.log(this.name); }; return o; } var person1 = createPerson('Nicholas', 29, 'Software Engineer'); var person2 = createPerson('Greg', 27, 'Doctor');
构造函数模式
function Person (name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { console.log(this.name); }; } var person1 = new Person('Nicholas', 29, 'Software Engineer'); var person2 = new Person('Greg', 27, 'Doctor');
原型模式
理解原型对象
function Person () {} Person.prototype.name = 'Nicholas'; Person.prototype.age = 29; Person.prototype.job = 'Software Engineer'; Person.prototype.sayName = function () { console.log(this.name); }; var person1 = new Person(); var person2 = new Person();
在默认状况下,全部原型对象都会自动得到一个constructor(构造函数)属性,这个属性包含一个指向一个prototype属性所在函数的指针。例如,Person.prototype.constructor指向Person
咱们能够经过isPrototypeof()方法来肯定对象之间是否存在原型关系。从本质上讲,若是[[Prototype]]指向调用isPrototypeof()方法的对象(Person.prototye),那么这个方法就返回true。
console.log(Person.prototype.isPrototypeOf(person1)); // true console.log(Person.prototype.isPrototypeOf(person2)); // true
ECMAScript5增长了一个新方法,叫Object.getPrototypeOf(),在全部支持的实现中,这个方法返回[[Prototype]]的值。例如:
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
虽然能够经过对象实例访问保存在原型中的值,但却不能经过对象实例重写原型中的值。若是咱们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那咱们就在实例中建立该属性,该属性将会屏蔽原型中的那个属性。
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { alert(this.name); }; var person1 = new Person(); person1.name = "Greg"; console.log(person1.name); //"Greg" — 来自实例 delete person1.name; console.log(person1.name); //"Nicholas" — 来自原型
经过delete操做符删除实例的属性,就恢复了对原型中name属性的链接。所以接下来再调用person1.name是,就返回了原型中name属性的值了。
Object.hasOwnProperty()方法能够检测一个属性是否存在于实例中,仍是存在于原型中。
原型与in操做符
function hasPrototypeProperty (object, name) { if (name in object) { return object.hasOwnProperty(name) // true:属性在实例中,false:属性在对象中 } else { console.log('没有该属性'); } }
function Person () {} Person.prototype.name = 'Nicholas'; Person.prototype.age = 29; Person.prototype.job = 'Software Engineer'; Person.prototype.sayName = function () { console.log(this.name); }; Object.keys(Person.prototype); // ["name", "age", "job", "sayName"] var person1 = new Person(); person1.name = 'Rob'; person1.age = 31; Object.keys(person1); // ["name", "age"]
Object.getOwnPropertyNames(Person.prototype); // ["constructor", "name", "age", "job", "sayName"]
更简单的原型方法
function Person () {} Person.prototype = { name: 'Nicholas', age: 29, job: 'Software Engineer', sayName: function () { console.log(this.name); } }
咱们将Person.prototype设置为一个新的对象,本质上是彻底重写了默认的prototype对象。可是这样有一个例外,constructor属性再也不指向Person了,而是指向Object。因此咱们须要将他的constructor属性设置成Person
function Person () {} Person.prototype = { constructor: Person, name: 'Nicholas', age: 29, job: 'Software Engineer', sayName: function () { console.log(this.name); } }
可是这种方式重设constructor属性会致使它的[[Enumerable]]的特性被设置为true,默认状况下,原生的constructor属性是不可枚举的。
function Person () {} Person.prototype = { name: 'Nicholas', age: 29, job: 'Software Engineer', sayName: function () { console.log(this.name); } } Object.defineProperty(Person.prototype, 'constructor', { enumerable: false, value: Person });
原型的动态性
重写整个原型对象会切断构造函数与最初原型之间的联系。记住:实例中的指针仅指向原型,而不指向构造函数
function Person () {} var friend = new Person(); Person.prototype = { constructor: Person, name: 'Nicholas', age: 29, job: 'Software Engineer', sayName: function () { console.log(this.name); } } friend.sayName(); // error
原生对象的原型
原型模式的重要性不只体如今建立自定义类型方面,就连全部的原生的引用类型,都是采用这种模式建立的。全部原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法。
原型对象的问题
原型模式的全部实例在默认状况下都将取得相同的属性值,最大的问题是其共享的本性所致使的。
function Person () {} Person.prototype = { constructor: Person, name: 'Nicholas', age: 29, job: 'Software Engineer', friends: ['Shelby', 'Court'], sayName: function () { console.log(this.name); } } var person1 = new Person(); var person2 = new Person(); person1.friends.push('Van'); console.log(person1.friends); // ["Shelby", "Court", "Van"] console.log(person2.friends); // ["Shelby", "Court", "Van"]
组合使用构造函数和原型模式
建立自定义类型的最多见的方式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
每一个实例都会有本身的一份实例属性的副本,但同事又共享着对方法的引用,最大的节省了内存
function Person (name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ['Shelby', 'Court']; } Person.prototype = { constructor: Person, sayName: function () { console.log(this.name); }; } var person1 = new Person('Nicholas', 29, 'Software Engineer'); var person2 = new Person('Greg', 27, 'Doctor'); person1.friends.push('Van'); console.log(person1.friends); // ["Shelby", "Court", "Van"] console.log(person2.friends); // ["Shelby", "Court"]
动态原型模式
经过在构造函数中初始化原型(仅在必要的状况下),又保持同时使用构造函数和原型模式的优势。换句话说,能够经过检查某个应该存在的方法是否有效,来决定是否须要初始化原型。
function Person (name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ['Shelby', 'Court']; if (typeof this.sayName === 'function') { Person.prototype.sayName = function () { console.log(this.name); }; } }
原型链
构造函数、原型和实例的关系:每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
function SuperType () { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property }; function SubType () { this.subproperty = false } // 继承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; }; var instance = new SubType(); console.log(instance.getSuperValue()); // true
别忘记默认原型
所用引用类型默认都继承Object,而这个继承也是经过原型链实现的。全部函数的默认原型都是Object的实例,所以默认原型都会包含一个内部指针,指向Object.prototype。
肯定原型与实例的关系
instanceof操做符
console.log(instance instanceof Object); // true console.log(instance instanceof SuperType); // true console.log(instance instanceof SubType); // true
isPrototypeOf()方法,只要是原型链中出现过的原型,均可以说是该原型链所派生的实例的原型
console.log(Object.isPrototypeOf(instance)); // true console.log(SuperType.isPrototypeOf(instance)); // true console.log(SubType.isPrototypeOf(instance)); // true
谨慎的定义方法
子类型有时须要重写超类型中的某个方法,或者须要添加超类型中不存在的某个方法。但无论怎样,给原型添加方法的代码必定要放在替换原型语句以后。
原型链的问题
借用构造函数
在解决原型中包含引用类型值所带来问题的过程当中,开始使用借用构造函数的技术。即在子类型构造函数的内部调用超类型构造函数
function SuperType () { this.colors = ['red', 'blue', 'green']; } function SubType () { // 继承了SuperType SuperType.call(this); // SuperType.apply(this); } var instance1 = new SubType(); instance1.colors.push('black'); console.log(instance1.colors); // ["red", "blue", "green", "black"] var instance2 = new SubType(); console.log(instance2.colors); // ["red", "blue", "green"]
传递参数
相对于原型链而言,借用构造函数有一个很大的优点,既能够在子类型构造函数中向超类型构造函数传递参数
function SuperType (name) { this.name = name; } function SubType () { // 继承了SuperType SuperType.call(this, 'Nicholas'); this.age = 29 } var instance = new SubType(); console.log(instance.name); // 'Nicholas' console.log(instance.age); // 29
借用构造函数的问题
方法都在构造函数中定义,由于函数复用就无从谈起
组合继承
既能经过在原型上定义方法实现了函数复用,又能保证每一个实例都有它本身的属性
function SuperType (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } SuperType.prototype.sayName = function () { console.log(this.name); }; function SubType (name, age) { // 继承了SuperType SuperType.call(this, name); this.age = age } // 继承了SuperType SubType.prototype = new SuperType(); SubType.prototype.sayAge = function () { console.log(this.age); }; var instance1 = new SubType('Nicholas', 29); instance1.colors.push('black'); console.log(instance1.colors); // ["red", "blue", "green", "black"] instance1.sayName(); // 'Nicholas' instance1.sayAge(); // 29 var instance2 = new SubType('Greg', 27); console.log(instance2.colors); // ["red", "blue", "green"] instance2.sayName(); // 'Greg' instance2.sayAge(); // 27
定义函数的方式有两种:一种是函数声明,另外一种是函数表达式。
函数声明的特征是函数声明提高,意思是在执行代码以前会先读取函数声明。
函数做用域链
当某个函数第一次被调用时,会建立一个执行环境及相应的做用域链,并把做用域链赋值给一个特殊的内部属性(即[[Scope]])。而后,使用this.arguments和其余命名参数的值来初始化函数的活动对象。但在做用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位,......直至做为做用域链终点的全局执行环境。
闭包与变量
// i 最终为10 function createFunctions () { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function () { return i } } return result; } // i 为 0,1,2...9 function createFunctions () { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function (num) { return function (arguments) { return num; }; }(i) } return result; }
关于this对象
this对象是在运行中基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被做为某个对象的方法调用时,this等于那个对象。不过匿名函数的执行环境具备全局性,所以其this对象一般指向window。固然,再经过call()和apply()改变执行函数执行环境的状况下,this就会指向其余对象
var name = 'The Window'; var object = { name: 'My Object', getNameFunc: function () { return function () { return this.name } } } console.log(object.getNameFunc()()); // 'The Window'
模仿块级做用域
匿名函数能够用来模仿块级做用域并避免这个问题。用块级做用域(一般称为私有做用域)的匿名函数的语法以下所示。
(function(){ })()
私有变量
function Person(name) { this.getName = function() { retirm name; } this.setName = function(value) { name = value } } var person = new Person('Nicholas'); console.log(person.getName()); // 'Nicholas' person.setName('Greg'); console.log(person.getName()); // 'Greg'
以上代码的构造函数中定义了两个特权方法:getName()和setName()。这两个方法均可以在构造函数外部使用,并且都有权访问私有变量name。但在Person构造函数外部,没有任何方法访问name。因为这两个方法是在构造函数内部定义的,它们做为闭包可以经过做用域链访问name。
静态私有变量
全局做用域
抛开全局变量会成为window对象的属性不谈,定义全局变量与在window对象上直接定义属相仍是有一点差异:全局变量不能经过delete属性操做符删除,而直接在window对象上的定义的属性能够。
var age = 29; window.color = 'red'; delete window.age; // 不会报错 delete window.color // 不会报错 返回true var newValue = oldValue; // 会抛出错误,由于oldValue未定义 var newValue = window.oldValue; // 不会报错,由于这是一次属性查询
窗口关系及框架
了解frameset和frame
窗口位置
下列代码能够跨浏览器取得窗口左边和上边的位置
Opera支持screenX,screenY。其余浏览器支持screenLeft,screenTop
var leftPops = (typeof window.screenLeft === 'number') ? window.screenLeft : window.screenX; var topPops = (typeof window.screenTop === 'number') ? window.screenLeft : window.screenY;
locatoin对象的属性
navigator对象
识别浏览器的信息
DOM是针对HTML和XML文档的一个API。DOM描绘了一个层次的节点树。
NODE类型
每一个节点都有一个nodeType属性,用于代表节点的类型。
if (someNode.nodeType == 1) { console.log('Node is an element'); }
nodeName和nodeValue属性
nodeName返回节点的标签名,如p,div,span等
nodeValue的值始终是null
节点关系
操做节点
事件流
事件冒泡
IE的事件流叫作事件冒泡,即事件开始由最具体的元素接收,而后逐级向上传播到较为不具体的节点
事件捕获
Netscape的事件流叫事件捕获,即不太具体的节点应该更早接收事件,而最具体的节点应该最后接收事件
DOM事件流
包括三个阶段:事件捕获阶段。处于目标阶段和事件冒泡阶段
事件处理程序
DOM2级时间处理程序
addEventListener
removeEventListener
定义了两个方法用于处理指定和删除事件处理程序的操做。全部的DOM节点中都包含这两个方法,接受三个参数:事件名、事件处理程序和布尔值。最后这个布尔值若是是true,表示在捕获阶段调用事件处理程序;false表示在冒泡阶段调用事件处理程序,默认是false。
经过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除。若是经过addEventListener()添加的匿名函数将没法移除。传入的函数要相同,不然没法移除
attachEvent
detachEvent
这两个方法接受两个参数:事件名(带on)和事件处理函数。
var btn = document.getElementById('myBtn');
var handler = function(){ console.log('clicked') };
btn.attachEvent('onclick', handler);
btn.detachEvent('onclick', handler); // 有效
事件对象
DOM的事件对象
事件类型
UI事件
load:当页面彻底加载后在window上面触发,img图片加载完
unload:当页面彻底卸载
abort:当用户中止下载过程
error:当发生JavaScript错误时在window触发
select:当用户选择文本框中的一个或者多个触发
resize:当窗口大小变化是触发
scroll:用户滚动时触发
内存和性能
事件委托利用了时间冒泡,指定一个事件处理程序,就能够管理某一个类型的全部事件
HTML5脚本编程
跨文档消息传递
核心方法是postMessage()方法,接受两个参数:一条消息和一个表示消息接收方来自哪一个域的字符串。
// 注意:全部支持XDM的浏览器也支持iframe的contentWindow
属性
var iframeWindow = document.getElementById('myframe').contentWindow; iframeWindow.postMessage('A secret', 'http://www.wrox.com');
高级技巧
高级函数
安全的类型检测
function isArray (value) { return Object.prototype.toString.call(value) === '[object Array]'; } function isFunction (value) { return Object.prototype.toString.call(value) === '[object Function]'; } function isRegExp (value) { return Object.prototype.toString.call(value) === '[object RegExp]'; }
做用域安全的构造函数
防止this指向window对象
function Person (name, age, job) { if (this instanceof Person) { this.name = name; this.age = age; this.job = job; } else { return new Person(name, age, jon); } } 惰性载入函数 function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined"){ if (typeof arguments.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i,len; for (i=0,len=versions.length; i < len; i++){ try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (ex){ //跳过 } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available."); } }
第一种实现方法: function createXHR () { if (typeof XMLHttpRequest != 'undefined') { createXHR = function () { return new XMLHttpRequest(); }; } else if (typeof ActiveXObjext != 'undefined') { createXHR = function () { if (typeof arguments.callee.activeXString != 'string') { var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i,len; for (i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (e) { // skip } } } return new ActiveXObject(arguments.callee.activeXString); }; } else { createXHR = function () { throw new Error('No XHR object available.'); } } return createXHR(); } 第二种改法: var createXHR = (function () { if (typeof XMLHttpRequest != 'undefined') { return function () { return new XMLHttpRequest(); }; } else if (typeof ActiveXObjext != 'undefined') { return function () { if (typeof arguments.callee.activeXString != 'string') { var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i,len; for (i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (e) { // skip } } } return new ActiveXObject(arguments.callee.activeXString); }; } else { return function () { throw new Error('No XHR object available.'); } } })();
函数绑定
bind函数: function bind (fn, context) { return function () { fn.call(context, arguments) } }
函数柯里化 function curry (fn) { var args = Array.prototype.slice.call(arguments, 1); return function () { var innerArgs = Array.prototype.slice.call(arguments) var finalArgs = args.concat(innerArgs) return fn.apply(null, finalArgs); } } function bind (fn, context) { var args = Array.prototype.slice.call(arguments, 2); return function () { var innerArgs = Array.prototype.slice.call(arguments) var finalArgs = args.concat(innerArgs) return fn.apply(context, finalArgs); } }