13-函数的四种调用模式与this
方法调用模式
方法调用模式里的this指的是new出来的新对象
<script>
function Dog(dogname)
{
this.name=dogname;
this.age=2;
this.run=function()
{
console.log(this.name+' is runnimg..');
}
}
//使用构造函数创建一个Dog类型的实例 d
var d=new Dog('pink');
d.run(); //调用d对象的run方法这就是方法调用模式。
//this<=>d
</script>
如果函数当做构造函数调用,并没有返回任何数据的时候,默认返回新对象(this)。
在方法调用模式中,方法内部的this指向当前调用者的对象 d。
构造函数(构造器)调用模式
构造函数调用模式里的this指的是new出来的新对象
构造函数内
如果return的是简单类型直接忽略
如果return的是引用类型把return的值赋值给cat(新对象),之前this定义的全部作废。
function Cat()
{
//1.创建一个空对象cat
//2.cat=>this
this.name='cat';
this.age=2;
this.run=function()
{
console.log(this.name+" is running..");
}
// return 0;
//如果return的是简单类型直接忽略
return{
//如果return的是引用类型把return的值赋值给cat(新对象),之前this定义的全部作废
age:3,
run:function()
{
console.log(this.name+' return running');
//如果后面没有定义name这里会显示undefined+ ' return running'
}
}
}
var cat=new Cat(); //构造器调用模式
cat.name='lucy';
cat.run(); //因为前面定义了name,所以会显示 Lucy return running
函数调用模式
function f1( )
{
this.a=19; //这里this指的是window 所以a就变成了全局变量,所以下面是true
console.log('a' in window);
}
f1(); //直接调用函数,函数调用模式
函数调用模式里的this指的是window
注意!严格模式下this指的是undefined
apply/call借用方法调用模式
function sum(a,b)
{
console.log(this);
return a+b;
}
var t={
name:"name"
}
sum(1,2); //=>window
//执行并赋值,赋的值是return出来的值
var m= sum.call(t,2,3); //=>{name:"name"}
console.log(m); //5
var j=sum.apply(t,[3,4]); //=>{name:"name"}
console.log(j); //7
sum.call(2,1,2); //Number{2}
sum.call(null,1,2); //=>window
使用apply和call借用方法调用模式,函数的第一个参数就是this的指向,后面的参数是传入函数的参数
call(this的指向,参数1,参数2)
apply(this的指向,[参数1,参数2]) apply传入的参数是一个数组
this的指向
- 如果是null、undefined =>window
- 如果是number、string、Boolean转成对应的包装类型
练习
//1.定义按钮类,要求按钮类的构造函数可以接受参数初始化按钮的宽度、高度、坐标xy
function Btn(width,height,px,py)
{
this.width=width;
this.height=height;
this.px=px;
this.py=py;
}
var btn=new Btn(100,100,30,50);
//2.借用Math的min方法实现求数组[2,9,33]中的最小值
var a=[2,9,33];
var m= Math.min.apply(null,a);
console.log(m);
//3.把类数组(伪数组)转换成真正的数组。 var t = {}; t[0] = 1; t[1] = true; t.length = 2;
var t = {};
//方法1:
//var t=new Array();
t[0] = 1;
t[1] = true;
t.length = 2;
m=[1,2,3];
//方法2
//slice截取数组,后面两个参数是从索引a位置截取到索引b位置,没有参数的话就截取全部
//本来this指的是数组,现在this指的是t
var k= Array.prototype.slice.call(t); //Array.prototype也可以写成m
console.log(m);
console.log(t);
console.log(k);
function Dog() //构造函数
{
console.log(this);
}
Dog();// 函数调用模式 window
var d = new Dog(); //构造函数调用模式 this == d
Dog.call(null); // 借用调用模式 window
输出结果:
14-js中函数没有重载
js中没有重载
重载:一组相同的函数名,有不同个数的参数,在使用时调用一个函数名,传入不同参数,根据你的参数个数,来决定使用不同的函数
注意!js中没有重载,重复定义的属性和方法会被后面的覆盖掉。
js中如何实现重载: https://www.cnblogs.com/pianruijie/p/7997914.html
没有重载的原因
所有的函数声明斗湖创建在VO或者AO上的一个属性。之后声明的函数会把前面VO或者AO中同名的属性覆盖。
arguments模拟函数重载
创建一个矩形的类型。构造函数接受一个参数,返回一个正方形,接受两个参数返回一个矩形。
<script>
function React()
{
//一个参数返回正方形
if(arguments.length==1)
{
this.width=arguments[0];
this.heigth=arguments[0];
}
//两个参数返回长方形
if(arguments.length>1)
{
this.width=arguments[0];
this.heigth=arguments[1];
}
//重写了object原型上的tostring方法
this.toString=function()
{
return 'width:'+this.width+'heigth'+this.heigth;
}
}
var r1=new React(10);
console.log(r1.toString());
var r2=new React(20,30);
console.log(r2.toString());
<script>
15-函数的递归调用
用递归求1-100的和
function resum(number)
{
if(number<=1) //使用递归的时候要定义一个出口
{
return 1;
}
return resum(number-1)+number;
}
console.log(resum(100));
arguments.callee:就是指向函数自身的变量,所以可以直接用它来代替函数名,在匿名函数中非常有用。但是在严格模式下会报错。
//函数表达式执行
var m= function fun ()
{
console.log("b");
}
m();
//fun();
var m= function fun(){ };
在fun函数内部,可以直接使用fun函数,但是在外部不能使用fun。
求斐波那契数列 f(n)=f(n-1)+f(n-2) n>3
function fibonacci (n)
{
if(n==0)
{
return 0;
}
if(n==1)
{
return 1;
}
return fibonacci(n-1)+fibonacci(n-2);
}
console.log(fibonacci(6)); //8
16-函数式编程
JavaScript不是纯函数式编程的语言,它只是支持函数式编程
函数是object类型的变种,在js中函数是一等公民。
一等公民: 函数和别的数据类型一样处于平等地位。函数可以赋值给变量,可以作为参数传入另一个参数,或者作为别的函数的返回值。
复习Arrary.prototype.sort 对数组中的元素进行排序
排序的算法是根据字符串比较大小的算法进行排序
不适合于数字的数组排序
sort方法实现数字排序——函数式编程
注意! t.sort排完序的结果直接赋值给t
<script>
var t=[2,14,87,9,45];
t.sort();
console.log(t); //只能进行字符串比较
//[14, 2, 45, 87, 9]
//sort方法实现数字排序——函数式编程
//注意t.sort排完序的结果直接赋值给t
t.sort(function(a,b){
if (a>b)
{
return 1;
}
if(a==b)
{
return 0;
}
if(a<b)
{
return a-b;
}
})
console.log(t);
//[2, 9, 14, 45, 87]
</script>
数组的map方法——遍历数组
- 返回新数组
- 方法接受一个回调函数,回调函数三个参数:当前项、索引、操作的数组
- 不影响原来的数组
注意!!!!map方法可以在回调函数中对遍历的数组进行处理,然后通过return生成一个新的数组返回给map(each方法不可以在回调函数类对遍历的数组进行处理)
//map方法
a=[1,6,3,7,12];
var b=a.map(function(value,index,array)
{
console.log(index,value);
return value*2;
});
console.log(b);
结果:
数组的forEach方法——遍历数组
i=[8,12,3,5];
var j=i.forEach(function(value,index,array){
console.log(index,value);
})
console.log(j);
结果:
注意!! each方法没有返回值。可以在函数内部对index和value进行操作。
函数是对象也是函数
function f (){};
f instanceof Object; // true
instanceof:f是否在object的原型链上,用来判断f是不是object
-
函数有内部属性:arguments,可以在函数内部使用。
-
函数的自身的属性:length,函数定义的形参个数。
-
另外我可以自定义函数的其他属性和方法。【一般用于全局变量、静态变量、公共存储等】
17-GC垃圾回收
垃圾回收机制
垃圾回收的时候js代码不会执行,两者之间是互斥的关系。
在C与C++等语言中,开发人员可以直接控制内存的申请和回收。但是在Java、C#、JavaScript语言中,变量的内存空间的申请和释放都由程序自己处理,开发人员不需要关心。
垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
变量进入环境时(如在函数内声明一个变量)这个变量就会被标记为进入环境
从逻辑上来说,永远释放不了进入环境的变量的内存。
变量离开环境时,会将其标记为离开环境被标记离开环境的变量在下一次垃圾回收启动的时候会被释放掉占用的内存空间。
Javascript引擎基础GC方案: mark and sweep(标记清除),即
(1)从根对象开始遍历所有可访问的对象。
(2)回收已不可访问的对象。
- IE6的垃圾回收是根据内存分配量运行的,当环境中存在256个变量、4096个对象、64k的字符串任意一种情况的时候就会触发垃圾回收器工作。bug!!!
- IE7中做了调整,触发条件不再是固定的,而是动态修改的,初始值和IE6相同,如果垃圾回收器回收的内存分配量低于程序占用内存的15%,说明大部分内存不可被回收,设的垃圾回收触发条件过于敏感,这时候把临界条件翻倍,如果回收的内存高于85%,说明大部分内存早就该清理了,这时候把触发条件置回
<script>
var m=2;var n=4; //将m、n、add()标记为进入环境
add(m,n); //add()函数执行时将a、b、c标记为进入环境
console.log("1"); //add()函数执行完毕,将a、b、c标记为离开环境等待垃圾回收
function add(a,b)
{
a++;
var c=a+b;
return c;
}
</script>
垃圾回收应用
var a=[1,45,66,2,3];
a.forEach(function(value,index){
console.log(value);
})
//用完之后要进行垃圾回收
//a=null; 这种方法将a的类型改变了,不可取
//a=[]; 虽然没有改变a的类型但是在堆中又创建了新数组,原来的东西还在内存空间中等待垃圾回收
a.length=0; //直接清空数组,而且a的类型不变
var t={}; //var 声明的对象没有块级作用域
for(var i=0;i<5;i++)
{
//var t={}; 这样创建了5个t,不可取,可以将此条语句放在外面
t.age=20;
t.name='aaa';
console.log(t);
}
对象尽量复用,尤其是在循环等地方出现创建新对象,能复用就复用。不用的对象,尽可能设置为null,尽快被垃圾回收掉。
在循环中的函数表达式,能复用最好放到循环外面。