JS实现继承的方式

1. 属性拷贝(混入式继承)

问题:如果父对象中有引用类型(以下示例中的friend属性)的属性,子对象和父对象的该属性会共享同一份数据,修改其中一个会影响另外一个。

  • for in 方式获取
 var obj = {
        name:'zs',
        age:20,
        friend:['小明','小红']
    };
    var o = {};
    //需求:o获取obj的属性和方法
    for (var k in obj){
        o[k] = obj[k];
    }
    o.friend.push('小慧');
    console.log(o);//Object {name: "zs", age: 20, friend: Array(3)}
    console.log(obj);//Object {name: "zs", age: 20, friend: Array(3)}
  • 函数拷贝:
    Object.assign(o,obj,obj1,obj2···);
    第一参数:目标对象,后面的参数为要拷贝属性的对象(ES6 支持)
   var obj = {name : 'zs'};
    var o = {};
    var obj1 = {age : 20};
    var obj2 = {friend :['小明']};
    Object.assign(o,obj,obj1,obj2);//Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
    o.friend.push('小红')
    console.log(obj2);//Object {friend: Array(2)}
2.原型式继承:以下方法可以可以继承原类型中的原型属性和原型方法,但是无法继承实例属性和实例方法

方式一:利用对象的动态特性设置原型对象。

     function A() {
          }
        A.prototype.des = 'des'
        var a1 = new A();
       //利用构造函数创建出来的对象可以使用原型对象的属性和方法
       console.log(a1.des);

方式二:利用字面量的方法(替换原型)。

function A() {
   }
   A.prototype = {
       des :'des',
       constructor:A//此处修正,否则A.prototype.constructor 指向object.
   }
var a1 = new A();
   console.log(a1.des);

方式三:设置子构造函数的原型对象是父构造函数的原型对象。

  function  Person() {
        this.name ='你猜';
    }
    Person.prototype.des ='description';
    function Student() {
    }
    //设置子构造函数的原型对象是父构造函数的原型对象
    Student.prototype = Person.prototype;
    var stu1 = new Student();
    var p1 = new Person();
console.log(p1.constructor == stu1.constructor);//true
3.原型链继承:

问题:无法传递参数给父构造函数,继承过来的实例属性和方法会成为原型属性和方法,有共享问题。

  1. 每个构造函数都有原型对象;
  2. 每个对象都有自己的构造函数;
  3. 每个构造函数的原型都是一个对象;
  4. 那么这个构造函数的原型对象也有自己的构造函数;
  5. 那么这个构造函数的原型对象的构造函数也有自己的原型对象;
    以上形成一个链式的结构,称之为原型链,原型链的顶端是Object.prototype。Object.prototype.__ proto __ = null
  //1.提供父构造函数
    function Person() {
        this.name = 'guess'
    }
    //设置父构造函数的原型对象
    Person.prototype.des = 'description';
    //2.提供子构造函数
    function  Student() {
    }
    //实现原型链继承
    Student.prototype = new Person();
    var s1 = new Student();
    console.log(s1.des);//description
    console.log(s1.name);//guess

注意点:1.在完成原型链继承后,再修正构造器属性。
2.完成原型链继承后再修改(设置)原型属性和原型方法;
3.在完成原型链继承后,只能通过对象的动态特性添加属性和方法,不能使用字面量的方式(此操作会替换原型对象);

 function Person() {
        this.name ='guess'
    }
    function Student() {
    }
    //设置原型链继承
    Student.prototype = new Person();
    //修正构造器属性
    Student.prototype.constructor = Student;
    Student.prototype.des = 'description';
    var s1 = new Student();
    console.log(s1.name);//guess
4.借用构造函数继承 经典继承 伪对象继承
  function  Person(name) {
        this.name =name
    }
    function Student(num,name) {
        //默认会创建一个新的对象(Student类型的对象)
        //默认会把这个对象赋值给this,指向Student类型的对象
        this.num = num;
        //借用构造函数继承
        Person.call(this,name)
    }
    var s1 = new Student('20170112','zs');
    var s2 = new Student('20170113','ls');
    console.log(s1);//Student {num: "20170112", name: "zs"}
    console.log(s2);//Student {num: "20170113", name: "ls"}
