闭包
JavaScript中的函数会形成闭包,闭包是由函数以及创建该函数的词法环境组合形成。这个环境包含了这个闭包创建时所能访问的所有局部变量。
1、每次调用执行foo函数,将foo内部从头到尾执行一遍,打印10。
function foo(){
var a = 10;
function bar(){
console.log(a++);
}
bar();
}
foo(); // 10
foo(); // 10
2、返回函数体,在外部可访问到变量a,并在每次的基础上进行变化。
function foo(){
var a = 10;
return function bar(){
console.log(a++);
}
}
var baz = foo();
// 定义变量接收返回的函数体
baz(); // 10
baz(); // 11 在之前的基础上进行变化
闭包的优点
减少全局变量的污染,创建命名空间。
var yh = (function(){
var a = 10;
var b = 20;
function foo(){
console.log(a); // 10
}
function bar(){
console.log(b);
}
// 把函数内部的变量和函数当做变量属性和方法返回
// 可供外部调用
return {
foo,
a
}
})();
yh.foo();
console.log(yh.a); // 10
闭包的缺点
内存泄漏,一块内存空间既不能被使用也不能被释放。
JavaScript中的两大回收机制:标记清除、引用计数。
1、标记清除
function foo(){
var a = 10;//进入环境的标记
}
foo();//调用完成之后 a变量的标记置为退出环境的标记
2、引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。
var obj1 = {};
var obj2 = obj1; // 此时obj2引用了obj1,则obj1不能被回收
obj2 = null; // 此时obj1的引用次数为0,可回收
闭包的经典应用
//结合闭包实现点击每个li,打印对应的索引
var aLi = document.querySelectorAll("li");
for(var i = 0; i < aLi.length; i++){
(function(i){
aLi[i].onclick = function(){
console.log(i);
}
})(i);
}
闭包面试题
function fun(n, o) {
console.log(o);
return {
fun: function(m) {
return fun(m, n);
}
};
}
var a = fun(0); //传入参数n=0
// 此时没有第二个参数o,打印undefined
// a接收的返回值是对象{ fun:function(m){ return fun(m,0); } }
a.fun(1); // 传入参数1 打印第二个参数0
// 执行函数 function(m){ return fun(m,0); }
a.fun(2); // 传入参数2 打印第二个参数0
// 执行函数function(m){ return fun(m,0); }
a.fun(3); // 传入参数3 打印第二个参数0
// 执行函数function(m){ return fun(m,0); }
//var b = fun(0).fun(1).fun(2).fun(3);
var b=fun(0); // 此时无第二个参数,打印undefined
// b此时接收的返回值是对象 { fun:function(m){ return fun(m,0); } }
var c=fun(0).fun(1);
//打印undefined 0
var d=fun(0).fun(1).fun(2);
//打印undefined 0 1
var e=fun(0).fun(1).fun(2).fun(3);
//打印undefined 0 1 2
var f = fun(0).fun(1); // undefined 0
f.fun(2); // 1
f.fun(3); // 1
继承
1、构造函数继承
通过call()、apply()、bind()改变this指向
此方法只能继承构造函数内的属性和方法,不能继承原型对象上的属性和方法。
function Person(name,age){
this.name = name;
this.sayHello = function(){
console.log(this.name);
}
}
function Male(name,age){
/*this.name = name;
this.sayHello = function(){
console.log(this.name);
}*/
Person.call(this,name);
}
var male = new Male("john");
male.sayHello();
2、原型对象继承
通过Male.prototype = new Person();更改子类的原型对象,再Male.prototype.constructor = Male;纠正指向。
function Person(){
}
Person.prototype.name = "john";
Person.prototype.age = 20;
Person.prototype.sayHello = function(){
console.log(this.name);
}
function Male(){
}
Male.prototype = new Person();
Male.prototype.constructor = Male;
Male.prototype.sayHi = function(){
console.log("hi");
}
var male = new Male();
male.sayHello();
male.sayHi();
console.log(male.__proto__.constructor);
3、组合继承
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function(){
console.log(this.name);
}
function Male(name,age){
Person.call(this,name,age);
}
/*Male.prototype = new Person();*/
for(var attr in Person.prototype){
Male.prototype[attr] = Person.prototype[attr];
}
Male.prototype.sayHi = function(){
console.log("hi");
}
var male = new Male("john",20);
male.sayHello();
var person = new Person("mr",18);
person.sayHi();
4、寄生式组合继承
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function(){
console.log(this.name);
}
function Male(name,age){
Person.call(this,name,age);
}
//继承原型对象上的方法
Male.prototype = Object.create(Person.prototype);
Male.prototype.constructor = Male;
Male.prototype.sayHi = function(){
console.log("hi");
}
var male = new Male("john",20);
male.sayHello();
console.log(male.__proto__.constructor);
function inherit(subType,superType){
subType.prototype = Object.create(superType.prototype);
subType.prototype.constructor = subType;
}
5、ES6继承
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
sayHello(){
console.log(this.name);
}
static foo(){ //类方法
console.log("foo");
}
}
//Person.foo();
class Male extends Person{
constructor(name,age){
super(name,age);//创建this对象,改变this指向,指向父类的构造函数的
this.sexy = "male";
}
sayHi(){
//this.sayHello();
super.sayHello();//指向父类的原型对象
console.log("hi");
}
static bar(){
super.foo();//指向父类
}
}
Male.bar();
var male = new Male("john1",20);
//male.sayHello();
male.sayHi();
补充:给数组添加最大值最小值的方法
//getMinVal() getMaxVal() 所有的数组都能访问这个方法
//把这两个方法挂载到原型对象上
var arr = [11,1,5,78];
var arr1 = [32,34,9,243];
Array.prototype.getMinVal = function(){
return Math.min.apply(null,this);
}
Array.prototype.getMaxVal = function(){
return Math.max.apply(null,this);
}
var min = arr1.getMinVal();
var max = arr1.getMaxVal();
console.log(min,max);