javascript奇怪的知识点
闭包与高阶函数
闭包与高阶函数的特性属于函数式编程语言,虽然javascript是一门完整的面向对象的编程语言,但这完全不会影响他函数式的特点。
本次奇怪知识点就走进js的闭包与高阶函数
闭包的形成与变量的作用域密切相关,什么是作用域呢?其实就是指的变量的有效范围,当在函数中声明一个变量时,如果没有用到var,那么这个变量就会变成全局作用域,当我们在函数内部声明一个变量,在函数外面时访问不到的。
function foo(){
b; // b被挂载到了window下面
let a = 1;
alert(a) // 1
}
alert(a)
在外面访问,a是未声明的状态。
变量的生存周期
对于全局变量来说,就像上面的b,他是一直存在的,除非页面销毁,而对于局部变量比如:
function foo (){
let a = 1
}
foo() // 当foo调用结束,a将随函数一起销毁
但是有一种意外的情况:
let foo1 = function(){
let a = 2 ;
return function () {
a++;
alert( a )
}
};
let f = foo1();
f(); // 3
f() // 4
奇怪的知识点就在这里,难道a没有消失吗?
因为 f 指向了 foo1(),这是一个引用,f是可以调起函数产生时的环境,这时a就被保存在那个环境中,没被
销毁,由此局部变量的生命周期得到了延长。
闭包的经典应用
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
------------------
var aLi = document.getElementsByTagName("li");
for(let i = 0; i < aLi.length; i++){
aLi[i].onclick = function(){
console.log(i);
}
}
会发现始终在打印5,因为点击事件是异步的,当点击事件触发时,for循环已经走完了,当点击事件找到i的时候为5,for会把点击事件推送至事件队列,所以打印会执行5次,但每次都打印5.
闭包更多作用
闭包可以保留私有变量
var mult = (function(){
var cache = {} ;
return function()
{
var args = Array.prototype.join.call( arguments, ',');
if( args in cache ){
return cache[args]
}
var a = 1;
for( var i = 0 , k = arguments.length ; i < k,i ++ ){
a = a * arguments[i];
}
return cache[ args ] = a;
}
})()
高阶函数
高阶函数有这样的定义:
- 函数可以作为参数被传递
- 函数可以作为返回值被输出
作为参数被传递:
let appendiv = function( callback ) {
for(let i =0;i<100;i++){
var div = document.createElement('div');
div.innerHTML = i;
if( typeof(callback) === 'function' ){
callback( div )
}
}
};
appendiv( function( node){
node.style.display = 'none'
} )
作为返回值被输出:
我们来一个简单的例子:
let getsingle = function( fn){
let ret;
return function() {
return ret || ( ret = fn.apply( this,arguments ) )
}
};
let getsomething = getsingle( function(){
return document.createElement('script')
} );
let scri = getsomething()
let scrr = getsomething()
Object.is( scri,scrr ) //true
高阶函数实现AOP:
什么是AOP?,其主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,然后再通过动态插入的方式混入具体的业务模块中去。
我们来看一个例子:
我们通过扩展Function.prototype来进行一些好玩的事情:
Function.prototype.before = function( beforefn ) {
let _this = this;
return function(){
beforefn.apply(this. arguments ); // 修正this
return _this.apply( this,arguments )
}
};
Function.prototype.after= function( afterfn ) {
let _this = this;
return function(){
let ret = _this.apply( this , arguments )
afterfn .apply(this. arguments );
_this.apply( this,arguments )
return ret
}
};
let func = function(){
console.log( 2)
}
func = func .before( function(){
console.log(1)
} ) .after( function(){
console.log(3)
} )
高阶函数的其他应用:
- curring(函数柯里化)
直接上例子:
let month = 0;
let cost = function (money) {
month += money
};
cost( 1000 )
cost( 2000 )
cost( 3000 )
cost( 4000 )
alert( month )
这不是对函数柯里化的实现,只是表达柯里化的思想
下面是柯里化的实现:
let currying = function(fn){
let arg = [];
return function(){
if (arguments.length == 0){
return fn.apply(this,args);
}else{
[].push.apply(args,arguments);
return arguments.callee;
}
}
};
let cost = (function(){
let money = 0;
return function()
{
for( let i =0,k = arguments,length;i<k;i++ ){
money += arguments[i];
}
return money;
}
})();
let cost = currying( cost ) // 函数柯里化
cost( 100 );
cost( 200 );
cost() // 求值并输出:300
函数节流:
函数节流的方式有许多种,再次提供一种思路:
即将被调用的函数设置一个setimeout延迟一段时间执行,当该函数被再次触发时,如果延迟没结束,则不执行。
let throttle = function( fn , interval){
let _self = fn,timer,fistTime = true ;
return function () {
let args = arguments ,
_me = this ;
if( fistTime ){
_self.apply( _me ,args );
return fistTime = false ;
}
if( timer ){
return false ;
}
timer = setTimeout( function(){
clearTimeout( timer );
timer = null ;
_self.apply(_me,this);
},interval || 500);
};
};
window.onresize = throttle( function(){
console.log(1)
} ,500);
分时函数:
有时候,页面有大量的数据需要渲染,浏览器会因为此而崩溃,为了放置这种情况的发生,我们需要分时函数经行安全防范。
比如:有一个函数,让原本需要1秒内渲染1000次的操作改为200毫秒10次。
let timethunk = function(ary , fn , count){
let obj,t;
let len = arr.length;
let start = function(){
for( let i = 0; i < Math.min(count || 1,arr.length ); i++){
let obj =ary.shift();
fn( obj );
}
};
return function(){
t = setInterval( function(){
if(ary.length === 0){ //如果全部节点创建完毕
return clearInterval(t)
}
start();
},200)
}
};
let arr = [];
for( let i =0;i<=1000;i++){
ary.push(i)
};
let render = timethunk( ary,function(n){
let div = document.createElement('div');
div,innerHTML = n;
document.body.appendChild( div ) ;
},10);
render()