LiuL0703 / blog

Home Page:https://liul0703.github.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

原型&继承&闭包

LiuL0703 opened this issue · comments

原型

我们创建每一个函数 都有一个prototype属性 该属性指向一个对象 这个对象就是原型
我们创建一个对象时候 可以根据自己的需求 选择性的将一些方法属性通过prototype属性 挂载在原型对象上 而每一个new出来的实例都有一个__proto__属性 该属性指向构造函数的原型对象 通过这个属性 让实例方法能够访问 到原型对象上的这方法


闭包

当函数可以记住并访问所在的词法作用域时,就产生了闭包,这个函数持有对该词法作用域的引用,这个引用就叫做闭包
闭包本质还是函数,只不过这个函数绑定了上下文环境(函数内部引用的所有变量)
缺点:常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
作用(使用场景):可以用来管理私有变量和私有方法,将对变量(状态)的变化封装在安全的环境中,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。
闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收


继承

  • 原型链继承

function Animals(){
    this.name = ['cat','dog'];
    this.other = 'animals';
}
function Cat(){
};
// 继承
Cat.prototype = new Animals();

var c1 = new Cat();
var c2 =  new Cat();

c1.name.push('pig');
console.log(c1.name);     // ["cat", "dog", "pig"]
console.log(c2.name);    // ["cat", "dog", "pig"]

c1.other = 'cat';
console.log(c1.other);     // cat
console.log(c2.name);     // animals

缺点: 引用类型的属性被所有实例共享 一旦修改 会反应在所以实例上 创建子类型实例时 不能向超类型传参

注:通过原型链实现继承时 不能使用对象字面量创建原型方法 因为会重写整个原型链

  • 借用构造函数

function Animals(){
    this.name = ['cat','dog'];
    this.other = 'animals';
}
function Cat(){
    Animals.call(this);
};

var c1 = new Cat();
var c2 =  new Cat();

c1.name.push('pig');
console.log(c1.name);     // ["cat", "dog", "pig"]
console.log(c2.name);    // ["cat", "dog"]

优点:可以在子类型构造函数中向超类型构造函数传参

缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法 无法实现函数复用

  • 组合继承

function Animals(name){
    this.name = name;
    this.animals = ['cat','dog'];
}

Animals.prototype.sayName = function(){
    console.log(this.name);
}
function Cat(name,age){
    // 属性继承
    Animals.call(this,name);
    this.age = age;
}

Cat.prototype = new Animals();
Cat.prototype.constructor = Cat();
Cat.prototype.sayAge= function(){
    console.log(this.age);
};

var c1 = new Cat('Cat1',18);
c1.animals.push('pig');
console.log(c1.animals);    //  ["cat", "dog", "pig"]
c1.sayName();                   // Cat1
c1.sayAge();                       // 18

var c2 = new Cat('Cat2',19);
console.log(c2.animals);    //  ["cat", "dog"]
c2.sayName();                   // Cat2
c2.sayAge();                     // 19

优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。

缺点:无论什么情况下都会调用两次父类型构造函数 一次是在创建子类型原型时候 另一次是在子类型构造函数内部 子类型中包含全部父类型对象的实例属性 调用子类型构造函数时会重写这些属性

  • 寄生式继承

创建一个仅用于封装继承过程的函数 该函数在内部以某种方式来增强对象 最后返回对象

function Animals(orginal){
    var c = Object(orginal);
    c.sayHi = function(){
        console.log('Hi');
    }
    return c;
}

var c = {
    name:'cat',
    others:['cat1','cat2']
}
var cat = Animals(c);
cat.sayHi();    // 'Hi'

缺点:无法做到函数复用

  • 寄生组合式继承

基本思路是:不必为了指定子类型的原型而调用父类型的构造函数 我们只需要的是一个父类原型的副本 本质上就是使用寄生式继承来继承父类的原型 然后讲结果指定给子类型的原型

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function inheritPrototype(child, parent) {
    var prototype = object(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

//  继承
inheritPrototype(Child, Parent);

优点:集组合继承和寄生继承优点于一身 是实现继承最有效的方式