嵌套函数和闭包

1. 嵌套函数:
在函数体内定义另一个(多个)函数. 函数 scalePt2( ) 和 getBevelVec( ) 定义在 addShape( ) 的内部, 在 addShape( ) 的内部可以调用它们, 在外部则不行. function addShape( shape ) { ... ... function scalePt2( pt, vec, size ) {} function getBevelVec( inPt, inPrev, inNext ) {} ... ... vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); ... ... contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); }

2. 嵌套函数的变量作用域:
全局变量在整体代码体内都可访问. var n = 0; function counter(){ n++; } counter(); console.log( n ); // 1 局部变量在所定义的函数内可访问, 但在函数外部不可访问. function counter2(){ var n = 0; n++; } counter2(); console.log( n ); // undefined 在内层函数 increase( ) 里也可访问在外部定义的 n, 并且外层函数最后通过 return increase( ) 来输出, 相对于从外部访问局部变量的值. function counter3(){ var n = 0; function increase(){ n++; return n; } return increase(); } console.log( n ); // undefined console.log( counter3() ); // 1

3. 闭包: 函数对象的内部状态不仅包含代码逻辑, 还包含当前的作用域链. 当一个函数嵌套了另一函数, 被嵌套的函数对象 作为返回值时, 外层函数所指向的作用域链和定义时的并不相同. 闭包是把函数体内的变量都保存起来, 并把这些局部变量作为私有状态.

你会想, 如果只需使局部变量自增, 按照下面的 counter4( ) 的写法也可以. 但是, 这个函数一旦执行完毕, 它便会在作用域链中销毁该函数对象, 也就被垃圾回收. function counter4(){ var n = 0; n++; return n; } var c = counter4(); console.log( c ); // 1 console.log( c ); // 1 而如果使用嵌套函数, 并将它作为返回值返回或储存在某处的属性里, 这时就会有一个外部引用指向这个嵌套的函数, 它就不会被当作垃圾回收. 如 counter5( ), n可以不断的自增, 也可以清零. function counter5(){ var n = 0; return { increase: function(){ n++; return n; }, reset: function(){ return n = 0 } } } var c2 = counter5(); console.log( c2.increase() ); // 1 console.log( c2.increase() ); // 2 console.log( c2.increase() ); // 3 console.log( c2.reset() ); // 0 比较一下: function setNumber(){ var arr = []; for(var i = 0; i < 10; i++){ arr[i] = i; } console.log( arr[5] ); // 5 return arr; } function setNumber2(){ var arr = []; for(var i = 0; i < 10; i++){ arr[i] = function(){ return i; }; } console.log( arr[5]() ); // 10 return arr; }

4. Three.js里的闭包 raycast: ( function () { function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {} function checkBufferGeometryIntersection( object, material, raycaster, ray, position, uv, a, b, c ) {} return function raycast( raycaster, intersects ) { if ( geometry.isBufferGeometry ) { if ( index !== null ) { if ( Array.isArray( material ) ) { } else { } } else if ( position !== undefined ) { if ( Array.isArray( material ) ) { } else { } } } else if ( geometry.isGeometry ) { for ( var f = 0, fl = faces.length; f < fl; f ++ ) { if ( faceMaterial === undefined ) {} if ( faceMaterial.morphTargets === true ) {} if ( intersection ) {} } } }; }() )