Javascript:原型编程思想带来的2个好处

原创 2017年01月29日 10:10:43

作者:vuefine


需要思考的问题
JavaScript最重要的一个属性非prototype莫属了,那么prototype在JavaScript中到底起了什么作用呢?没有它,不行吗?在传统的基于Class的语言如Java、C#、C++中,继承的本质是扩展一个Baseclass,得到新的Subclass。由于这类语言严格区分实例,继承实际上是出现新的类型。JavaScript没有严格区分类和实例,那么它是如何做到继承呢?

1解答prototype在JavaScript中到底起了什么作用呢?没有它,不行吗?
首先看一下原型编程思想,如果不采用原型编程思想,JavaScript又是什么样子呢?

//定义Animal函数:2属性,1个方法
function Animal(props) {
    this.name = props.name;
    this.birthYear = props.birthYear;
    //得到Animal的年龄
    this.getAge = function () {
        return new Date().getFullYear() - this.birthYear;
    }
}

let birdLiu=new Animal({name:'刘鸟',birthYear:'2006'}); //采用new表明是一个函数(‘类’)对象
console.log(birdLiu.getAge());
let sheepGuo=new Animal({name:'郭洋',birthYear:'2010'});
console.log(sheepGuo.getAge());

做一个测试:

birdLiu.getAge===sheepGuo.getAge; //false

这表明是什么意思呢?birdLiu和sheepGuo各自的getAge是一个函数,但它们是两个不同的函数,虽然函数名称和代码都是相同的! 如果我们通过new Animal()创建了很多对象,这些对象的getAge函数实际上只需要共享同一个函数就可以了,这样可以节省很多内存

那么如何解决这个问题呢? JavaScript采取了原型编程的思想去解决这个问题。简单说,在原型对象上赋值属性prototype,面向这个prototype编写原型方法等,然后通过new()出来的对象都统一走这个prototype上的定义的方法。

下面这行代码,测试了刚才的原理。

birdLiu.__proto__.getAge===sheepGuo.__proto__.getAge; //true:birdLiu和sheepGuo都指向了同一个getAge

这样,我们把上面的直接在Anmial中写的getAge()放到JavaScript提供的Prototype这个原型属性上,

//定义Animal函数(构造函数):2属性
function Animal(props) {
    this.name = props.name;
    this.birthYear = props.birthYear;
}

//Animal的原型prototype上定义获得Animal的年龄方法:getAge()
Animal.prototype.getAge=  function () {
        return new Date().getFullYear() - this.birthYear;
    }

再测试2个新建对象是不是指向了共同的getAge():

//采用new表明是一个函数(‘类’)对象
let birdLiu=new Animal({name:'刘鸟',birthYear:'2006'}); 
let sheepGuo=new Animal({name:'郭洋',birthYear:'2010'});
let b1 =birdLiu.getAge===sheepGuo.getAge; //true

为什么将getAge放到prototype上,两个new出来的对象指向了同一个getAge方法了呢?这个是如何做到的呢?

谈一下我对这个问题的一点理解,因为Animal(props)是一个构造函数,在创建birdLiu和sheepGuo时,通过操作new()之后,birdLiu指向了内存地址address1,地址内容分别是name:刘鸟,birthYear:2006;并且在内存地址address3上创建了prototype,保存了getAge函数地址,sheepGuo指向了内存地址address2,地址内容分别是name:郭洋,birthYear:2010;并且通过Animal.prototype找到了address3,进一步定位到了getAge函数地址。这样birdLiu和sheepGuo共享了getAge()。

2 JavaScript没有严格区分类和实例,那么它是如何做到继承呢?
现在想继承Animal,得到一个Bird对象。先定义一个Bird对象。

function Bird(props){
    Animal.call(this,props);
    this.flySpeed=props.flySpeed; //扩展一个鸟的飞行速度这个属性
}

实现对Animal的继承,创建一个纽带函数F,将纽带函数的prototype属性赋值为Animal.prototype,Bird的prototype属性重新赋值。

//纽带函数
function F(){

}

//bird继承于Animal的实现:
F.prototype=Animal.prototype;//纽带函数的prototype属性赋值为Animal.prototype
//Bird的prototype属性重新赋值,值为一个F对象,
//并且F对象的原型为Animal.prototype,同时也等于Animal.prototype。
Bird.prototype = new F(); 

//bird原型上扩展一个飞行的方法
Bird.prototype.fly= function(){
    console.log(this.name+ ' : 飞行速度为'+this.flySpeed);
}

//注意观察,Bird的prototype=new F()后,
//Bird.prototype.constructor也被破坏了,因此需要修复一下:
Bird.prototype.constructor=Bird;

//这样Bird完成了对Animal的继承

测试Animal上的getAge()是不是被Bird拿过来了,同时自己的个性方法是不是也有了:

let maque=new Bird({name:'麻雀张',birthYear:'2016', flySpeed:'15'});
console.log(maque.getAge()+'岁');
console.log(maque.fly());

测试结果:

1岁
麻雀张 : 飞行速度为15

如果把继承这个动作:

//bird继承于Animal的实现:
//纽带函数的prototype属性赋值为Animal.prototype
F.prototype=Animal.prototype;
//Bird的prototype属性重新赋值,值为一个F对象
//并且F对象的原型为Animal.prototype,同时也等于Animal.prototype。
Bird.prototype = new F(); 
//注意观察,Bird的prototype=new F()后
//Bird.prototype.constructor也被破坏了,因此需要修复一下:
Bird.prototype.constructor=Bird;
//这样Bird完成了对Animal的继承

封装到一个inherits()函数中,还可以隐藏F的定义

