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,['机智','灵活']);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  •   面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意...
    霜天晓阅读 2,107评论 0 6
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,563评论 0 5
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,754评论 2 9
  • 王伟军.男.汉族.生别于1985年.11月.20日,家住:甘肃省.陇南市.西和县.洛峪镇.关坝村一社,自六岁时就得...
    谦和画苑阅读 813评论 0 3
  • 鱼鳞般的云朵 在天空游来游去 远山在蓝天下 露出嶙峋的肌肤 你的百褶裙 在操场舞动 青春在阳光下 闪耀着熠熠光辉 ...
    风过无痕L阅读 118评论 0 0