pfan123 / Articles

经验文章
169 stars 25 forks source link

代码复用之继承 #7

Open pfan123 opened 7 years ago

pfan123 commented 7 years ago

代码复用之继承

js继承通常分两类:原型链的继承,实例对象继承(浅拷贝,深拷贝)

原型链的继承

    function Parent(name){
        this.name= name || "adam",
        this.age="24";
    }
    Parent.prototype.say = function () {
        return this.name
    }   

    function Child(name){
        Parent.apply(this, arguments)
    }

    var child = new Child()

缺点:子类无法继承父类 prototype 共享属性,原型链断裂。

    function Parent(){
        this.name = "adam"
    }
    Parent.prototype.say = function () {
        return this.name
    }   

    function Child(){
        Parent.call(this)
    }

    Child.prototype = new Parent()

    Child.prototype.constructor = Child

    var child = new Child()

缺点:实现继承原型,同时继承了两个对象的属性,即添加到this的属性以及原型属性。在绝大多数的时候,并不需要这些自身的属性,因为它们很可能是指向一个特定的实例,而不是复用。

    function Parent(){
        this.name = "adam"
    }
    Parent.prototype.say = function () {
        return this.name
    }   

    function Child(){
        Parent.call(this)
    }

    Child.prototype = Parent.prototype;

    var child = new Child()

优点: 效率比较高(不用执行和建立父类Parent的实例了),比较省内存。 缺点: 子类Child.prototype和父类Parent.prototype现在指向了同一个对象,那么任何对Child.prototype的修改,都会反映到Parent.prototype,影响作用域。

如这样操作Child.prototype.constructor = Child; 则 alert(Parent.prototype.constructor); // Child

function Parent(name){
    this.name=name || "gaolu",
    this.age="24";
}
Parent.prototype.sayName=function(){
    return this.name;
}

function Child(name){
   this.sex = “male”,
   Parent.apply(this, arguments)
}
function inhert(C,P){
    var F=function(){}; //F是空对象,所以几乎不占内存
    F.protototype = P.prototype;
    C.prototype = new F();
    C.prototype.constructor = C;
    C.uber = P;
}

inhert(Child, Parent)
var child = new Child()

此种方式最优,也被大多数框架库所采用,节省内存效率较高。

Object.create()

//通过Object.create()继承
function Parent(){
    this.name = "adam"
}

Parent.prototype.say = function () {
    return this.name
}

function Child (name){
    Parent.call(this)
}

Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
console.log(new Child())
class Queue{
    consturctor(contents = []) {
        this._queue = [...contents]
    }
    pop() {
        const value = this._queue[0];
        this._queue.splice(0, i);
        return value;
    }
}

//继承
class PeekableQueue extends Queue{
        constructor(...args) {
             super(...args);
       }
    peek() {
        return this._queue[0];
    }
}

实例对象继承(非构造函数的继承)

简单的把父对象的属性,全部拷贝给子对象,也能实现继承

    function extend(p){
        var c = {}
        for (var i in p){
            c[i] = p[i];
        }
        c.uber = p;

        return c;
    }

如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。它的实现只要递归调用"浅拷贝"就行了。

    function deepExtend(p, c){
        var c = c || {};

        for(var i in p){
            if(typeof p[i] === 'object'){
                c[i] = (p[i].constructor === Array) ? [] : {};
                //c[i] = (Object.prototype.toString.call(p[i]) === '[object Array]') ? [] : {};
                deepExtend(p[i], c[i])
            }else{
                c[i] = p[i]
            }
        }

        c.uber = p

        return c;
    }

引伸数组的常用方法拷贝

arrayObject.slice(start,end)

返回一个新的数组,不影响父数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。

parent = [1,2,3,4]
//slice() 与 slice(0),开始参数默认为0
child = parent.slice()
child.push(22)
console.log(parent, child) // [1, 2, 3, 4], [1, 2, 3, 4, 22]
arrayObject.concat(arrayX,arrayX,......,arrayX)

该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本

parent = [1,2,3,4]
child = parent.concat([])
child.push(22)
console.log(parent, child) // [1, 2, 3, 4], [1, 2, 3, 4, 22]

ps: 三者效率,slice 与 concat 相当,而for in 最差

ES6 中,扩展运算符也能很方便的拷贝数组

const items = [1,2,3,4] 
const itemCopy = [...items]

注意 splice 不能实现数组拷贝

arrayObject.splice(index,howmany,item1,.....,itemX)

splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目,该方法会改变原始数组

//使用splice()
var x = [14, 3, 77]
var y = x.splice(1, 2)
console.log(x)           // [14]
console.log(y)           // [3, 77]

//使用slice()
var x = [14, 3, 77];
var y = x.slice(1, 3);
console.log(x);          // [14, 3, 77]
console.log(y);          // [3,77]

由此,也发现 spliceslice 区别,splice会改变原生数组,而slice返回一个新的数组。

参考:

proto和prototype来深入理解JS对象和原型链

Javascript 面向对象编程(一):封装

Javascript面向对象编程(二):构造函数的继承

Javascript面向对象编程(三):非构造函数的继承

Object.create()