function inherits(Child, Parent) {
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}

完整的代码实现为:

function Animal(props) {
    this.name = props.name;
    this.birthYear = props.birthYear;
}

//得到Animal的年龄
Animal.prototype.getAge=  function () {
        return new Date().getFullYear() - this.birthYear;
    }

function Bird(props){
    Animal.call(this,props);
    this.flySpeed=props.flySpeed; //扩展一个鸟的飞行速度这个属性
}

function inherits(Child, Parent) {
    var F = function () {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
}

inherits(Bird,Animal);

//bird原型上扩展一个飞行的方法
Bird.prototype.fly= function(){
    console.log(this.name+ ' : 飞行速度为'+this.flySpeed);
}

//测试一下:

let maque=new Bird({name:'麻雀张',birthYear:'2016', flySpeed:'15'});
console.log(maque.getAge()+"岁");
console.log(maque.fly());

第2部分小结

JavaScript的原型继承实现方式就是:

定义新的构造函数,并在内部用call()调用希望“继承”的构造函数,并绑定this;

借助中间函数F实现原型链继承,最好通过封装的inherits函数完成;

继续在新的构造函数的原型上定义新方法。

JavaScript的对象模型是基于原型实现的,特点是简单,缺点是理解起来比传统的类-实例模型要困难。

好消息是ES6正式将关键字class引入进来,使得定义类更简单。

因此将代码(完整的代码实现部分)改写后:

class Animal {
    constructor(props) {
        this.name = props.name;
        this.birthYear = props.birthYear;
    }
    getAge() {
        return new Date().getFullYear() - this.birthYear;
    }
}

class Bird extends Animal {
    constructor(props) {
        super(props);
        this.flySpeed = props.flySpeed; //扩展一个鸟的飞行速度这个属性
    }
    fly() {

        console.log(this.name + ' : 飞行速度为' + this.flySpeed);
    }
}

的确是简单了很多,那么它们是不是又重新设计另一个思想呢,不是的,新引入的class等实际上是封装了上面说的比较麻烦的那种实现,class在后台解析后还是麻烦的样子,只不过我们用class就看不到那些多余的代码了。

测试一下:


let maque = new Bird({ name: '麻雀张', birthYear: '2016', flySpeed: '15' });
console.log(maque.getAge() + "岁"); //1岁
console.log(maque.fly());//麻雀张 : 飞行速度为15

总结:

JavaScript采用原型编程,所有对象都能共享原型上的方法,节省内存;同时基于原型这一实现思想,JavaScript通过找对原型链,方便地实现了继承。这就是原型编程带来的2个最大好处!!!

版权声明:本文为博主原创文章,欢迎转载,请注明 http://blog.csdn.net/daigualu

相关文章推荐

JavaScript原型中的哲学思想

记得当年初试前端的时候,学习JavaScript过程中,原型问题一直让我疑惑许久,那时候捧着那本著名的红皮书,看到有关原型的讲解时,总是心存疑虑。  当在JavaScript世界中走过不少旅...

JavaScript 面向对象思想以及原型、继承

首先,回顾一下JavaScript 对象的概念。每个对象中封装了一些属性和方法,并且这一部分保存在堆内存中,而每个对象实例其实是一个句柄,也就是一个指针,指向堆内存中的那块数据,所以说,这是JavaS...

Javascript继承机制的设计思想(原型链模式)

我一直很难理解Javascript语言的继承机制。 它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype...

JavaScript 原型中的哲学思想

欢迎来我的博客阅读:「JavaScript 原型中的哲学思想」 记得当年初试前端的时候,学习JavaScript过程中,原型问题一直让我疑惑许久,那时候捧着那本著名的红皮书,看到有关原型的讲解时...

韩顺平_轻松搞定网页设计(html+css+javascript)_第29讲_二维数组转置_js面向对象编程介绍 类(原型对象)和对象_学习笔记_源代码图解_PPT文档整理

文西马龙:http://blog.csdn.net/wenximalong/ 在上一个博客中未完成的矩阵转置的问题:韩顺平_轻松搞定网页设计(html+css+javascript)_第28...

了解javascript编程中的Prototype(原型)

日期:2012-5-16  来源:GBin1.com 当你定义javascript方法的时候,会产生一些预定义的属性,其中一个比较让人迷惑的属性就是prototype。在本文中,我们将详细介绍什...
  • jjfat
  • jjfat
  • 2012年05月22日 17:14
  • 444

javascript: 基于原型的面向对象编程

Douglas Crockford指出javascript是世界上最被误解的编程语言。由于javascript缺少常见的面向对象概念,许多程序猿认为javascript不是一个合适的语言。我在做第一个...
  • bdss58
  • bdss58
  • 2016年04月30日 00:11
  • 319

Javascript编程难点解析,如this,原型,继承,闭包等这些概念

Javascript编程难点解析 很多人刚刚接触前端甚至一些“老”前端都经常会在JavaScript中所谓的难点,如this,原型,继承,闭包等这些概念中迷失了自我。 ...

什么是JavaScript中的面向对象?与其他编程语言的面向对象有什么区别?什么是原型?

达人科技 2016-12-21 00:47 面向对象与原型模式 1.1. js的对象: 定义:是"无序属性的集合,其属性可以包含基本值,对象,和函数",没有类的概念(其他面向对象的语言都有类...

JavaScript模块化编程(一):模块原型和理论概念详解

随着网站逐渐变成"互联网应用程序",嵌入网页的Javascript代码越来越庞大,越来越复杂。   网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等……开发者不得不使用软件工程...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Javascript:原型编程思想带来的2个好处
举报原因:
原因补充:

(最多只允许输入30个字)