JavaScript-原型&原型链&原型继承

JavaScript的原型是一个重要的知识点,很多扩展应用都是从原型出发的。要说原型,我们先简单说一下函数创建过程。上一篇文章用闭包实现类和继承中用的是原型继承,今天就讲一讲原型继承。更多继承在后面的文章中更新。

函数创建过程

function Xzavier() {};

1.创建一个对象(有constructor属性及[[Prototype]]属性),其中[[Prototype]]属性不可访问、不可枚举。
2.创建一个函数(有name、prototype属性),再通过prototype属性 引用第1步创建的对象。
3.创建变量Xzavier,同时把函数的引用赋值给变量Xzavier。

 

构造函数是用来新建同时初始化一个新对象的函数,所以,任何一个函数都可以是构造函数。只是我们在写代码的时候一般首字母大写以便区分使用。

原型

每个函数在创建的时候js都自动添加了prototype属性,这就是函数的原型,原型就是函数的一个属性,类似一个指针。原型在函数的创建过程中由js编译器自动添加。

function Xzavier() {
this.name = 'xzavier';
this.sex = 'boy';
this.job = "jser";
}
//给A添加属性
Xzavier.age = 23;
//给A的原型对象添加属性
Xzavier.prototype.sports = function() {console.log('basketball')}
Xzavier.prototype = {
hobbit1: function() {console.log('basketball');},
hobbit2: function() {console.log('running');}
};

  

原型链

在JavaScript中,每个对象都有一个[[Prototype]]属性,其保存着的地址就构成了对象的原型链
[[Prototype]]属性是js编译器在对象被创建时自动添加的,其取值由new运算符的右侧参数决定。字面量的方式可转化为new Obejct();

var x = new Xzavier();
vae o = {}; //var o = new Obejct();

  

通过对象的[[Prototype]]保存对另一个对象的引用,通过这个引用往上进行属性的查找,这就是原型链查找机制

对象在查找某个属性的时候,会首先遍历自身的属性,如果没有则会继续查找[[Prototype]]引用的对象,如果再没有则继续查找[[Prototype]].[[Prototype]]引用的对象,依次类推,直到[[Prototype]].….[[Prototype]]undefined

var str = new String('xzavier');
str

  

Object.prototype的[[Prototype]]就是undefined

function Xzavier() {
this.name = 'xzavier';
}
var x = new Xzavier();
x.age = 23; console.log(x.job); // 获取x的job属性 undefined

  

1、遍历x对象本身,结果x对象本身没有job属性
2、找到x的[[Prototype]],也就是其对应的对象Xzavier.prototype,同时进行遍历。 Xzavier.prototype也没有job属性
3、找到Xzavier.prototype对象的[[Prototype]],指向其对应的对象Object.prototype。Object.prototype也没有job属性
4、寻找Object.prototype的[[Prototype]]属性,返回undefined。

函数的变量和内部函数

说了函数的原型链,这里需要说一下的变量和内部函数。

私有变量和内部函数

私有成员,即定义函数内部的变量或函数,外部无法访问。

function Xzavier(){
var name = "xzavier"; //私有变量
var sports = function() {console.log('basketball')}; //私有函数
}
var x = new Xzavier();
x.name; //undefined
如果要访问,需要对外提供接口。 function Xzavier(){
var name = "xzavier"; //私有变量
var sports = function() {console.log('basketball')}; //私有函数
return{
name: name,
sports: sports
}
}
var x = new Xzavier();
x.name; //"xzavier"

  

静态变量和内部函数

用点操作符定义的静态变量和内部函数就是实例不能访问到的变量和内部函数。只能通过自身去访问。

function Xzavier(){
Xzavier.name = "xzavier"; //静态变量
Xzavier.sports = function() {console.log('basketball')}; //静态函数
}
Xzavier.name; //"xzavier"
var x = new Xzavier();
x.name; //undefined

  

实例变量和内部函数

通过this定义给实例使用的属性和方法。

function Xzavier(){
this.name = "xzavier"; //实例变量
this.sports = function() {console.log('basketball');}; //实例函数
}
Xzavier.name; //undefined
var x = new Xzavier();
x.name; //"xzavier"

  

原型链继承

有了原型链,就可以借助原型链实现继承。

function Xzavier() {
this.name = 'xzavier';
this.sex = 'boy';
this.job = "jser";
} function X() {};

  

