全局变量所有函数都能调用,容易对其造成污染(改变其值造成逻辑混乱),局部变量又只能在函数体里使用,不能为全局服务重复使用,要想解决此类问题闭包是个不错的选择。
对闭包的理解:闭包能在全局函数里面操控另一个作用域的局部变量
完成闭包的三步
1.外层函数嵌套内层函数
2.内层函数使用外层函数的局部变量
3.把内层函数作为外层函数的返回值
function numCompany(){
var n=1;
function getNum(){
return n++;
};
return getNum;
}
var fn=numCompany();
console.log(fn());
以上是闭包的全部过程!!!
继承
继承通俗来说就是子承父业,为什么要使用继承,一个字就是懒,能继承父业还奋斗个啥?
当然面向对象的语言都有继承的方法,像强语言类型的Java,C++等,但是JavaScript的面向对象是通过构造函数来实现的,所以有些许不同,但是目的都是一样的,继承父类的属性和方法。
JavaScript也有封装和多态(重写和重载),JavaScript本来就是弱语言类型,不像java那样严格,所以重写和重载会相当方便,这里不再赘述。
那么JavaScript是怎么实现继承的呢? 子类共享父类的属性和方法,js的继承都是基于原型实现的!!!!
JavaScript实现继承的方法有有六种 以下面的例子介绍!
//定义一个动物类
function Animal (name) { // 属性 this.name = name; // 实例方法 this.say= function(){ console.log("My name is "+this.name); } } // 原型方法 Animal.prototype.eat = function(food) { console.log(this.name + '正在吃:' + food); };
1.原型链继承:子类的原型指向父类的实例
function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = 'cat'; var cat = new Cat(); console.log(cat.name); cat.say(); cat.eat('fish'); console.log(cat instanceof Animal); //true console.log(cat instanceof Cat); //true
结论:能较好的继承,实例是子类的实例也是父类的实例,父类新增的原型方法属性子类都能访问到,但是必须要在new Animal()这样的语句之后执行,不能放到构造器中,不能实现多继承,来自原型对象的引用属性是所有实例共享的,创建子类实例时无法向构造函数传参
2.构造继承(call,apply继承)
function Cat(name){ Animal.call(this,name); } var cat = new Cat("Tom"); console.log(cat.name); cat.say(); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
结论:解决1中子类实例共享父类引用属性的问题,可以向父类传递参数,可以实现多继承(call多个父类对象),但是并不是父类的实例,而是子类的实例,只能继承父类的实例属性和方法不能继承原型上的属性和方法,无法实现函数的复用,每个子类都有父类实例函数的副本,影响性能
3.拷贝继承
function Cat(name){ var animal = new Animal(name); for(var key in animal){ Cat.prototype[key] = animal[key]; } } // Test Code var cat = new Cat("Tom"); console.log(cat.name); cat.say(); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
结论:支持多继承,但是这相当于直接复制的父类的属性,效率低,内存占用高,无法获取父类的不可枚举方法(不可枚举方法,不能使用for in访问到)
4.组合继承(原型链和构造函数的组合)
function Cat(name){ Animal.call(this,name); } Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; // Test Code var cat = new Cat(); console.log(cat.name); cat.say(); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // true
结论:弥补2的缺陷,实例和原型都能继承,不存在引用属性共享问题,可传参,函数可复用,唯一不足就是调用了两次构造函数,生成了两份实例(子类实例将子类原型上那一份屏蔽了)
5.寄生组合继承
function Cat(name){ Animal.call(this); this.name = name; } (function(){ // 创建一个没有实例方法的类 var Super = function(){}; Super.prototype = Animal.prototype; //将实例作为子类的原型 Cat.prototype = new Super(); })(); // Test Code var cat = new Cat(); console.log(cat.name); cat.say(); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); //true
结论:就很完美,看起来比较复杂,实际也复杂
6.ES6继承(语法糖)
class Animal{ constructor(name){ this.name = name; } say(){ alert("My name is "+this.name); } eat(food){ alert(this.name+" is eating "+food); } } class Cat extends Animal{ constructor(name){ super(name); } } var tom = new Cat("Tom"); tom.say(); tom.eat("apple"); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); //true
结论:就用这个!!!
学习一门语言就要熟悉它的底层原理,到时候使用岂不是信手拈来?
tips:你我山巅自相逢