(三)Javascript从立即执行函数至包装类(含闭包问题)

(三)Javascript从立即执行函数至包装类(含闭包)

立即执行函数
定义
- 此类函数没有声明,在一次执行过后即释放,适合做初始化工作。
//立即执行函数(只用一次,声明的话浪费空间)
//针对初始化功能的函数,形式如下:
(function abc(){
	var a = 123;
	var b = 234;
	cconsole.log(a+b)
}())
//在控制台中输入abc,会显示报错,abc is not undefined

//前后两个括号的作用,前面是形参,后面是实参
var num = (function(a,b,c){
    var d = a + b + c *2 - 2;
    return d;
}(1,2,3));

写法
//(function (){}());W3C建议第一种
//(function (){})();
//只有表达式才能被执行符号"()"执行,如何转为表示式?(+,-,=,括号里面转化成表达式)
function test(){
	......
}();
//这里语法上有错误,因为执行符号前面那一块不叫表达式,叫函数声明

var test = function(){
	console.log("a");
} ();
//这里可以执行,因为赋值号后面是函数表达式
//注意 : 能被执行符号执行的表达式,它的名字会被自动忽略
+ function test(){
	console.log("a");
}();
//这里被执行了,出现a。test的名字被忽略,显示EER:test is not defined


var test =(function(){
	console.log("a");
}());
//这里控制台会出现a,在控制台中输入test,它的值是undefined,他的类型是undefined

//一道阿里巴巴笔试题
function test(a,b,c,d){
	console.log(a + b + c +d);
}(1,2,3,4);

//这里因为(1,2,3,4);并没有被认为是执行符,所以被理解成下面:
function test(a,b,c,d){
	console.log(a + b + c +d);
}//这里被理解成了声明
(1,2,3,4);//这里是一个运算表达式(含逗号运算符),但其实没有意义


/*
在控制台输入test,得到 function test(a,b,c,d){
	console.log(a + b + c +d);
}
*/
    
闭包
定义

当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄漏。

作用
  • 实现公有变量

    • eg:函数累加器

    • function add(){
      	var count = 0;
      	function demo(){
      		count++;
      		console.log(count);
      	}
      	return demo;
      }
      var counter = add();
      counter();
      counter();
      counter();
      
  • 可以做缓存(存储结构)

    • eg:eater

    • function test(){
      	var num = 100;
      	function a(){
      		num++;
      		console.log(num);
      	}
      	functon b(){
      		num--;
      		console.log(num);
      	}
      	return [a,b];
      }
      var myArr = test();
      myArr[0]();
      myArr[1]();
      
      //得到结果:101 100
      
      
      
      function eater(){
          var food = "";
          var obj = {
              eat : function(){
                  console.log("i am eationg" + food);
                  food = ""; 
              },
              push : function(myFood){
                  food = myFood;
              }
          }
          return obj;
      }
      var eater1 = eater();
      eater1.push("banana");
      eater1.eat();
      
  • 可以实现封装,属性私有化(后期补充)

    • eg:Person();
  • 模式化开发,防止污染全局变量(后期补充)

一个闭包问题
//想要利用闭包依次打印出0~9
function test(){
	var arr = [];
	for(var i = 0; i < 10; i++){
		arr[i]=function (){
		document.write(i + " ");
		}
	}
	return arr;
}

var myArr = test();
for(var j = 0; j < 10;j++){
    myArr[j]();
}

/* 
这里打印出了10个10
首先这里是闭包,此道题的解题关键在于在函数体赋给arr[i]的时候,函数题里面的内容实际上是不被考虑的,当执行函数的时候,在arr[j]的作用域链中查找变量,即在作用域链第1位testAO中获取i,而i的值在循环的时候从0变到了10。

因此此处的关键就在于在将函数体赋给arr[i]的时候,是不考虑函数体内的内容的,所以也不会将i在循环中的值赋给函数体里面的document.write(i)。执行的时候才需要考虑,执行的时候才打开函数体内部。

所以myArr中的十个元素都是function(){
document.write(i + " ");
}
而不是function(){
document.write(固定的数 + " ");
}
*/
唯一解决方法(10对1AO转为1对1AO)
//利用立即执行函数
//①执行出数值再赋给arr,但这里显然脱离了闭包的思想
function test(){
	var arr = [];
	for(var i = 0; i < 10; i++){
		arr[i]=(function (){
		document.write(i + " ");
		})()
	}
	return arr;
}

var myArr = test();
for(var j = 0; j < 10;j++){
    myArr[j]();
}
//②从10对1个AO的关系变为1对1的关系
function test(){
	var arr = [];
	for(var i = 0; i < 10; i++){
		(function(j){
            arr[j] = function(){
                document.write(j + " ");
            }
        }(i));
	}
	return arr;
}

var myArr = test();
for(var j = 0; j < 10;j++){
    myArr[j]();
}

/*
这里重点关注第20~25行代码,解题关键:十个立即执行函数互不相同,影响它子函数被声明时所攥住的AO
第一次:i=0是实参传入立即执行函数中的j,这时是预编译四部曲的第三步,实参形参统一,j=0存入了AO中,之后赋给arr[0]的函数被声明(注意:声明的时候函数体内容被折叠,不考虑函数体中的内容)在赋给arr[0]的函数被声明的时候,这个函数攥住的AO,即作用域链第0位中含有j=0,之后立即执行函数结束。
到了第二次循环,又是一个新的立即执行函数,步骤与上面相同,但赋给arr[1]的函数的作用域链第0位中j=1(因为是一个新的立即执行函数,相当于开了一个新AO,所以说对应的房间不同)
*/
  • 可以实现封装,属性私有化。(需要联系到对象)

    • eg:Person();
  • 模块化开发,防止污染全局变量(后期)

