什么是闭包?
闭包就是能够读取其他函数内部变量的函数。在JS中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解为”定义在一个函数内部的函数”。
function fn() {
var b = 1; //闭包
function box() { // console.log(b); }
//返回一个函数,这个函数就是一个闭包
return box;
}
//res 就是 box 函数
var res = fn();
//2秒以后调用res函数,还是能打印b的值,闭包能让局部变量常驻内存
setTimeout(function() { res(); }, 2000);
闭包的特点
1:可以读取函数内部的变量。 变量的作用域无非就是两种:全局变量和局部变量。 JS语言的特殊之处,就在于函数内部可以直接读取全局变量。另一方面,函数外部自然无法读取函数内的局部变量
2:让这些变量的值始终保存在内存中。
闭包的应用场景
1:函数作为返回值。
2:函数作为参数被传递 回调函数 构造函数继承 本质是在子类中,调用父类构造函数,从而让子类拥有父类的属性和方法
call/apply方法
all和apply都是为了改变某个函数运行时的context即上下文而存在的,换句话说,就是为了改变函数内部this的指向。
二者作用完全一样,只是接受参数的方式不太一样。
fn.call(obj, arg1, arg2 [, argN]);
call方法是Function类中的方法 call方法的第一个参数的值赋值给类(即方法)中出现的this call方法的第二个参数开始依次赋值给类(即方法)所接受的参数
注意:调用函数的call或者apply方法相当于是调用函数
function box(a,b){ console.log(this,a,b) } var arr = [1111]; //使用arr改变的 box中this box.call(arr,1,3)
fn,.apply(obj, [arg1, arg2,…, argN]);
apply以数组的形式传参,call是以列表的形式 apply方法接受2个参数
A、第一个参数与call方法的第一个参数一样,即赋值给类(即方法)中出现的this
B、第二个参数为数组类型,这个数组中的每个元素依次赋值给类(即方法)所接受的参数
function box(a,b){ console.log(this,a,b) } var arr = [1111]; //使用apply改变的 box中this box.apply(arr,[2,4])
利用apply使用math的min和max 方法
var arr1 = [1,2,3,4,5,6,6,45,45]; console.log(Math.min.apply(null,arr1)); console.log(Math.max.apply(null,arr1));
rototype原型
大部分面向对象的编程语言,都以“类”(class)为基础,实现对象的继承。JavaScript 语言不是如此,它的对象继承以“原型对象”(prototype)为基础。
JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,可以定义在构造函数内部。
通过构造函数为实例对象定义属性,虽然很方便,但是有一个缺点。
同一个构造函数的多个实例之间,无法共享属性,从而造成对系统资源的浪费。 cat1和cat2是同一个构造函数的两个实例,它们都具有meow方法。由于meow方法是生成在每个实例对象上面,所以两个实例就生成了两次。也就是说,每新建一个实例,就会新建一个meow方法。这既没有必要,又浪费系统资源,因为所有meow方法都是同样的行为,完全应该共享。
prototype的概念
每一个构造函数都有一个prototype属性,这个属性会在生成实例的时候,成为实例对象的原型对象。
JavaScript 的每个对象都继承另一个对象,后者称为“原型”(prototype)对象。
一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。null也可以充当原型,区别在于它没有自己的原型对象。
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
上面代码中,构造函数Animal的prototype对象,就是实例对象cat1和cat2的原型对象。原型对象上添加一个color属性,结果,实例对象都继承了该属性。
原型对象的属性不是实例对象自身的属性。但是只要修改原型对象,变动就立刻会体现在所有实例对象上。
原型对象的作用,就是定义所有实例对象共享的属性和方法。
这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象 原型链 对象的属性和方法,有可能定义在自身,也有可能定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototype chain)。
比如,a对象是b对象的原型,b对象是c对象的原型,以此类推。
原型链”的作用是,读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。
如果直到最顶层的Object.prototype还是找不到,则返回undefined。
举例来说,如果让某个函数的prototype属性指向一个数组,就意味着该函数可以当作数组的构造函数,因为它生成的实例对象都可以通过prototype属性调用数组方法
var MyArray = function () { }; // MyArray.prototype = new Array(); var mine = new MyArray(); mine.push(1, 2, 3); mine.length //
上面代码中,mine是构造函数MyArray的实例对象,由于MyArray的prototype属性指向一个数组实例,使得mine可以调用数组方法(这些方法定义在数组实例的prototype对象上面)。
原型链继承就是利用这个原理 将一个构造函数的原型指向另一个构造函数的实例对象来实现继承。
function Box() { //Box构造 this.name = 'Lee'; } function Desk() { //Desk构造 this.age = 100; } Desk.prototype = new Box(); //Desc继承了Box,通过原型,形成链条 var desk = new Desk(); alert(desk.age); alert(desk.name); //得到被继承的属性
混合继承 结合使用原型链与构造函数继承
function Box(age) { this.name = 'Lee' this.age = age; } Box.prototype.run = function () { return this.name + this.age; }; function Desk(age) { Box.call(this, age); //构造函数继承继承了 name 和age 属性 } Desk.prototype = new Box(); //原型链继承 run方法 var desk = new Desk(100); alert(desk.run());