先上链接: 深入理解javascript原型和闭包(完结) ; 学习Javascript闭包(Closure) ;JavaScript秘密花园 ;深入理解js闭包
原型:
JavaScript 语言特性中,有很多方面和我们接触的其他编程语言不太一样,比如说,javascript语言实现继承机制的核心就是prototype ,而不是Java语言那样的类式继承。Javascript 解析引擎在读取一个Object的属性的值时,会沿着原型链向上寻找,如果最终没有找到,则该属性值为undefined ; 如果最终找到该属性的值,则返回结果。与这个过程不同的是,当javascript解析引擎执行“给一个Object的某个属性赋值”的时候,如果当前Object存在该属性,则改写该属性的值,如果当前的Object本身并不存在该属性,则赋值该属性的值 。
使用 for in 循环数组中的元素会枚举原型链上的所有属性,过滤这些属性的方式是使用 hasOwnProperty 函数。
函数也是一种对象。他也是属性的集合,可以对函数进行自定义属性。
每个函数都有一个属性叫做prototype。prototype的属性值是一个对象(属性的集合),默认只有一个叫做constructor的属性,指向这个函数本身。
每个函数function都有一个prototype,即原型。每个对象都有一个__proto__,可成为隐式原型。指向创建该对象的函数的prototype。
Object.prototype的__proto__指向null。
对于值类型,可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/function,不知道它到底是一个object对象,还是数组,还是new Number等等。这个时候就需要用到instanceof。
Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof的判断规则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。
当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。
到查找到达原型链的顶部 - 也就是 Object.prototype
- 但是仍然没有找到指定的属性,就会返回 undefined。
为了判断一个对象是否包含自定义属性而不是原型链上的属性, 我们需要使用继承自 Object.prototype
的 hasOwnProperty
方法。
hasOwnProperty
是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。
上下文和闭包:
javascript在执行一个代码段之前,都会进行这些“准备工作”来生成执行上下文。这个“代码段”其实分三种情况——全局代码,函数体,eval代码。
准备工作包括:变量、函数表达式——变量声明,默认赋值为undefined;this——赋值;函数声明——赋值;
函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。
函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。
全局代码的上下文环境数据内容为:
普通变量(包括函数表达式), 如: var a = 10; |
声明(默认赋值为undefined) |
函数声明, 如: function fn() { } |
赋值 |
this |
赋值 |
如果代码段是函数体,那么在此基础上需要附加:
参数 |
赋值 |
arguments |
赋值 |
自由变量的取值作用域 |
赋值 |
JavaScript 中每个函数内都能访问一个特别变量 arguments
。这个变量维护着所有传递到这个函数中的参数列表。
由于 arguments
已经被定义为函数内的一个变量。 因此通过 var
关键字定义arguments
或者将arguments
声明为一个形式参数, 都将导致原生的arguments
不会被创建。
arguments
变量不是一个数组(Array
)。 尽管在语法上它有数组相关的属性 length
,但它不从 Array.prototype
继承,实际上它是一个对象(Object
)。
函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。
如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。
如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象。
当一个函数被call和apply调用时,this的值就取传入的对象的值。
在全局环境下,this永远是window。
执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。其实这是一个压栈出栈的过程——执行上下文栈。
javascript除了全局作用域之外,只有函数可以创建的作用域。作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
作用域在函数定义时就已经确定了。而不是在函数调用时确定。
当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。
闭包的用处:一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。