(2020年11月2日学习笔记)闭包深入理解,对象继承的方法(原型链继承,构造继承,语法糖继承)

  • Post author:
  • Post category:其他




一:闭包

​ 在一些使用场景中,我们不想使用全局变量,但是又不想每次使用后改变其值,这个时候该怎么做呢。高级一点说”一个变量既能重复使用,又不会污染全局“这时候

闭包

就产生了

闭包的重要理解:闭包可以在全局函数里面操作另一个作用域的局部变量



形成闭包的步骤

一:外层函数嵌套内层函数

二:内层函数访问外层函数的变量

三:外层函数的返回值为内层函数(注:不是返回内层函数的结果而是内层函数)



闭包的实现原理

这里引用了

熊大林老师的关于闭包深入理解的文章

2、外层函数调用时

在这里插入图片描述

3、外层函数调用后

在这里插入图片描述

4、内层函数调用时

在这里插入图片描述

5、内层函数调用后

在这里插入图片描述



闭包分析

在形成闭包后,外层函数的活动对象(函数在调用的时候被创建的对象)与内层函数的执行上下文,内层函数调用形成的执行栈形成一个稳定的三角形,且不能释放,所以在闭包创建后,在不调用的使用的时候,会造成内存浪费

换句话说,

只要涉及到函数嵌套的时候,都会有闭包的存在



二:对象的继承

在js中,由于类是基于原型链和构造函数来实现的,所以js对象(类)的继承也有不同的方式



一:原型链继承

原理:定义一个父类以及子类,将子类的原型对象,指向父类的实例对象,这样便可以访问到父类所有的属性与方法了

//定义一个动物类
function Animal(name){
    this.name = name
}
Animal.prototype.say(){
    console.log(`My name is ${this.name}`)
}
//定义一个cat类,因为下面会继承Animal类,所以不需要写什么东西
function Cat(){
    
}
//将Cat的原型对象执行Animal的实例对象
Cat.prototype = new Animal('jerry')   //原型链继承的重点代码
//这个时候new Cat对象便能访问Animal的属性方法了
var jerry = new Cat()
jerry.say()


原型链继承的原理图

在这里插入图片描述



原型链继承的优缺点

有点:

非常纯粹的继承关系,实例是子类的实例,也是父类的实例

父类新增原型方法/原型属性,子类都能访问到

简单,易于实现

缺点:

要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中

无法实现多继承

来自原型对象的引用属性是所有实例共享的

创建子类实例时,无法向构造函数传参



二:构造函数继承

原理:还是一个定义父级类,子类。在子类的构造函数中,调用父类(这里将父类看出普通的函数,直接调用),不过需要注意的是,父类里面的this指向问题

//定义一个动物类
function Animal(name){
    this.name = name
}
//定义一个Cat类,并指定形成name
function Cat(name){
    //将动物类看出一个普通函数,在这里调用,并传入形参name
    Animal.call(this,name)   //注意由于在动物类中,涉及到this指向,如果不更改的话,this将指向window,无法指向Cat的实例对象。利用apply或call方法修改this
}
//new一个Cat实例对象,这里可以传入name参数了
var jerry = new Cat('jerry')


构造函数继承的优缺点

优点:

解决了原型链继承中,子类实例共享父类引用属性的问题创建子类实例时

可以向父类传递参数可以实现多继承(call多个父类对象)

缺点:

实例并不是父类的实例,只是子类的实例

只能继承父类的实例属性和方法,不能继承原型属性/方法

无法实现函数复用,每个子类都有父类实例函数的副本,影响性能



三:组合继承(原型链继承与构造函数继承相结合)

原理:原型链继承与构造函数继承相结合,弥补各自的不足

//定义一个动物类
function Animal(name){
    this.name = name
}
//创建原型方法
Animal.prototype.say(){
    console.log(`My name is ${this.name}`)
}
//定义一个Cat类,并指定形成name
function Cat(name){
    //将动物类看出一个普通函数,在这里调用,并传入形参name
    Animal.call(this,name)   //注意由于在动物类中,涉及到this指向,如果不更改的话,this将指向window,无法指向Cat的实例对象。利用apply或call方法修改this
}
//将Cat的原型对象指向Animal的实例对象
Cat.prototype = new Animal();
//由于原型链继承没有构造函数属性,所以为了严谨,我们自己添加
Cat.prototype.constructor = Cat;//当然这里也可以不写,只是为了更加严谨
//最后使用原型链的方法,看看是否继承成功
console.log(cat instanceof Animal); // true
var cat = new Cat();
console.log(cat instanceof Cat); // true


组合继承的优缺点

优点:

弥补了原型链继承与构造函数继承的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法

既是子类的实例,也是父类的实例

不存在引用属性共享问题

可传参

函数可复用

缺点:

调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)



四:寄生组合继承(组合继承的升级版)

原理:在原型对象赋值的时候,通过一个子调用函数来实现,调用结束后,及时销毁,就不会存在生成了两份实例(子类实例将子类原型上的那份屏蔽了)这个问题了

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


寄生组合继承的优缺点

优点:堪称完美

缺点:实现较为复杂



五:拷贝继承(不推荐,做了解)

原理:遍历父类的属性方法赋值给自己

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 访问到)



六:语法糖继承(ES6继承)

使用最多,最方便的继承方法

class Animal{
    constructor(name){
        this.name = name;
    }
    say(){
        alert("My name is "+this.name);
    }
    eat(food){
        alert(this.name+" is eating "+food);
    }
}
//使用extends关键字,自动继承原型方法,属性继承,需要使用super关键字
class Cat extends Animal{
    constructor(name){
        super(name);//这里传入构造函数的参数
        // 注意:如果子类的构造函数里还有一些别的代码一定要放在super之后
    }
}

var  tom = new Cat("Tom");
tom.say();
tom.eat("apple");
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true



版权声明:本文为weixin_50518977原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。