一、浅谈原型

首先我们要知道创建对象的方法有两种:

1.通过字面量的方式直接创建


    var obj = {
        name:'baimao',
        age:21
    }

2.通过构造函数创建对象

    function People(name,age) {
        this.name = name;
        this.age = age;
    }

  而当我们使用构造函数创建对象时,对对象的属性或者方法的设置是通过传参的方式进行的,考虑一个问题,公有的属性或者方法如果仍在构造函数中通过传参设置,每次new出来的新的对象都保留有一份构造函数中的信息,且有部分信息时相同并不会更改的,比如,人生来就有四肢,能吃饭能说话等。这样就浪费了内存,相同的信息在内存中存在了很多份(每通过构造函数new一个实例就复刻了一份相同的东西)。

  而原型就是为了解决相同信息的复用问题,使用原型可以使得所有通过构造函数new出来的对象的原型指向的是同一个地址空间,避免了内存上的浪费,也使得继承概念更合理。

  这里面要指明的是,放在原型中的属性和方法在内存中只存在一份,所有通过该构造函数new出来的对象都使用的是这一份原型。所以js中的原型是引用原型。

来声明一下对象的 __proto__ 与构造函数的prototype区别。对象都存在__proto__ 属性,而通过调用构造函数new出来的对象,其__proto__ 属性是指向构造函数的prototype属性的,而构造函数的prototype属性本身也是一个对象,仍存在 __proto__ 属性,这将会引入后续的原型链的问题,在后面会介绍。

3.为构造函数设置原型的方法

    (1)     People.prototype.arms = 2;
            People.prototype.eat = function() {
                console.log(this.name + '会吃饭');
            }
    
    (2)     var people = new People('baimao',21);
            people.__proto__.arms = 2;
            people.__proto__.eat = function() {
                console.log(this.name + '会吃饭');
            }

二.js中的继承

  我们都理解的是js中实际是不存在类的概念的,所谓继承只是通过构造函数的继承和构造函数中的原型的继承来实现的。但我们下面直接用类的概念来讲更容易理解,假设js中存在类吧

1.构造函数的继承

  (1)首先介绍构造函数的继承

     function People(name,age) {
        this.name = name;
        this.age = age;
    }
    People.prototype.eat = function () {
        console.log(this.name + '正在吃...');
    }
    function Student(name,age) {
        People.call(this,name,age);
        //People.apply(this,arguments);
    }
    console.log(new Student('baimao',21));

  通过上图我们可以看到通过call或者apply的方法调用父类的构造函数是可以使用父类的构造函数对子类实例进行赋值的,总结一下,也就是说通过call,apply方法可以使用父类的构造函数。但是这种方法有局限性,细心的人可能会观察到,使用call或者apply方法继承到的只有父类的构造方法中的属性或者方法,父类原型中的属性和方法不会被继承,为了解决这一问题,我们可以通过如下方式:

2.子类原型的继承

    方法一:
    Student.prototype = Object.create(People.prototype);
    //在这里之所以要自己添加子类原型中的构造函数是因为我们直接使用
    //Object.create(People.prototype)创建一个对象出来给子类的原型赋值,会
    //覆盖掉子类原有的构造函数,所以需要手动设置
    Student.prototype.constructor = Student;

    方法二:
    Student.prototype.__proto__ = People.prototype;

    var student = new Student('baimao',21);
    student.eat();
    console.log(student);

  实际中我们可以发现,通过上述两种方式是在子类构造函数的原型的原型中添加方法,当我们通过new子类对象并调用方法时,该方法会先在子类自己的构造函数中找,找不到则在子类的原型中找,再找不到就去子类原型的原型中找,直到找到为止,否则报错,该方法未定义 not defined

即为原型链的概念:

关于原型链:
        先在对象自身去找(构造函数中找),找到后调用即可;
        如果找不到,就去自身原型中找,找到调用即可;
        如果还找不到,就去原型的原型中找,找到调用即可...
        直到找到Object的原型,还是找不到,就抛出异常

  通过继承父类而得到的子类对象,如果继续在子类的原型中添加父类中存在的属性或者方法,意义上为重写父类的方法,但实际意义上只是在子类的原型中添加了一个方法,并没有真正覆盖父类的方法,通过子类构造函数的原型的原型中还是可以查看到父类原型的属性或者方法.