X的原型X.prototype原型本身就是一个Object对象。F12打开控制台输入函数,再打印X.prototype:

Object {
constructor: X()
__proto__: Object
}

  

prototype本身是一个Object对象的实例,所以其原型链指向的是Object的原型。

X.prototype = Xzavier.prototype

X.prototype = Xzavier.prototype;

  

这样相当于把X的prototype指向了Xzavier的prototype;
这样只是继承了Xzavier的prototype方法,Xzavier中的自定义方法则不继承。

X.prototype.love = "dog";

  

这样也会改变Xzavier的prototype,所以这样基础就不好。

X.prototype = new Xzavier()

X.prototype = new Xzavier();

  

这样产生一个Xzavier的实例,同时赋值给X的原型,也即X.prototype相当于对象:

{
name: "xzavier",
sex: "boy",
job: "jser",
[[Prototype]] : Xzavier.prototype
}

  

这样就把Xzavier的原型通过X.prototype.[[Prototype]]这个对象属性保存起来,构成了原型的链接。
不过,这样X产生的对象的构造函数发生了改变,因为在X中没有constructor属性,只能从原型链找到Xzavier.prototype,读出constructor:Xzavier。

var x = new X;
console.log(x.constructor); 输出:
Xzavier() {
this.name = 'xzavier';
this.sex = 'boy';
this.job = "jser";
}

  

手动改正:

X.prototype.constructor = X;

这是X的原型就多了个属性constructor,指向X。这样就OK。

function Xzavier() {
this.name = 'xzavier';
this.sex = 'boy';
this.job = "jser";
} function X(){}
X.prototype = new Xzavier();
var x = new X()
x.name // "xzavier"

  

[[Prototype]],__proto__,prototype

关于我们经常遇到的[[Prototype]],__proto__,prototype

我们在控制台打印 var str = new String('xzavier'),展开查看属性时,只会看到__proto__,所以起作用的是__proto____proto__是对象的内置属性,是每个对象都有的属性,但是这个属性使用不标准,所以不建议直接使用。但是,我们的原型链就是基于 __proto__的。通过构造函数得到的实例的 __proto__ 属性,指向其对应的原型对象 String.prototype,这正如文中我们打印 var str = new String('xzavier') 中看到的一样。

[[Prototype]]是一个隐藏属性,指向的是这个对象的原型。几乎每个对象有一个[[prototype]]属性。

prototype是每个函数对象都具有的属性,指向原型对象,如果原型对象被添加属性和方法,那么由应的构造函数创建的实例会继承prototype上的属性和方法,这也是我们在代码中经常遇到的。构造函数产生实例时,实例通过其对应原型对象的 constructor 访问对应的构造函数对象。所以,我们继承出来的实例往往没有constructor,只是通过原型链查找,会让我们产生错觉,可参见本系列原型链文章。

hasOwnProperty

hasOwnProperty是Object.prototype的一个方法,判断一个对象是否包含自定义属性而不是原型链上的属性。
hasOwnProperty 是JavaScript中唯一一个处理属性但是不查找原型链的函数。

function Xzavier() {
this.name = 'xzavier';
this.sex = 'boy';
this.job = "jser";
}
//给A的原型对象添加属性
Xzavier.prototype.sports = function() {console.log('basketball');}; var x = new Xzavier();
x.name; // 'xzavier'
'sex' in x; // true x.hasOwnProperty('job'); // true
x.hasOwnProperty('sports'); // false

  

当检查对象上某个属性是否存在时,hasOwnProperty 是非常推荐的方法。

继承在js中使用频繁。ES6也设计了专门的CLASS语法糖供开发者使用。
更多继承方法在新的文章中更新...

难得周末,应该运动O(∩_∩)O~ 打打篮球,运动运动,有代码,有篮球,有生活。。。
长时间不动肩膀(其实身体各地方都是),还真疼啊。希望程序猿们都健健康康的!!

参考资料:https://segmentfault.com/a/1190000006876041#articleHeader0

作者:xzavier

