您的位置:首页 > Web前端 > JavaScript

5js面向对象基础-闭包的概念及应用

2016-11-03 00:32 387 查看

5 js面向对象基础 - 闭包的概念及应用

闭包的概念

注意:预解析,变量声明,词法作用域,作用域链等知识的理解,对闭包的彻底理解起重要的作用。没有基础的建议先看上一篇 4 js面向对象基础 - 预解析,词法作用域,作用域链

字面意义:

闭 : 关闭,封闭

包 : 包裹, 打包

闭包的含义就是一个被包裹的隔离的空间

在 js 中, 什么是闭包 ?

在 js 中函数是一个具有变量作用域隔离特性的一个内存结构, 函数的内部内容外部无法访问,是一个被封闭的内容,即为一个闭包。

学习闭包, 在 js 中到底要解决什么问题

在 js 中闭包要解决的问题就是间接的访问到这个被隔离的数据.

function foo () {
var num = 123;
return num; // return 只是进行了 拷贝,外界还是没有真正的访问 num
}
var num = foo();


在外界想访问到 num 中的数据. 怎么做? =》 我们可以使用闭包的间接访问

闭包的间接访问

使用 return 数据不能直接访问原来的数据, 那么可以考虑利用函数的返回访问原始数据

function foo() {
var num = Math.random();    // 建随机数

function fuc() {
return num;  /* 原始数据 num */
}
return fuc; // 把函数返回
}
var fn = foo(); // 获取函数引用, 返回的函数功能是 获取 foo函数中 变量 num

var num1 = fn();
console.log( num1 );    // 0.4612285847198856
var num2 = fn();
console.log( num2 );    // 0.4612285847198856


fn 中存储的是 foo 里面定义的函数的 引用,可以使用 fn 来调用,根据词法作用域,返回了 foo 中的 num,间接的访问了闭包中的数据

闭包的应用

闭包的应用-利用闭包实现私有数据

function foo(){
var num1 = 123, num2 = 456;
return {
get_num1: function() {
return num1;
},
set_num1: function( value ) {
num1 = value;
},
get_num2: function() {
return num2;
}
};
}
var o = foo();
console.log( o.get_num1() );    // 获取 num1  123
o.set_num1(789);    // 修改 num1
console.log( o.get_num1() );    // 再次获取 num1    789
console.log( o.get_num2() );    // 获取 num2 456


从上可以看到外部对 num1 可读可写,对 num2 可读不可写

函数允许返回一个对象, 那么该对象可以提供数据访问方法,但是数据存储在闭包中, 来达到私有的目的

function createPerson ( name, age, gender ) {
var hasChangeGender = false;
return {
get_Name: function () {
return name;
}, set_Name: function ( value ) {
name = value;
}, get_Age: function () {
return age;
}, get_Gender: function () {
return gender;
}, set_Gender: function ( value ) {
if ( hasChangeGender == false ) {
gender = value;
hasChangeGender = true;
} else {
throw new Error( '已经改变过一次性别了, 不能再修改了' );
}
}
};
}

var p1 = createPerson( '张三', 19, '男' );

console.log( 'p1.name = ' + p1.get_Name() );    // 张三
console.log( 'p1.age = ' + p1.get_Age() );  // 19
console.log( 'p1.gender = ' + p1.get_Gender() );    // 男

p1.set_Name( '王二' );
p1.set_Gender( '女' );

console.log( 'p1.name = ' + p1.get_Name() );    // 王二
console.log( 'p1.age = ' + p1.get_Age() );  // 19
console.log( 'p1.gender = ' + p1.get_Gender() );    // 女

p1.set_Name( '王三' );
p1.set_Gender( '男' );   // Uncaught Error: 已经改变过一次性别了, 不能再修改了


总结:闭包实现各种特性, 其根本的核心内容只有两个

带有私有数据的 函数

function foo () {
var num = 123;
return function () {
// 可以访问 num
}
}
var func = foo();
// 称 func 是一个 带有私有数据的 函数
// 称 func 带有缓存


**称 func 是一个 带有私有数据的 函数**

**称 func 带有缓存**


带有私有数据的 对象

闭包的应用-沙箱模式

沙箱就是一个隔离的执行环境。js 中沙箱就是一个自调用函数,写在这个函数中变量,不会被外面的内容所影响,这样的话,使得我们的数据更加的安全,代码也能正常使用和运行。

在 js 中 什么情况需要使用沙箱?