对象
1. 属性的增删改查
var mrDeng = {
    name : "MrDeng",
    age : 40,
    sex : "male",
    health : 100,
    smoke : function(){
        console.log("I am smoking ! cool!!!");
        this.health--;
        //这里的this.health代表的就是medeng.health
    },
    drink : function(){
        console.log("I am drinking");
        this.health--;
    }
}

/* 属性的增加
	mrDeng.wife = "xiaoliu";
	在控制台中输入mrDeng,得到
	Obiect{	   
		age: 40,
		drink: ƒ (),
		health: 100,
		name: "MrDeng",
		sex: "male",
		smoke: ƒ (),
		wife: "xiaoliu"
	}
	
	属性的更改
	mrDeng.sex = "female"
	
	属性的删除
	delete mrDeng.name;
	
注意:访问不存在的属性,不会报错只会显示undefined
2. 对象的创建方式
/*
1.var obj = {} plainObject 对象字面体/对象直接量
2.构造函数(可批量生产对象,但相互独立,返回的值是对象)
	1) 系统自带的构造函数 new Object() Array() Number()
	2) 自定义构造函数 
*/
var obj = {
    obj.name = "abc",
	obj.sex = "male",
	obj.say = function(){
        
	}
}

var obj = new Object();
obj.name = "abc";
obj.sex = "male";
obj.say = function(){
    
}
//-----------------------------
function Car(){
    this.name = "BMW";
    this.height = "1400";
    this.lang = "4900";
    this.weight = 1000;
    this.health = 100;
    this.run = function(){
        this.health--;
    }
}
//这里是构造函数自定义写法,注意里面是等号和分号
//所以要用大驼峰式命明规则 TheFirstName 小驼峰:theFirstName
var car1 = new Car();
var car2 = new Car();
//car1 和 car2是独立的

//传递参数型构造函数
function Student(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
}
var s1 = new Student("zhangsan",18,"male");



//一个易错的地方
function Student(name,code){
    this.name = "zhangsan";
    this.code = "A001";
}
var s1 = new Student("lisi","A002");
console.log(s1.name+":"s1.code);
//这里得到的结果应该是"zhangsan":A001,而不是"lisi":"A002"
//因为函数里面并没有使用形参,this.name是属性不是参数
包装类
概念的引入
// 数字不一定都是原始值,原始值数字才属于原始值
//下面的也属于数字,但不是原始值
var num = new Number(123);
//这时候它属于对象123了
num.abc = "a";
console.log(num.abc);
//这里控制台得到"a"
console.log(num);
//这里控制台得到 Number{ abc:"a" , [[PrimitiveValue]]:123}
//值得注意的是,num虽然是对象123,但它仍然保持数字的特性
console.log(num*2);

//字符串类型对象
var str = new String("abcd");
//布尔型对象(先进行布尔运算,下面的意思是bol属性的初始值是true)
var bol = new Boolean(123);

注意:undefined和null没有属性,也就是说下面的情况会报错

console.log(null.abc);
console.log(undefined.abc);
包装类出现的过程
//首先从一个现象印出来
var str = "abcd";
str.abc = 'a';
//理论上原始值是不能赋予属性的,但这里并不报错
console(str.abc);
//控制台访问str.abc的结果是undefined

var num = 4;
//首先记住:原始值是坚决不可能有,但是为什么能调用呢?
//因为这里经历了一个叫做包装类的过程
num.len = 3;
/*隐式的发生下面的过程来弥补你的不足:
new Number(4).len=3
即先创建一个数字对象存原始数值,然后增加一个属性len=3; 但是执行完下一步就是销毁delete
*/
console.log(num.len);
//此处是访问num.len,所以又发生了 new Number(4).len,但是此处的len与上面不同,是undefined


//关于str.length
var str = "abcd";
str.length = 2;//发生包装类的过程
// new String("abcd").length = 2; delete
console.log(str);
// 这里得到的结果是"abcd"
console.log(str.length);
// 这里得到的结果是4,因为.length是字符串自带的属性
一道题
var str = "abc";
str += 1;
var test = typeof(str);
if(test.length == 6){
    test.sign = "typeof返回结果可能是String";
}
console.log(test.sign);
//这里得到的结果是undefined


练习
//百度一道题
var x = 1, y = z = 0;

function add(n){
	return n = n + 1;
}
y = add(x);
function add(n){
	return n = n + 3;
}
z = add(x);
//求x,y,z的值
//1,4,4

//该道题解题的关键是预编译四部曲和作用域
//首先应该是变量声明和函数声明提升
//第一个声明函数add被第二个声明的函数add覆盖,所以函数add的作用就只是返回n+3的值

//下面console.log是[1,2,3,4,5]的是
function foo(x){
    console.log(arguments)
    return x;
}
foo(1,2,3,4,5);
//这里是对的,因为arguments表示的是实参列表,尽管实参列表传入的只有一个,但实参列表属于外部

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值