在Javascript语言中,原型是一个经常被讨论到但是有非常让初学者不解的概念。那么,到底该怎么去给原型定义呢?不急,在了解是什么之前,我们不妨先来看下为什么。

Javascript最开始是网景公司的死直男工程师Brendan Eich负责开发。起初设计的意愿非常简单,网景公司在1994年发布了Navigator浏览器0.9版(历史上第一个比较成熟的网络浏览器),这时候需要一个网页脚本语言,使得浏览器可以与网页互动。Brendan Eich认为这种语言无需复杂,尽量简单。然而Javascript里面都是对象,必须有一种机制,将所有对象联系起来,这就需要设计一个继承机制。为了维持Javascript的简单,Eich抛弃了传统面向对象语言中类的设计,而使用构造函数的方式去实现继承。比如下面一个构造函数:

1 function Stark(name){
2  this.name = name;
3 }

咳咳,为什么是Stark?因为博主是个骨灰级的冰与火之歌NC粉(●′艸`)ヾ 好好好言归正传,那么当我们要创建一个新的Stark对象时,仅需调用一个new命令:

1 var branStark = new Stark('布兰');
2 console.log(branStark .name); // 布兰

然而这种继承方式有个最大的缺点,便是无法实现数据和方法的共享。比如

 1 function Stark(name){
2  this.name = name;
3  this.words = 'Winter is coming. ';
4 }
5 var branStark = new Stark('布兰');
6 var aryaStark = new Stark('艾莉亚');
7 Stark.words = 'Summer is coming.'
8
9 console.log(branStark.words); //Winter is coming.
10 console.log(aryaStark.words); //Winter is coming.

可以看到,当Stark改变了家族族语words属性,布兰和艾莉亚的族语并没有改变。这是因为他们都有自己的一个Stark属性和方法副本。有时候这是好事,不会造成父子类数据上的混乱,但是有时候我们需要统一规划,使子类之间有数据的共享(大家一个家族的为毛不共用一个族语啊(╯‵□′)╯︵┻━┻ ),也节约资源上的开销。因此,Eich提出了一个概念:原型(prototype)。他为构造函数设置一个prototype属性。prototype属性包含一个对象,所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。

 1 function Stark(name){
2  this.name = name;
3 }
4 Stark.prototype.words = 'Winter is coming.';
5
6 var branStark = new Stark('布兰');
7 var aryaStark = new Stark('艾莉亚');
8
9 console.log(branStark.words); //Winter is coming.
10 console.log(aryaStark.words); //Winter is coming.
11
12 Stark.prototype.words = 'Summer is coming.';
13 console.log(branStark.words); //Summer is coming.
14 console.log(aryaStark.words); //Summer is coming.

可以看到,当Stark改变了words属性,布兰和艾莉亚的words属性也跟着改变了。这就实现了整齐划一~

通过以上介绍,大家应该对Javascript中的prototype概念有了比较基础的了解。但是在实际应用中,从父类继承创建一个子类,还是没那么简单。子类有时候希望拥有自己的构造函数,这时候上面例子整齐划一的方法就不适用了。那么该如何保证子类既能继承父类的方法,又能保留自己的特色呢?别急,首先我们需要更深入一点,了解一下原型链(prototype chain)的概念。

简单整理一下构造函数,原型和实例之间的关系:“每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针”。好,那我们看下以下代码:

 1 function Stark (){
2 this.words = 'Winter is coming.';
3 }
4 function BranStark(){
5 this.pet = 'Summer';
6 }
7 function Summer(){
8 this.favor = 'meat';
9 }
10 BranStark.prototype = new Stark();
11 Summer.prototype = new BranStark();
12
13 summer = new Summer();
14
15 console.log(summer.favor); //meat
16 console.log(summer.pet); //Summer
17 console.log(summer.words); //Winter is coming.
以上展示一个原型链继承的效果。我们让Summer的原型对象等于BranStark的实例,而BranStark的实例中包含一个指向BranStark原型对象的内部指针;而BranStark的原型对象又等于Stark的实例,Stark的实例中包含一个指向Stark原型对象的内部指针。这种关系可以层层递进,形成一条实例与原型的链条,这就是原型链。我在网上找了个图,大概是这样:

在JS中实现继承,大概有两种思路:一个就是我们一开始所讲的使用构造函数;另一个就是上面例子所讲述的原型链继承。但两者各有利弊,构造函数继承会造成资源的浪费,方法和数据难以复用;原型链继承当有包含引用类型值的原型时,则容易造成数据上的混乱。请看下面例子:

 1 function Stark (){ }
2 Stark.prototype.words = ['Winter is coming'];
3 function BranStark(){
4  this.name = '布兰';
5 }
6 function AryaStark(){
7  this.name = '艾莉亚';
8 }
9
10 BranStark.prototype = new Stark();
11 AryaStark.prototype = new Stark();
12
13 aryaStark = new AryaStark();
14 console.log(aryaStark.words); //['Winter is coming.']
15 aryaStark.words.push('Needle is good.');
16 console.log(aryaStark.words); //['Winter is coming.', 'Needle is good.']
17
18 branStark = new BranStark();
19 console.log(branStark.words); //['Winter is coming.', 'Meat is good.']

可以看到当原型内包含引用类型值时,子类实例aryaStark对words属性做出修改后,连其他子类也受到影响啦!Σ(  ̄д ̄;)艾莉亚你这个坑爹货!!咳,回顾下我们一开始所得到的结论:在实现继承的时候,所有实例对象需要共享的属性和方法,可以放在prototype对象里面;那些不需要共享的属性和方法,就放在构造函数里面。所以为了避免艾莉亚这种调皮孩子乱捣蛋,我们可以稍微做点改进!

 1 function Stark (){
2 this.words = ['Winter is coming.'];
3 }
4 function BranStark(){
5  this.name = '布兰';
6 }
7 function AryaStark(){
8  this.name = '艾莉亚';
9 }
10
11 BranStark.prototype = new Stark();
12 BranStark.prototype.constructor = BranStark;
13 AryaStark.prototype = new Stark();
14 AryaStark.prototype.constructor = AryaStark;
15
16 aryaStark = new AryaStark();
17 console.log(aryaStark.words); //['Winter is coming.']
18 aryaStark.words.push('Needle is good.');
19 console.log(aryaStark.words); //['Winter is coming.', 'Needle is good.']
20
21 branStark = new BranStark();
22 console.log(branStark.words); //['Winter is coming.']

_(:з」∠)_于是这样, 把需要被保护的数据放在构造函数就OK了,子类之间既能共享数据又能保证安全。这种继承方式也叫组合继承,是JS最常见的一种。注意到我增加了两行代码:

1 BranStark.prototype.constructor = BranStark;
2 AryaStark.prototype.constructor = AryaStark;

解释下为什么:当我们把一个构造函数的prototype对象赋值给一个实例,我们相当于把该prototype对象完全删除,赋予一个新值。而每一个prototype对象都有一个constructor属性,指向它的构造函数。重新赋值后,constructor属性被改变,改为指向父类的构造函数。所以为了不会导致继承链的紊乱,我们需要手动把原型对象的构造函数指针重新指向自己。

好了终于写完了,希望大家看得明白!\( ̄▽ ̄)/

原文链接:http://www.cnblogs.com/waitforwind/p/3674766.html

深入浅出理解Javascript原型概念以及继承机制(转)的更多相关文章

  1. 深入理解javascript原型和闭包(6)——继承

    为何用“继承”为标题,而不用“原型链”? 原型链如果解释清楚了很容易理解,不会与常用的java/C#产生混淆.而“继承”确实常用面向对象语言中最基本的概念,但是java中的继承与javascript中 ...

  2. 彻底理解Javascript原型继承

    彻底理解Javascript原型继承 之前写过一篇Javascript继承主题的文章,这篇文章作为一篇读书笔记,分析的不够深入. 本文试图进一步思考,争取彻底理解Javascript继承原理 实例成员 ...

  3. 深入理解javascript原型和闭包(5)——instanceof

    又介绍一个老朋友——instanceof. 对于值类型,你可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/ ...

  4. 《深入理解javascript原型和闭包系列》 知识点整理(转)

    深入理解javascript原型和闭包系列 对原型和闭包等相关知识的讲解,由浅入深,通俗易懂,每个字都值得细细研究. 一.一切都是对象 1. typeof操作符输出6种类型:string boolea ...

  5. 《深入理解javascript原型和闭包系列》 知识点整理

    深入理解javascript原型和闭包系列 对原型和闭包等相关知识的讲解,由浅入深,通俗易懂,每个字都值得细细研究. 一.一切都是对象 1. typeof操作符输出6种类型:string boolea ...

  6. 深入理解javascript原型和闭包 (转)

    该教程绕开了javascript的一些基本的语法知识,直接讲解javascript中最难理解的两个部分,也是和其他主流面向对象语言区别最大的两个部分--原型和闭包,当然,肯定少不了原型链和作用域链.帮 ...

  7. 深入理解javascript原型和闭包系列

    从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然不至于上头条,但是也算是中规中矩,有看的人,也有评 ...

  8. 深入理解javascript原型和闭包(1)——一切都是对象

    “一切都是对象”这句话的重点在于如何去理解“对象”这个概念. ——当然,也不是所有的都是对象,值类型就不是对象. 首先咱们还是先看看javascript中一个常用的函数——typeof().typeo ...

  9. 深入理解javascript原型和闭包(7)——原型的灵活性

    在Java和C#中,你可以简单的理解class是一个模子,对象就是被这个模子压出来的一批一批月饼(中秋节刚过完).压个啥样,就得是个啥样,不能随便动,动一动就坏了. 而在javascript中,就没有 ...

随机推荐

  1. Java多线程常见问题

    1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环境是一个包含了不同的类和 ...

  2. Git命令图片版

  3. Android商城开发系列(五)—— 商城首页回到顶部和搜索框布局实现

    今天我们来开发商城的首页[输入搜索框]布局和点击右下角图片回到顶部的效果 搜索功能在App中很常见,尤其是在商城类的项目当中,一般都会提供很强大的搜索功能,App的搜索布局一般都是在App的顶部,如下 ...

  4. 结构化查询语言-SQL

    结构化查询语言(Structured Query Language)简称SQL(发音:/ˈes kjuː ˈel/ "S-Q-L"),是一种特殊目的的编程语言,是一种数据库查询和程 ...

  5. 如何从Ubuntu 16.04 LTS升级到Ubuntu 18.04 LTS

    可以说非常简单(假设过程顺利!!) 您只需打开Software&Update,进入"Updates"选项卡,然后从“有新版本时通知我”下拉菜单中选择“适用长期支持版”选项. ...

  6. nginx之HTTP模块配置

     listen   指令只能使用与server字段里 如果本地调用可以监听本地Unix套接字文件,性能更加,因为不用走内核网络协议栈 listen unix:/var/run/nginx.sock; ...

  7. 如何远程连接Windows server上的MySQL服务

    废话不多说,直接开干 首先要打开服务器的MySQL端口号:3306(当然,也可以把服务器的防火墙直接关闭,不过不安全) 1.打开服务器管理器,有个高级安全Windows防火墙,下面有一个入站规则, 右 ...

  8. js菜鸟备忘

    1.图片切换 function changeImage() { var img = document.getElementById("myImg"); ")) img.s ...

  9. webpack开始一个项目的步骤

    这几天在学习Vue  用到了webpack打包工具  开始一个项目的时候  需要配置很多项  刚开始写的时候  配置文件总是缺什么再去配置什么  创建项目就用了半个小时  后来觉得应该有个步骤  这样 ...

  10. B1002 写出这个数

    读入一个正整数 n,计算其各位数字之和,用汉语拼音写出和的每一位数字. 输入格式: 每个测试输入包含 1 个测试用例,即给出自然数 n 的值.这里保证 n 小于 1. 输出格式: 在一行内输出 n 的 ...