关于isPrototypeOf 和 hasOwnProperty()

  1. isPrototypeOf

    判断原型和对象之间的关系

    isPrototypeOf 判断原型和对象之间的关系:判断对象是否存在于另一个对象的原型链中
  2. hasOwnProperty()

    用于指示一个对象自身(不包括原型链)是否具有指定名称的属性。 如果有,返回true,否则返回false。
  3. in

    可以查看原型链中是否存在某属性

    console.log('family' in dog)

  注意我们在修改原型时不能使用对象的打点调用:

dog.family = '哈哈';这样写是为对象新增加一个属性,不会修改到原型,通过打点调用的方法相当于只能get不能set

3.修改父类原型中的方法

  子类继承父类,对父类的原型中修改原先存在的属性,修改只供自身使用,对父类没有影响但如果子类对父类的原型中添加了父类不存在的属性,添加操作会影响父类。

    function Animal(family) {
        this.family = family;
        Animal.prototype.run = function () {
            console.log('动物都会跑');
        }
        Animal.prototype.legs = 4;
    }

    function Dog(family, name, color) {
        Animal.call(this, family);
        this.name = name;
        this.color = color;
    }

    // 用一个中间变量obj 作为桥梁去完成子类对父类的继承
    var obj = Object.create(Animal.prototype);
    Dog.prototype = obj;
    Dog.prototype.constructor = Dog;

    Dog.prototype.skill = function () {
        console.log('看家');
    }

    //修改了父类
    Dog.prototype.__proto__.add = function () {
        console.log('这是一个通过子类去修改父类的测试');
    }

    //会发现中间变量被改,所以js中对象的拷贝是引用拷贝,即拷贝地址的值拷贝
    console.log(obj);

    Dog.prototype.__proto__.legs = 2;

    var animal = new Animal('test');

    //在这里输出父类对象,会发现add方法增添了,但父类的legs属性并没有被改
    animal.add();
    console.log(animal.legs);

    

执行结果如下:

三、ES6中的继承新语法

在ES6中新增了class和extends关键字


        class People {
            // 构造函数
            constructor(name, age) {
                //初始化属性
                this.name = name;
                this.age = age;
            }

            eat(food) {
                console.log(this.name + '吃:' + food);
            }
        }

        //设置原型中的内容
        People.prototype.legs = 2;

        //创建People对象
        var people = new People('baimao', 21);
        people.eat('苹果');
        console.log(people);

        class Man extends People {
            constructor(name, age, sex) {
                //相当于会自动的去执行People的构造函数
                super(name, age);
                //新增,扩展自己的属性
                this.sex = sex;
            }

            playMJ(hours) {
                console.log(this.name + '打了' + hours + '个小时麻将');
            }
        }

        var man1 = new Man('翠花', 28, '男');
        man1.playMJ(2);

    

ES6中类的数据类型就是函数,类本身指向构造函数

在ES6中,除了constructor中的属性是放在类中,其余定义的方法都是放在构造函数的prototype中.

子类继承父类,使用super关键字调用父类的构造方法