5.组合继承:共享同一个原型对象
  1. 借用构造函数继承-->只能获取实例的属性和方法;
  2. 原型式继承-->获取原型属性和方法。
function Person(name) {
        this.name = name;
    }
    Person.prototype.des = 'des';
    function Student(num,name) {
        this.num = num;
        // 借用构造函数
        Person.call(this,name);
    }
    Student.prototype = Person.prototype;
    var s1 = new Student('110','ls');
    console.log(s1.des);
     Person.prototype.des = 'des===P';
    console.log(s1.des);
6.通过特定的方法实现继承:

Object.create()方法ES5开始规定使用,会创建一个新的对象,并设置该对象的原型对象是传入的对象。

  var obj = {
        name:'guess'
    }
    var o = Object.create(obj);
    console.log(o);
打印值

兼容写法

 var obj ={
        name:'guess',
        age:'secret'
    };
    if(Object.create){
        var o =Object.create(obj)//如果支持就直接使用这个方法来创建对象并设置原型
    }else{
        function F() {}//自定义构造函数
        F.prototype = obj //设置构造函数的原型对象为obj
        var o = new F();
    }
7.内容拷贝
  • 浅拷贝:又称地址拷贝或者指针拷贝,只复制简单类型,引用类型复制的为属性存储的地址。
 var obj = {
        name:'zs',
        age:20,
        car:{type:"火车"}
    }
    var o ={};
    for(var k in obj){
        o[k] = obj[k];
    }
    o.car.type = '飞船';
   console.log(obj);//Object {name: "zs", age: 20, car: Object}
  • 深拷贝(内容拷贝):
    函数构造思路:
  1. 提供一个函数封装深拷贝,有2个参数,第一参数是目标对象,第二个参数要拷贝属性的对象;
  2. 判断第一个参数,如果没有值就自己初始化;
  3. or..in遍历第二个参数
    1. 如果是值类型的数据,直接赋值
    2. 如果不是值类型的数据,就再一次调用这个方法拷贝内部存储的内容
 var zs = {
        name:'zs',
        age:20,
        car:{
            type:"火车"
        },
        friends:["小明","小红"]
    };
    var o = {};
    //如果有这个方法就直接使用,没有就给Array添加一个isArray的方法
    if(!Array.isArray){
        Array.isArray = function (obj) {
            return Object.prototype.toString.call(obj) ==  '[object Array]';
        }
    }
    function deepCopy(obj,copyObj) {
        //兼容性处理
        obj = obj ||{};
        //属性拷贝
        for(var k in copyObj){
            //判断只拷贝实例属性
            if(copyObj.hasOwnProperty(k)){
                if(typeof copyObj[k] =='object'){
                    //引用类型的数据
                    //copyObj[k]判断是否为数组
                    obj[k] = Array.isArray(copyObj[k])?[]:{};
                    //调用函数
                    deepCopy(obj[k],copyObj[k]);
                }else {
                    // 如果是值类型/函数的数据,直接赋值
                    obj[k] = copyObj[k];
                }
            }
        }
    }
   deepCopy(o,zs);
   console.log(zs);
   console.log(o);

call和apply函数
作用:借用其他对象的方法。
call: 参数列表 参数1,参数2...
参数一:调用方法的对象(函数内部this绑定对象);
参数二:函数调用传递的参数1;
参数三:函数调用传递的参数2···
apply:参数数组 [参数1,参数2]
参数一:调用方法的对象(函数内部this绑定对象);
参数二:函数调用传递的参数1;
参数三:函数调用传递的参数2。

  var p1 = {
        name : '老实的人',
        show : function (parm1,parm2) {
            console.log(this.name, parm1, parm2);
        }
    }
    var p2 = {
        name : '聪明的人'
    }
    p1.show.apply(p2,['机智','灵活']);

你可能感兴趣的:(JS实现继承的方式)