关闭

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

标签: javascriptprototype
689人阅读 评论(3) 收藏 举报
分类:

作者: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个最大好处!!!

2
1
查看评论

javascript 编程思想

  • 2009-02-17 22:55
  • 106KB
  • 下载

用面向对象的编程思想去写js

有时候,还会看到一些搞前端的朋友或者苦逼被迫兼职写前端的后端朋友,去写js的时候,还是一个方法一个方法的去罗列。 就像下面这样: function click1(){ alert("1"); } function click2(){ alert("2...
  • u010480479
  • u010480479
  • 2016-08-05 14:46
  • 2294

控制反转为程序开发带来的好处是什么?

IOC全称是Inversion of Control,即反转控制,或者说是依赖注入更为合适。选择别纠结这些全称的专业词。我们可以用别外一些方式去理解它,IOC,是一种设计模式。它的延生所要实现的是把藕合从代码中移出去,统一放到XML文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实...
  • H12KJGJ
  • H12KJGJ
  • 2017-06-23 09:29
  • 969

javascript学习难点剖析1

1.函数内部两个对象:arguements和this; 其次,还有另一个函数对象的属性对象的属性:caller (1)arguements 表示类数组对象,包含传入函数中的所有参数。 用途:保存函数参数。 属性:callee,表示的是一个指针类型,即指向当前拥有arguements对象的函数。 ...
  • zxy9602
  • zxy9602
  • 2016-09-20 18:02
  • 288

科技和互联网教育带来的好处就是尽可能给学生带去机会,带来公平

“教育最大的不公平就是不能真正做到因材施教,而科技和互联网教育带来的好处就是尽可能给学生带去机会,带来公平”,出身于教育世家,在教育领域深耕30多年的陈冬华有着这样的感悟。于是2012年,他在国内推出学乐云教学平台,有志于借助大数据和信息技术,深度挖掘教育大数据价值,让中国教育能够真正受益于科技所带...
  • gnicky
  • gnicky
  • 2017-02-06 14:40
  • 519

工作到底都能给你带来哪些好处?

在离开一家公司之前,你的心态是充满感恩和不舍,还是愤恨和怨念?无论你留还是走,给自己列一个list,原来它还有这么多值得感恩的地方。 1每月工资,供你生活 无论你现在实习还是在做全职工作,根据现在的职场规则都是按劳分配,每个月获得自己的薪酬及其他福利。也许你认为目前工资低,工作量大,...
  • u010794281
  • u010794281
  • 2015-09-05 18:23
  • 1074

另辟蹊径:云计算给企业带来的4个好处

随着云技术的不断发展,很多公司开始将传统的面对面进行的新员工入职培训和老员工的再学习逐渐的转变成基于云的网络培训,那么这种转变会给我们带来什么样的好处呢?企业又将从中得到什么样的便捷呢?不管是新员工的入职培训还是老员工的再学习,开发行业中的培训很难做到一刀切。除了要精心制作相关且适时的课程(这在安全...
  • wangpeng198688
  • wangpeng198688
  • 2016-02-26 17:42
  • 466

彻底理解JavaScript原型

 原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有"[[prototype]]"属性,函数对象有"prototype"属性,原型对象有"constructor"属性。 为了弄清原型,以及原型相关的...
  • wxw_317
  • wxw_317
  • 2015-11-03 16:14
  • 5807

python的好处

最近体会到python带来的好处了,因为我发现写一些小的程序它是再合适不过了。1.有次我需要删除一篇很长文章的一部分内容,首先我用普通的记事本打开这个文件,然后选中我要删除的部分,我发现这是一种很难的事,因为这篇文章很长,选择的时候又不能拖动滚动条,因此文字向下滚动的很慢,过了几十秒了,才滚动了10...
  • longxj04
  • longxj04
  • 2008-11-05 21:45
  • 1546

关于原型设计的重要性

从2012年底开始学JAVA编程到现在已将近三年,已完成单位复核部的管理系统的开发并维护有差不多两年,现在也准备给项目管理部开发一个项目管理系统,以前在开发公司也开发过系统,但与现在自己单位帮自己单位开发系统的感觉确实不一样,       ...
  • wilsonyun
  • wilsonyun
  • 2015-05-11 13:08
  • 1045
    算法channel

    交流思想,注重分析,实例阐述,通俗易懂,包含但不限于:经典算法,机器学习,深度学习,LeetCode 题解,Kaggle 实战。期待您的到来!

    算法与人工智能交流群:646901659

    个人资料
    • 访问:319463次
    • 积分:7589
    • 等级:
    • 排名:第3380名
    • 原创:350篇
    • 转载:1篇
    • 译文:0篇
    • 评论:58条
    博客专栏