function Person() {....}
var p = Person. ...
Person.prototype = ...
...


定义变量越多, 会怎样? -> 出现冲突的可能性越大

而有时代码中 为了使得代码更加简洁, 会引入很多变量, 我们通过沙箱就可以解决这样的问题。

(function () {
// 沙箱模式
// 所有的代码写在这里
})();


闭包的应用-模拟onload事件的追加和移除

onload 事件的追加 addEvent

/* 新建 jepson 对象来接收 沙箱返回的对象 */
var jepson = ( function() {
/* 新建 私有变量 */
var arr = [];
/* window onload 以后,遍历执行 arr数组中的全部方法 */
window.onload = function() {
for( var i = 0; i < arr.length; i++ ) {
if( typeof arr[ i ] == "function" )  arr[ i ]();
}
};
/* 返回对象,对象中存放 addEvent方法,用以追加 onload执行事件 */
return {
addEvent: function( fn ) {
arr.push( fn );
}
}
})();
jepson.addEvent( function() {
console.log( " 我被追加载 onload 执行了 1 " );
});


onload 事件的移除 removeEvent

/* 新建 jepson 对象来接收 沙箱返回的对象 */
var jepson = ( function() {
/* 新建 私有变量 */
var arr = [];

window.onload = function() {
for( var i = 0; i < arr.length; i++ ) {
if( typeof arr[ i ] == "function" )  arr[ i ]();
}
};
return {
addEvent: function( fn ) {      /* 追加 */
arr.push( fn );
},
removeEvent: function( fn ) {   /* 移除 */
// 遍历 arr, 发现相同的就删除
for( var i = 0; i < arr.length; i++ ) {
if ( fn == arr[ i ] )   arr.splice( i, 1 )
}
}
}
})();
/* 要删除必须传引用,所以用变量存一下引用 */
var f1 = function() {
console.log( " 我被追加载 onload 执行了 1 " );
};
var f2 = function() {
console.log( " 我被追加载 onload 执行了 2 " );
};
var f3 = function() {
console.log( " 我被追加载 onload 执行了 3 " );
};
jepson.addEvent( f1 );
jepson.addEvent( f2 );
jepson.addEvent( f3 );
jepson.removeEvent( f1 );   // f1 被移除了


这里只会输出,f1已被移除

我被追加载 onload 执行了 2

我被追加载 onload 执行了 3

补充: setInterval函数调用时,也尽量用变量保存引用,不然每隔一秒,都会新建一个不同的函数来调用,大大的占用了内存,消耗了性能。

var f1 = function() { ... };
setInterval( f1, 1000 );


闭包的应用-模拟一个缓存结构

cache 对象, 可以使用 cache[ key ] = value 存储数据, cache[ key ] 获得数据

当 cache 里面的数据达到 1024 条, 将最早放进去的数据移除。

而模拟时, cache = {} 可以存取数据, 但是不能限定数据的长度

我们需要限定数据,就是在加入数据的时候判断,是否已经超过尺寸,来决定是否移除

可以将 cache 做成函数,那么添加数据时,使用 cache( key, value ),而且函数本身也是对象

function cache ( key, value ) {
// 可以在这里加上限定长度的代码
cache[ key ] = value;
}

cache( 'attr', function ( node, attr ) {
return node.getAttribute( 'name' ) == attr;
} );
cache( '__name__', '张三' );


由于需要记录键的数量. 并且需要记录添加数据的先后顺序. 所有首先考虑有序的数组.因此需要让 cache 函数带有缓存功能

搭建结构

var cache = (function () {
var data = [];  // 新建 data数组,用来保存 键

function cache ( key, value ) {
// 准备做判断, 如果超出范围, 则, 将最开始加入的 移除
cache[ key ] = value;
}
return cache;
})();
cache( 'age', 20 ); // 给 cache 添加 age 属性,值为 20


实现

var cache = (function () {
var data = [], max = 3;
function cache ( key, value ) {
// 做判断, 如果超出范围, 则, 将最开始加入的 移除
if ( data.length >= 3 ) {
// 需要先移除
var temp = data.shift();    // 移除第一项并获取 key
delete cache[ temp ];   // 删除 cache对象中的 temp 属性
}

data.push( key );
console.log( data );

cache[ key ] = value;
}
return cache;
})();

cache( 'name1', '张三' );
cache( 'name2', '李四' );
cache( 'name3', '王五' );
cache( 'name4', '找钱' );
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息