JavaScript-原型&原型链&原型继承的更多相关文章

  1. 玩转JavaScript OOP[3]——彻底理解继承和原型链

    概述 上一篇我们介绍了通过构造函数和原型可以实现JavaScript中的“类”,由于构造函数和函数的原型都是对象,所以JavaScript的“类”本质上也是对象.这一篇我们将介绍JavaScript中 ...

  2. JavaScript原型链与继承

    最近学习了<Javascript高级程序设计>面向对象部分,结合书中的例子总结一下原型链和继承部分的内容. 创建对象 在Js当中没有类这个概念,当我们想要创建具有相同属性的对象的时候,有如 ...

  3. 《JAVASCRIPT高级程序设计》根植于原型链的继承

    继承是面向对象的语言中,一个最为津津乐道并乐此不疲的话题之一.JAVASCRIPT中的继承,主要是依靠原型链来实现的.上一篇文章介绍过,JAVASCRIPT中,每一个对象都有一个prototype属性 ...

  4. 对Javascript 类、原型链、继承的理解

    一.序言   和其他面向对象的语言(如Java)不同,Javascript语言对类的实现和继承的实现没有标准的定义,而是将这些交给了程序员,让程序员更加灵活地(当然刚开始也更加头疼)去定义类,实现继承 ...

  5. JavaScript中的原型链和继承

    理解原型链 在 JavaScript 的世界中,函数是一等公民. 上面这句话在很多地方都看到过.用我自己的话来理解就是:函数既当爹又当妈."当爹"是因为我们用函数去处理各种&quo ...

  6. JavaScript原型链和继承

    1.概念 JavaScript并不提供一个class的实现,在ES6中提供class关键字,但是这个只是一个语法糖,JavaScript仍然是基于原型的.JavaScript只有一种结构:对象.每个对 ...

  7. JavaScript: 认识 Object、原型、原型链与继承。

    目录 引用类型与对象 类与对象 成员组成 成员访问 实例方法 / 属性 引用类型与对象 JavaScript 存在着两种数据类型:"基本数据类型" 与 "引用数据类型&q ...

  8. 三张图搞懂JavaScript的原型对象与原型链 / js继承,各种继承的优缺点(原型链继承,组合继承,寄生组合继承)

    摘自:https://www.cnblogs.com/shuiyi/p/5305435.html 对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__pro ...

  9. javascript精髓篇之原型链维护和继承.

    一.两个原型 很多人都知道javascript是原型继承,每个构造函数都有一个prototype成员,通过它就可以把javascript的继承演义的美轮美奂了. 其实啊,光靠这一个属性是无法完成jav ...

  10. [转]深入javascript——原型链和继承

    在上一篇post中,介绍了原型的概念,了解到在javascript中构造函数.原型对象.实例三个好基友之间的关系:每一个构造函数都有一个“守护神”——原型对象,原型对象心里面也存着一个构造函数的“位置 ...

随机推荐

  1. R语言适配问题集锦

    画图时的中文乱码问题 我这是Mac Yousemite 10.10.5,在两个地方遇到了中文乱码 1.使用wordcloud包绘制中文标签云时. library(wordcloud) mydata & ...

  2. Invalid project description.

    1.错误描写叙述 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/ ...

  3. C++写的UrlEncode和UrlDecode

    关于UrlEncode的实现(C++).网上有非常多不同的版本号.对须要编码的字符集的选取并不统一.那么究竟有没有标准呢?答案是有的.參见wiki 绝对不编码的,仅仅有字母.数字.短横线(-).下划线 ...

  4. Android笔记——Activity中的数据传递案例(用户注冊)

    1.创建程序activity_main: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andro ...

  5. spark rdd saveAsTextFile保存为文件

    sc.parallelize(["one", "two", "two", "three", "three&qu ...

  6. hdoj--5569--matrix(动态规划)

    matrix Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Sub ...

  7. linux下修改完profile文件的环境变量后如何立即生效

    方法1: 让/etc/profile文件修改后立即生效 ,可以使用如下命令: # . /etc/profile 注意: . 和 /etc/profile 有空格 方法2: 让/etc/profile文 ...

  8. iOS开发——根据数组中的字典中的某一元素排序

    数组中的元素是字典,字典中的某一个元素,比如说姓名,现在需要按照姓名的首字母来排序,怎么搞? 做法很简单,在字典中加一个元素,保存姓名的首字母,然后用下面的方法排序. - (void)sortWifi ...

  9. [USACO07MAR]每月的费用Monthly Expense

    题目:POJ3273.洛谷P2884. 题目大意:有n个数,要分成m份,每份的和要尽可能小,求这个情况下和最大的一份的和. 解题思路:二分答案,对每个答案进行贪心判断,如果最后得出份数>m,则说 ...

  10. [APIO2010]巡逻(树的直径)

    [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到 ...