(三)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表示的是实参列表,尽管实参列表传入的只有一个,但实参列表属于外部