属性与原型
①function foo(){this.add = function(){};} ②foo.prototype.add = function(){}; ①中的add是属性;②中的add是原型。 当调用foo.add时,查询顺序:foo属性--->foo原型--->Object原型 foo.prototype.hasOwnProperty(prop);判断prop是否为foo的自身属性。 delete只能删除属性(除了window对象的属性和 configurable=false 的属性),不能删除原型
原型链和构造函数
function Foo(name){ this.name=name; } Foo.prototype.run=function(){}; var f = new Foo("Test"); 注:IE中不支持__proto__。 f.__proto__===Foo.prototype //true f.__proto__.run === Foo.prototype.run //true f.run ===f.__proto__.run //true f.constructor ===Foo //true Foo.prototype.constructor === Foo //true
继承方式中易犯的错误:
Son.prototype = Father.prototype 这时如果修改Son的原型链,Father的原型也会被修改 原因:Son继承的是Father的原型链地址,所以修改的也是该地址的内容。
闭包
简单的定义:有权访问另一个函数作用域中的变量的函数。
从理论角度:所有的函数。因为任意函数在创建之初就已将外层上下文保存起来了。
哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
从实践角度:以下函数才算是闭包:
即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)在代码中引用了自由变量
闭包很强大,但是不合理的应用也是会付出代价的,因为创建闭包必须维护额外的作用域,这可能会占用大量内存。
因为IE8以下版本垃圾回收机制的问题,如果闭包的作用域链中保存着DOM元素,则该元素无法被回收。即传说中的内存泄露。
可参照IE内存泄露分析 。Demo:
var but = document.getElementById("button1"); var text = document.getElementById("textbox1"); but.onclick = function() { /*现在是在闭包中*/ alert(text.value); };
对应方法:
var but = document.getElementById("button1"); var text = document.getElementById("textbox1"); var textValue = text.value; but.onclick = function() { /*现在是在闭包中*/ alert(textValue); };
即 把闭包中的DOM元素通过其他方式去除。不过这在工作中,显然不太容易。。。
执行上下文
JS中仅有函数能创建新的作用域。if,while,for...则不能。如下 for(var i=0;i<2;i++){} alert(i); 此时不会出现 ReferenceError 。 数组元素的this指向该数组对象,例如 var arr = [function(){return this;},3,"SS"]; 则 arr[0]()===arr;
typeof 与 instanceof
typeof 只能用来判断变量属于哪一种基本类型,即string,boolen,function等等 instanceof 用于判断对象的从属关系 例如 var foo = new Foo(); 则 foo instanceof Foo===true
在ES5中可以用getPrototypeOf或isPrototypeOf来完成相同功能
例如 Object.getPrototypeOf(foo) === Foo
Foo.prototype.isPrototypeOf(foo)
arguments:对象当前所执行的函数;不是数组,但访问方式与数组相同;可用于模拟方法重载。
属性: length 当前方法的传过来的参数个数; callee 当前正在执行的Function对象,且仅当改函数正在执行时才可用,常用于递归运算(同样适用于匿名函数) 例如 function foo(a,b,c){...} foo(1,3); arguments.callee===foo //true arguments.length ===2 //true foo.length === 3 //true 且只读(即使赋值foo.length=10,输出的时候foo.length仍然为3) arguments.length与foo.length的区别在于,前者是实参个数,后者是形参个数
组件模式(紧耦合增益):
var Module = (function(obj){ obj.old_Method = obj.Method; obj.Method = function(){...}; return obj; }(Module || {}));
函数表达式
函数声明与函数表达式的区别:
①通过函数声明定义的函数在进入上下文阶段就已经创建;如
function A(){ .... }
②而函数表达式则先定义一个变量,当代码执行时才将匿名函数赋给该变量。如
var B = function(){ .... }
二者还有个区别就是二者的name属性不同。函数声明的name是方法名,而函数表达式的name则为空字符串.
对构造函数和一般的函数来说,搞清this所指向的对象并不困难。但匿名函数就比较头晕了。就向下面的这段东东
function A(){ console.log(this); eval('console.log(this)'); (function(){ console.log(this); eval('console.log(this)'); })() } new A()
匿名函数的执行环境具有全局性,因此其中的this对象通常指向window
获取对象属性名
Object.keys(obj)//感觉这个方法不错,不仅仅是JS对象,连DOM对象的属性名也可以获取
使用JavaScript修改DOM树(考虑性能)
浏览器下载完页面中的所有组件之后会解析生成DOM树、渲染树两个数据结构,然后开始绘制页面元素;
当DOM的变化影响了元素的几何属性,会导致浏览器重新构造渲染树,这个过程叫做重排。
重排之后浏览器会重新绘制受影响的部分到屏幕,这个过程叫做重绘。
而重排和重绘都是代价昂贵的操作,会使web应用程序的UI反应迟钝。
所以在需要对DOM元素进行一系列操作时,要将重绘和重排最小化:
(1)使元素脱离文档流;
(2)对其应用多重改变;
(3)将元素带回文档中;
有三种方法可以使DOM脱离文档:
//1:隐藏元素,应用修改,重新显示;如 var div = document.getElementById("xx"); div.style.display = "none"; //TODO div.style.display = ""; //2:使用文档片段,在当前DOM之外构建一个子树,再把它拷贝回文档;如 var cdf = document.createDocumentFragment(); var child; for(var i = 0;i<100;i++){ child = document.createElement("div"); cdf.appendChild(child); } document.body.appendChild(cdf); //3:将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素;