JavaScript原型及继承的更多相关文章

  1. 深入理解:JavaScript原型与继承

    深入理解:JavaScript原型与继承 看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解. 首先JavaScr ...

  2. JavaScript原型与继承

    JavaScript原型与继承 原型 在JavaScript中,每个函数都有一个prototype属性,这个属性是一个指针,指向该函数的原型对象.这个原型对象为所有该实例所共享.在默认情况下,原型对象 ...

  3. JavaScript原型与继承的秘密

    在GitHub上看到的关于JavaScript原型与继承的讲解,感觉很有用,为方便以后阅读,copy到自己的随笔中. 原文地址:https://github.com/dreamapplehappy/b ...

  4. JavaScript 原型与继承

    JavaScript 原型与继承 JavaScript 中函数原型是实现继承的基础.prototype.construct.原型链以及基于原型链的继承是面向对象的重要内容 prototype 原型即 ...

  5. javascript原型链继承

    一.关于javascript原型的基本概念: prototype属性:每个函数都一个prototype属性,这个属性指向函数的原型对象.原型对象主要用于共享实例中所包含的的属性和方法. constru ...

  6. JavaScript 原型与继承机制详解

    引言 初识 JavaScript 对象的时候,我以为 JS 是没有继承这种说法的,虽说 JS 是一门面向对象语言,可是面向对象的一些特性在 JS 中并不存在(比如多态,不过严格来说也没有继承).这就困 ...

  7. 8条规则图解JavaScript原型链继承原理

    原形链是JS难点之一,而且很多书都喜欢用一大堆的文字解释给你听什么什么是原型链,就算有图配上讲解,有的图也是点到为止,很难让人不产生疑惑. 我们先来看一段程序,友情提示sublimeText看更爽: ...

  8. 【Javascript】Javascript原型与继承

    一切都是对象! 以下的四种(undefined, number, string, boolean)属于简单的值类型,不是对象.剩下的几种情况——函数.数组.对象.null.new Number(10) ...

  9. 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链

    1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...

随机推荐

  1. Linux进阶命令-sort、uniq、 cut、sed、grep、find、awk

    命令难度总体来说有简入难,参数都是工作中常常用到的.如果涉及到一些生僻的参数还请百度或man一下. sort(参考学习网站:http://www.cnblogs.com/dong008259/arch ...

  2. MVC通过递归+部分视图实现评论

    前一个项目里有一个关于评论系统的需求.感觉这个评论的实现还是蛮好玩的,所以记录下这个系统的实现相关内容. 功能需求: 1.用户可以再视屏下方留言. 2.用户可以再别的用户留言下方回复. 3.用户可以删 ...

  3. 函数的上下文就是函数里面的this是谁

    规律1:函数用圆括号调用,函数的上下文是window对象 比如小题目: function fun(){ var a = 888; alert(this.a); //实际上访问的是window.a } ...

  4. 谷歌是如何做代码审查的 | 外刊IT评论 - Google Chrome

    谷歌是如何做代码审查的           本文的作者 Mark CC 在上一篇文章中提到过,我已经不在Google工作了.我还没有想清楚应该去哪里-有两三个非常好的工作机会摆在我面前.因为在这段做决 ...

  5. java的字符串操作和for循环的使用

    /* "12 0 99 -7 30 4 100 13" 要求对字符串中的数值进行排序.生成一个数值从小到大新字符串. "-7 0 4 12 13 30 99 100&qu ...

  6. jsp实现上一页下一页翻页功能

    前段时间一直忙于期末考试和找实习,好久没写博客了. 这段时间做了个小项目,包含了翻页和富文本编辑器Ueditor的两个知识点,Ueditor玩的还不是很深,打算玩深后再写篇博客. 要实现翻页功能,只需 ...

  7. STL—list

    前面我们分析了vector,这篇介绍STL中另一个重要的容器list list的设计 list由三部分构成:list节点.list迭代器.list本身 list节点 list是一个双向链表,所以其li ...

  8. STL—vector空间的动态增长

    vector空间的动态增长     当添加元素时,如果vector空间大小不足,则会以原大小的两倍另外配置一块较大的新空间,然后将原空间内容拷贝过来,在新空间的内容末尾添加元素,并释放原空间.vect ...

  9. TWS日志查看

    背景:记录下tws的日志查看过程,备忘 1 日志查看过程 根据企业的流水号,在日志中查询企业发送的报文: ps:期间最好将日志所在的行号进行记录,方便定位. 2017032802_2017070700 ...

  10. (转)Oracle中的rownum,ROWID的 用法

    场景:在书写oracle的sql语句时候,如果语句不存在主键,需要删除几条重复的记录,这个时候如果不知道oracle中的伪列,就需要把所有的重复记录先删除,再插入.这样做好麻烦,可以通过伪列来定位记录 ...