闭包
闭包从字面意思理解就是闭合, 包起来.
所谓闭包就是一个闭合包起来的空间
在js中, 根据作用域规则,
只允许函数访问外部的数据, 外部无法访问函数内部的数据,
闭包要解决什么问题? 既然js一个函数就是一个闭包 我们为什么要学习闭包?
闭包内的数据不允许外界访问
要解决的问题就是间接访问该数据
我们要学习的就是如何间接的在外部访问内部数据 这个是有弊端的 要慎用
闭包的弊端: 打破了封装
容易产生内存泄漏(内存溢出)
闭包的使用步骤:
在下级作用域 利用return 返回一个内部函数
该函数内部的代码 对外暴露 下级作用域的变量
外部接收到该函数 调用该函数 就接收到了 这个函数对外暴露的变量
这个变量也就是下级作用域的变量
从而实现了闭包 间接的访问内部数据
function wai() {
var obj={
name:"ps4游戏机",
age:1
};
function show() {
return obj;
}
return show;
}
var show=wai();
var o1=show();
var o2=show();
var o3=show();
console.log(o1==o2);
闭包返回多条数据
function f() {
var obj={
name:"小强",
age:20
}
var per={
name:"茄子",
age:1
}
return {
obj_show:function () {
return obj;
},
per_show:function () {
return per;
}
}
}
var show=f();
var obj=show.obj_show();
console.log(obj);
var per=show.per_show();
console.log(per);
闭包在开放中常用
//用闭包构成一个完整的\存储空间 对外暴露 指定的操作方法 没暴露的 不能用
function func() {
var data={
name:"默认值",
age:0
}
return{
setName:function(name){
data.name=name;
},
getName:function () {
return data.name;
},
setAge:function (age) {
data.age=age;
},
getAge:function () {
return data.age;
},
addAttr:function (attrName,attrValue) {
data[attrName]=attrValue;
return data[attrName];
}
}
}
// 这个data只能调用func一次 data只能有一个 然后反复调用data里面的方法就好
/* var control=func();
console.log(control.getName());
control.setName("小砌墙")
console.log(control.getName());
console.log(control.addAttr("haha","吼吼"));*/
寄生式创建对象
构造函数的return语句补充说明:
* 构造函数中不需要return, 就会默认的return this
如果手动的添加return, 就相当于 return this
如果手动的添加return 基本类型; 无效, 还是保留原来 返回this
如果手动添加return null; 或return undefiend, 无效
如果手动添加return 对象类型; 那么原来创建的this就会被丢掉, 返回的是 return后面的对象
寄生式创建对象
function Person(name, age, gender){
// 这个o就是寄生式对象
var o = new Object();
o.name = name;
o.age = age;
o.gender = gender;
return o;
}
// var o=Person("小砌墙",16,"男"); //把Person函数 当做工厂函数调用
//用new 就是寄生创造函数
var per=new Person("小砌墙",16,"男");
console.log(per instanceof Person);//false
console.log(per instanceof Object);//true
console.log(per.name);
console.log(per.gender);
函数调用四种方式 第四种上下文调用 改变this指向
1.函数模式
函数名();
this在函数中表示全局对象,在浏览器中是window对象
2.方法模式
对象.方法名();
通过对象调用就是方法模式
这里面的this就是当前调用方法的对象
3.构造函数调用模式
var per=new Person();
this指向的是当前new的这个对象
4.函数的第四种调用模式:
上下文调用 可以改变函数内部的this指向问题
有两个方法 call和apply
函数名.apply(this将要指向的对象,[实参1,实参2,...实参n])
表示 调用这个函数 并把这个函数内部的this指向apply方法的第一个参数对象
function show(a,b) {
console.log(this,a+b);
console.log(this.num1+this.num2)
}
var o={
num1:1,
num2:2
};
show.apply(o,[1,3])
函数名.call(this将要指向的对象, 实参1,实参2...实参n)
function show(a,b) {
console.log(this,a+b);
console.log(this.num1+this.num2)
}
var o={
num1:3,
num2:2
};
show.call(o,1,5)
bind 是预处理this,不会直接调用
var 新函数名=函数名.bind(this将要指向的对象,参数1,参数2......);
新函数名();
或者
var 新函数名=函数名.bind(this将要指向的对象)
新函数名(,参数1,参数2......);
function show(a,b) {
console.log(this,a+b);
console.log(this.num1+this.num2)
}
var o={
num1:3,
num2:2
};
//第一种
/* var newShow=show.bind(o,1,2);
newShow();*/
//第二种
var newShow=show.bind(o);
newShow(1,2)
call和apply的区别:
共同作用是能够调用函数 并改变函数内部的this默认指向问题
区别在于 apply是用数组匹配实参
call是用 多个参数匹配实参
bind不会直接调用函数 是预处理this 参数可以再预处理的时候填写 ,也可以在调用的时候填写
如果传入的是一个对象, 那么就相当于设置该函数中的 this 为参数(看上边代码)
如果不传入参数, 或传入 null. undefiend 等, 那么相当于 this 默认为 window NaN为Number
//例子1
var o={
name:"抢到"
}
function show() {
console.log(this);
}
show.apply(null); //函数内部的this指向window
//例子2
var per={
name:"我是小砌墙",
age:16,
show:function () {
console.log("我是show方法--->"+this.name,this);
}
}
var o={
name:"强盗",
age:88
}
per.show.apply(o)
per.show.apply(null);; //this变成window
如果传入的是基本类型, 那么 this 就是基本类型对应的包装类型的引用
function f() {
console.log(this)
}
f.apply(99)//Number
上下文调用模式的应用---------------------------------
1. 将伪数组转换成真正的数组(传统做法)
var a={};
a[0]="a";
a[1]="b";
a[2]="c";
a.length="3";
a.name="xx";
// 传统方法
/*var arr=[];
for (var k in a){
if(Number(k)||Number(k)==0){
arr.push(a[k]);
}
}
console.log(arr);*/
//用上下文调用模式 去借函数 转换伪数组
// 利用apply实现 伪数组转换成 真数组
var newAr=Array.prototype.concat.apply([],a);
console.log(newAr)
2.
function Person ( name, age, gender ) {
this.name = name;
this.age = age;
this.gender = gender;
}
function Student(name,age,gender,skill) {
//调用Person方法 把Person方法里面的this指向当前 Student的this 也就是Student对象
Person.call(this,name,age,gender);
this.skill=skill;
}
var stu=new Student("小书包",16,"男","LOL");
console.log(stu);
立即执行函数(IIFE)
(Immediately Invoked Function Expression)
函数本身没有名字 执行到此处代码 函数就会调用运行
运行过后 函数自动别销毁 有点类似于 一次性函数的感觉
优点:
执行一次就自动销毁
没有名字 不空占内存空间
立即执行函数的格式:
第一种(不常用):
+function(形参){
}(实参);
+function (a,b) {
console.log("我是立即执行函数");
}(88,66)
第二种:
(function(形参){
}(实参))
(function (a) {
console.log("我是立即执行函数"+a);
}(80))
第三种格式(最常用)
(function(形参){
})(实参)
(function (a) {
console.log("我是立即执行函数"+a);
})(99)
实际开发中
(function (document,window,$) {
})(document,window,jQuery)
立即执行解决异步函数数据丢失
for(var i=0;i<10;i++){
var f=(function (num) {
return function () {
console.log(num);
}
})(i)
setTimeout(f,0)
}
缓存
什么是缓存:
能够提高访问速度的一种存储方式
缓存的目的:就是为了提高访问速度
为什么要用缓存:
因为可以提高访问速度
如果我们每次访问 都从数据来源处要数据 那么 速度相对很慢
我们学习过的缓存功能:
cookie 专门用来做缓存的
session和local是偏向一些临时数据车存储于传递的
function data() {
var cache={
}
// 每一个key同时在数组里面存储一份就可以了
var arr=[];
return function (key,value) {
if(value){
// 有value的时候必然有key
//表示新增或者修改
//判断是否是新增或者修改
if(arr.indexOf(key)!=-1 && cache[key]!=undefined){
// 修改
cache[key]=value; //改成新的值
// 数组得更新一下 把新改的这个key变成数组最后一位
arr.push(arr.splice(arr.indexOf(key),1));
}else{
// 新增
// 新增之前 先判断 缓存满没满
if(arr.length>2){
// 表示已经到3个了 需要删除第一个 然后再存新的
/*// 删除数组中这第一个key
var firstKey= arr.shift()
// 再拿这第一个key 去缓存对象中是删除这个属性
delete cache[firstKey];*/
// 简化写法:
delete cache[arr.shift()];
}
// 存储新的
arr.push(key);
cache[key]=value;
}
return cache[key];
}else{
return cache[key];
}
}
}
var cache=data();
console.log(cache("name","小砌墙"));
console.log(cache("age",16));
console.log(cache("location","文化大厦"));
用delete关键字删除一个对象属性
注意: 无法删除 该对象的原型属性
只能删除属于对象本身的属性
可以删除隐式全局变量,但不可已删除显示全局变量。
全局变量其实是global对象(window)的属性
var per={
name:"小砌墙",
age:16,
location:"文化大厦"
}
per.__proto__.color="黄色";
// console.log(per.location);
console.log(Object.keys(per));
console.log(per.color);
// 返回值 布尔值 表示是否删除成功
console.log(delete per.location);
console.log(delete per.color); //无法删除
// console.log(per.location);
沙箱
什么是沙箱?
火车站或者飞机场的防爆沙箱
360的隔离沙箱
这些沙箱都是起到 隔离作用
能够把一些指定的程序 封闭到一个箱子里面运行 不会影响到外面
这就是沙箱的作用
那么我们由这个 可以联想到 之前的闭包 闭合起来的空间
但是不够 因为闭包不会自动运行
我们需要一个闭合空间 不对外界直接操作
这就是 jQuery的封装原理 只对外暴露 $和jQuery 不直接操作外部数据
jQuery的沙箱模式就是 立即执行函数+闭包
(function (window,document) {
// 沙箱模式中 潜规则: 所有的变量声明 都要放到沙箱模式的第一行
var data="";
var cache="";
var itszt={
setStyle:{
},
setDom:{
},
getStyle:{
},
event:{
}
}
window.$=window.jQuery=itszt;
})(window,document)
callee caller
callee是函数中arguments对象的一个属性
返回当前函数本身 也是代表当前函数 可以直接调用的
一旦在函数中 用callee调用 那就变成了 递归(函数自己调用自己)
递归要小心使用 必须有结束条件 否则就报错 调用死循环
caller: 是当前函数名调用caller
如果当前函数的调用 放在了全局
那么 函数名.caller返回null
如果当前函数的调用 放到了 另一个函数里面
那么函数名.caller返回当前调用代码的外层函数本身
可以理解为一种上下文函数环境的获取
总结:
当前方法在哪调用 就返回当前代码所在的外部函数
如果外部没有函数 返回null
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
/* function show() {
console.log("我是show函数");
console.log(arguments.callee);
console.log(arguments.callee==show);//true
}
show();*/
function demo() {
console.log(demo.caller);
}
function test() {
function qq() {
demo();
}
qq();
}
test();
// demo();
</script>
</body>
</html>

被折叠的 条评论
为什么被折叠?



