1、一切(引用类型)都是对象,对象是属性的集合。
2、函数是一种对象,但是函数却不像数组一样——你可以说数组是对象的一种,因为数组就像是对象的一个子集一样。但是函数与对象之间,却不仅仅是一种包含和被包含的关系,函数和对象之间的关系比较复杂,甚至有一点鸡生蛋蛋生鸡的逻辑。
function Fn() {this.name = '王福朋';this.year = 1988;} var fn1 = new Fn(); var obj = { a: 10, b: 20 };等价于var obj = new Object(); obj.a = 10;obj.b = 20; var arr = [5, 'x', true];等价于var arr = new Array(); arr[0] = 5;arr[1] = 'x';arr[2] = true;
对象是函数创建的,而函数却又是一种对象。
3、每个函数都有一个属性叫做prototype。这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身。
4、每个对象都有一个__proto__属性,指向创建该对象的函数的prototype。这个__proto__是一个隐藏的属性,JavaScript不希望开发者用到这个属性值,有的低版本浏览器甚至不支持这个属性值。obj.__proto__=== Object.prototype
二、闭包
1、闭包的基本概念
有权访问另一个函数作用域中的变量的函数。简单理解为“定义在一个函数内部的函数”例如:
function createComparisonFunction(propertyName){ return function(object1,object2){//匿名函数 value1=object1[propertyName]; value2=object2[propertyName]; if(value1<value2){ return -1; }else if(value1>value2){ return 1; }else{ return 0; } } } //创建函数 var compareNames=createComparisonFunction("name"); //调用函数 var result=compareNames({name:"Nicolas"},{name:"Greg"}); //解除对匿名函数的引用(以便释放内存) compareNames=null; alert(result);//1
(1).好处:保护函数内的变量安全,加强了封装性;在内存中维持一个变量(缓存);匿名自执行函数;模拟面向对象编程。
(2).应用场景:使用闭包代替全局变量;函数外或在其他函数中访问某一函数内部的参数;包装相关功能;为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点;
(3).缺点:常驻内存,会增大内存使用量,使用不当很容易造成内存泄露,更重要的是,对闭包的使用不当会造成无效内存的产生。
只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间。父函数定义的变量在子函数的作用域链中,子函数没有被销毁,其作用域链中所有变量和函数就会被维护,不会被销毁。
2、闭包的用途
闭包有两个用途,一是方便实现嵌套的回调函数,二是隐藏对象的细节。
对于前者,NodeJS的编程风格已经可以说明问题,后者对于函数内部的局部变量外部是不可见的,但可以提供访问函数来访问和修改相应的局部变量,从而实现OO封装的意图。
(1)、简单的例子
首先从一个经典错误谈起,页面上有若干个div, 我们想给它们绑定一个onclick方法,于是有了下面的代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script language="javascript" src="https://www.jb51.net/jquery.js"></script> <script> $(document).ready(function() { var spans = $("#divTest span"); for (var i = 0; i < spans.length; i++) { spans[i].onclick = function() { alert(i); } } }); </script> <style> #divTest{ margin-top:30px; margin-bottom:40px; } span{ border-color:#3C0; border-style:solid; margin-bottom:5px; padding:10px 30px 10px 30px; border-radius:10px } </style> <title>无标题文档</title> </head> <body> <div> <span>0</span> <span>1</span> <span>2</span> <span>3</span> </div> </body> </html>
很简单的功能可是却偏偏出错了,每次alert出的值都是4,简单的修改就好使了
$(document).ready(function() { var spans = $("#divTest span"); for (var i = 0; i < spans.length; i++) { (function(num){//匿名函数表达式 spans[i].onclick = function() { alert(num); } })(i);//立即执行,并且把i的值传给num } });
上面代码在页面加载后就会执行,当i的值为4的时候,判断条件不成立,for循环执行完毕,但是因为每个span的onclick方法这时候为内部函数,所以i被闭包引用,内存不能被销毁,i的值会一直保持4,直到程序改变它或者所有的onclick函数销毁(主动把函数赋为null或者页面卸载)时才会被回收。这样每次我们点击span的时候,onclick函数会查找i的值(作用域链是引用方式),一查等于4,然后就alert给我们了。而第二种方式是使用了一个立即执行的函数又创建了一层闭包,函数声明放在括号内就变成了表达式,后面再加上括号括号就是调用了,这时候把i当参数传入,函数立即执行,num保存每次i的值。
(2)、内部函数
让我们从一些基础的知识谈起,首先了解一下内部函数。内部函数就是定义在另一个函数中的函数。例如:
function outerFn () { function innerFn () {} }