引用类型之对象(面向对象03)

2018-04-03  本文已影响0人  倾国倾城的小饼干

什么是继承

大多数使用继承不外乎是为了获得两点好处,代码的抽象和代码的复用。
代码的抽象不用说了,交通工具和汽车这类的例子数不胜数,在传统的语言中(java)代码的抽象更多是使用接口来实现,而试验继承更多的是为了代码的复用。
怎么复用的?打个比方,classa继承了classb,classa便拥有了classb的公有和私有的变量和方法,便是classb将这些属性和方法直接copy给classa。这样便实现了继承。因此我们可以这样说,继承实际上是一种类与类之间的copy行为。从另一个方面来说,js的数据类型分文基本数据类型和引用数据类型,我们在对数据进行复制的时候,如果这个数据是基本数据类型那很好办,直接赋值就好,如果在使用js对对象或数组进行操作的时候,经常要将数组进行备份,事实证明如果只是简单的将它赋值其他变量,那么我们只要更改其中的任何一个,其他的也会跟着改变,这就导致了问题的发生。

js中的继承

在js中没有类的概念,只有对象。虽然,现在人们经常使用class关键字,这让js看起来似乎是拥有了“类”,可表面看到的不一定是本质,class只是一块糖,嚼碎了才知道里面其实还是原型链那一套。因此,js中的继承只是对象与对象之间的继承。反观继承的本质,继承便是让子类拥有父类的一些属性和方法,那么在js中便是让一个对象拥有另一个对象的属性和方法。
所以,这给我们一条十分清晰的思路。js中如何实现继承?只需让一个对象拥有另一个对象的属性和方法,这就实现了。

浅拷贝

既然让一个对象拥有另一个对象的属性和方法,首先想到的便是粗暴的方式,直接将对象的属性和方法强制copy到另一个对象。

var person={
    name:'cj';
    age:22;
}
var programer={
    language:'js'
}
function extend(p,c){
    var c={};
    for(var prop in p){
        c[prop]=p[prop]
    }
}

局限性:如果person加上一个adress对象即address{home:'home address'}programer.address.home='home'即改变时,person.address.home也跟着改变,因为两个对象指向的是同一个地址,拷贝的只是引用,也就是浅拷贝。

深拷贝
var person={
    name:'cj';
    age:22;
    adderss{name:'home address'}
}
var programer={
    language:'js'
}
function extend(p,c){
    var c={};
    for(var prop in p){
        if(typeof p[prop] ==='object'){
            c[prop]=p[prop].constructor===Array?[]:{};
            extend(p[prop],c[prop])
        }else{c[prop]=p[prop]}
    }
}

深拷贝用递归的方式来复制子对象里面的所有属性和方法,直到子子属性为基本数据类型。

原型链继承

如何使用原型链来实现继承呢?这要归功于js中的委托机制。
当我们获取一个对象的某个属性时。比如a.b,会默认调用一个内置的方法,这个内置的方法就是:
在当前对象中查找,找不到则委托给当前对象的proto。再找不到则委托给protoproto直到object.prototype中也没有找到,则返回undefind。
因此,我们想让对象a拥有对象b的属性和方法,即对象a继承对象b,只需要把b赋值给a的proto。利用属性的查找的委托机制,实现了a也拥有了b的属性和方法。
前面提到,proto是个内置隐藏属性,设计的本意是不可被读取和修改的,那么我们如何利用原型链来建立继承关系?js提供了new关键字。new关键字实现了a.proto=A.prototype这样只需把a的构造函数的prototype连接到b 就行了。

function A(){
    
}
var b={
    show:function(){
        console.log('这是来自b的方法')
    }
}
A.prototype=b
A.prototype.constructor=A
var a=new A()
a.show//这是来自b的方法

核心:原型链继承子类只是继承了父类的原型链上的属性,并且在创建子类的时候不能向父类的构造函数传递参数。

call继承
function Parent(){
    this.x=100;
    this.y=199
}
Parent.prototype,fn=function(){}
function Child(){
    this.a=100;
    Parent.call(this)
}
var p=new Parent();
var c=new Child();
console.log(p)//Parent{x:100,y:199}
console.log(c)//Child{d:100,x:100,y:199}

这很好理解,在子类的构造函数中,改变父类的this指向,改变为子类的实例,同时运行父类方法,这样父类中的this.x就变成了子类的实例.x。这样只能继承父类的私有属性和方法。

call继承和原型继承集合在一起。

===》无论是私有还是公有的属性都拿到了。

function Parent(){
    this.x=100;
}
Parent.prototype.getx=function(){}
function Child(){
    Parent.call(this)
}
Child.prototype=new Parent();
Child.prototype.constructor=Child;
var p=new Parent();
var c=new Child();
console.log(c)
create继承
var b={c:1}  
var a=Object.create(b)
console.log(a.c)//1

那么create继承是怎样实现地呢?

var p={name:'cj'}
function create(p){
    var ins;
    function F(){
        F.prototype=p;
        ins=new F();
        return ins;
    }
}
var c=create(p)
c.name==>'cj'

上例中ins.__proto__=F.prototype又因为F.prototype=p所以c.name==>'cj'

继承

  1. 继承的作用

继承可以使一个对象直接使用另一个对象的方法和属性。

  1. 代码的区别

//方法1
function People(name, sex){
    this.name = name;
    this.sex = sex;
    this.printName = function(){
        console.log(this.name);
    }
}
var p1 = new People('饥人谷', 2)

//方法2
function Person(name, sex){
    this.name = name;
    this.sex = sex;
}

Person.prototype.printName = function(){
    console.log(this.name);
}
var p1 = new Person('若愚', 27);

区别: 第二种方法将公有的属性放在Person的prototype上,当再创建一个对象实例的时候就不用像第一种方法那样把printName再创建一遍,而是从原型中调用就可以了。

  1. Object.create的作用

Object.create() 方法会使用指定的原型对象及其属性去创建一个新的对象。
兼容性:

  1. hasOwnProperty

作用:hasOwnPerperty是Object.prototype的一个方法,可以判断一个对象是否包含自定义属性而不是原型链上的属性,hasOwnProperty是JavaScript中唯一一个处理属性但是不查找原型链的函数。
语法:obj.hasOwnProperty(prop)(prop为要检测的属性名称)

  1. call的作用

function Person(name, sex){
    this.name = name;
    this.sex = sex;
}
function Male(name, sex, age){
    Person.call(this, name, sex);    //在子类的构造函数中,改变父类的this指向,改变为子类的实例,同时运行父类方法,这样父类中的this.x就变成了子类的实例.x。这样只能继承父类的私有属性和方法。
    this.age = age;
}
  1. 补全代码

function Person(name, sex){
    this.name=name;
    this.sex=sex;
}

Person.prototype.getName = function(){
    console.log(this.name)
};    

function Male(name, sex, age){
   Person.call(this,name,sex)
   this.age=age
}
Male.prototype=Object.create(Person.prototype)
Male.constructor=Male;
Male.prototype.getAge = function(){
   console.log(this.age)
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
上一篇下一篇

猜你喜欢

热点阅读