原型prototype与__proto__

先简单理解以下几句话:

  • 每一个对象都有一个__proto__属性,并且指向它的prototype原型对象
  • 每一个构造函数都会有一个prototype原型对象,prototype原型对象里的constructor指向构造函数本身

直接看下面代码:

function Person(name, age, sex) {this.name = namethis.age = agethis.sex = sex
}
// 先打印看看Person是什么
// 打印window,从window上找到Person
console.log(window)

以下是Person的打印结果:


可以看到Person为一个构造函数,而这个构造函数身上有一个prototype属性,该属性的constructor又指向该构造函数本身。接下来让该构造函数创建一个实例,看卡这个实例身上有哪些内容。

function Person(name, age, sex) {this.name = namethis.age = agethis.sex = sex
}var p1 = new Person('qwe', 12, male)
console.log(p1)
console.log(p1.prototype)
console.log(p1.__proto__)

)

__proto__是[[Prototype]]的getter和setter,这里可以简单的将[[prototype]]理解为__proto__。从上图可以看出p1身上有一个__proto__属性,该属性指向自己的原型对象,而p1自身是没有prototype的。总的来说就是,对象自身的__proto__属性会指向自己的构造函数的prototype,即p1.__proto__ === Person.prototype

原型链

原型链简单的理解就是,多个不同的prototype之间通过__proto__链接成一条链(抽象的一种说明方式)。
如何体现出原型链有什么用,直接看下面代码:

function Person(name, age, sex) {this.name = namethis.age = agethis.sex = sex
}
// 在构造函数Person的prototype上增加一个sayName方法
Person.prototype.sayName = function() {console.log(this.name)
}

上面说到过p1.__proto__ === Person.prototype,那可以想到p1.__proto__身上也有sayName方法,通过以下几种调用方法来看一下分别是什么结果:
先看一下p1的结构:

p1.sayName() // abc
p1.__proto__.sayName() // undefined
// 这里之所以是undefined是因为this指向的是p1.__proto__,p1.__proto__身上并没有name属性

意思就是说我在构造函数的原型上添加一个方法后,在它的实例身上也会存在该方法。那如果将该实例的__proto__指向别的构造函数的原型呢?看以下代码:

function Person(name, age, sex) {this.name = namethis.age = agethis.sex = sex
}function Dog(name, age) {this.name = namethis.age = age
}Person.prototype.sayName = function() {console.log(this.name)
}
Dog.prototype.sayName = function() {console.log(this.name + '  dog')
}var p1 = new Person('abc', 123, 'male')// 改变p1的__proto__指向
p1.__proto__ = Dog.prototype// p1的sayName方法为Dog原型上的sayName方法
p1.sayName() // abc  dog

从上面可以看出实例的__proto__是可以更改的,__proto__指向谁的原型,就能够获取该原型上的方法。
再看一个例子:

var arr = [1, 2, 3]
arr.valueOf() // [1, 2, 3]
console.log(arr.__proto__ === Array.prototype) // true
console.log(arr.__proto__.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__) // null

这里就体现出原型链的结构,arr身上是没有valueOf方法的,但是它的__proto__指向Array.prototype,所以会去Array.prototype上找valueOf方法,发现Array.prototype身上也没有valueOf方法,但是Array.prototype.__proto__指向Object.prototype,所以又会去Object.prototype上找到valueOf方法。

继承

继承就是说一个对象可以得到另外一个对象身上的属性和方法,怎么得到,直接看下面事例:
假设我想写一个造车的方法,车的大小和模型的制作过程一样,只有车的名字需要定制。

// 制造车的基本信息
function CarBaseInfo(size, model) {this.size = size + 1this.model = '超大号:' + model
}// 制造车的名字,基本信息的制作过程都一致
function CarName(name, size, modle) {// 使得this指向CarName// 如果不使用call,this指向windowCarBaseInfo.call(this, size, modle)this.name = name + 'a'
}var car1 = new CarName('兰博基尼', 123, '1')
console.log(car1)

如何继承方法,上面原型链中已经提到过,在构造函数的原型上增加方法,让对象的__proto__指向它即可。

function CarBaseInfo(size, model) {this.size = size + 1this.model = '超大号:' + model
}CarBaseInfo.prototype.speed = function() {console.log('加速')
}function CarName(name, size, modle) {CarBaseInfo.call(this, size, modle)this.name = name + 'a'
}// 按照思路来说,将CarBaseInfo的原型赋给CarName的原型,car1上就会有speed方法
CarName.prototype = CarBaseInfo.prototypevar car1 = new CarName('兰博基尼', 123, '1')
console.log(car1.speed()) // 加速

这里会存在一个问题,对象是引用类型,意思就是说CarName.prototype = CarBaseInfo.prototype这样赋值后,CarName去更改自己原型上的speed方法时,CarBaseInfo原型上的speed方法也会跟着更改,说好只让你使用,可没让你更改,这样肯定是不允许的。所以这里需要完全将CarBaseInfo.prototype拷贝过来,并且将CarBaseInfo.prototype的constructor指向CarName自己。

// 这里使用Object.create方法,该方法为新建一个对象
CarName.prototype = Object.create(CarBaseInfo.prototype)
// 然后再将CarName.prototype的constructor指向构造函数本身
CarName.prototype.constructor = CarName

完整实现:

// 制造车的基本信息
function CarBaseInfo(size, model) {this.size = size + 1this.model = '超大号:' + model
}CarBaseInfo.prototype.speed = function() {console.log('加速')
}// 制造车的名字,基本信息的制作过程都一致
function CarName(name, size, modle) {CarBaseInfo.call(this, size, modle)this.name = name + 'a'
}CarName.prototype = Object.create(CarBaseInfo.prototype)
CarName.prototype.speed = function() {console.log('减速')
}var car1 = new CarName('兰博基尼', 123, '1')
var car2 = new CarBaseInfo(12, '222')
console.log(car1.speed()) // 减速
console.log(car2.speed()) // 加速

相关API

1. hasOwnProperty

用来检测该对象身上是否拥有某个属性,只会检测到自身属性,并不会检测继承来的属性,
注意:hasOwnProperty是可以作为属性名在对象身上的,所以在检测的时候可以采用Object.prototype.hasOwnProperty.call()的方式来检测。

2. instanceof

instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

function Person(name) {this.name = name
}
function Student(name) {Person.call(this, name)
}var p1 = new Student('123')
console.log(p1 instanceof Person) // falsep1.__proto__ = Person.prototype
console.log(p1 instanceof Person) // true
function C() {}
function D() {}
D.prototype = new C() // D.prototype.__proto__ === C.prototypevar o3 = new D() // o3.__proto__ === D.prototypeo3 instanceof D // true
o3 instanceof C // true C.prototype在o3的原型链上
console.log(o3.__proto__ === D.prototype)
console.log(o3.__proto__.__proto__ === D.prototype.__proto__)
console.log(o3.__proto__.__proto__ === C.prototype)

A instanceof B 即判断B的prototype是否在A的原型链上

JavaScript原型、原型链、继承相关推荐

  1. JavaScript进阶学习(二)—— 基于原型链继承的js工具库的实现方法

    文章来源:小青年原创 发布时间:2016-07-03 关键词:JavaScript,原型链,jQuery类库 转载需标注本文原始地址: http://zhaomenghuan.github.io... ...

  2. JavaScript:原型链、继承

    1.理解原型对象 我们先使用构造函数创建一个对象: function Person() { } var person = new Person(); person.name = 'Kevin'; co ...

  3. JavaScript简餐——继承之原型链继承

    文章目录 前言 一.实现方式 二.继承实例 三.问题所在 1.引用值误修改 2.子类型实例化时无法给父类构造函数传参 四.总结 前言 写本<JavaScript简餐>系列文章的目的是记录在 ...

  4. javascript 原型链继承

    JavaScript中没有类的概念,只有一个构造函数来创建对象. 但是JavaScript也可以实现继承. 首先要说的是,JavaScript中的对象分为函数对象和普通对象. 何为函数对象?? 就是 ...

  5. Javascript 对象继承 原型链继承 对象冒充 call 混合方式

    一.原型链继承 function ClassA() {} ClassA.prototype.color = "blue"; ClassA.prototype.sayColor = ...

  6. JavaScript: 原型链继承(原理解析 + 代码实现 + 结构图解)

    文章目录 一 原型搜索机制 1.1 代码实现 1.2 结构图解 1.3 搜索机制 二 原型链代码实现 2.1 代码实现 2.2 结构图解 2.3 链条拓展 三 原型链的缺点 原型链是实现继承的一种方式 ...

  7. Javascript的原型链、instanceof与typeof

    为什么80%的码农都做不了架构师?>>>    在Javascript里,一切对象(Object和Function)都有内部的属性_proto_,但只Object.prototype ...

  8. 深入浅出理解Javascript原型概念以及继承机制(转)

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

  9. 【面试必备】javascript的原型和继承

    摘要: 原型.闭包.作用域等知识可以说是js中面试必考的东西,通过你理解的深度也就能衡量出你基本功是否扎实.今天来复习一下javascript的原型和继承,虽说是老生常谈的话题,但对于这些知识,自己亲 ...

  10. ES6 继承(复习原型链继承)

    2019独角兽企业重金招聘Python工程师标准>>> 原型链继承 <script type="text/javascript">/* 原型链 继承 ...

最新文章

  1. 新建QQ群-欢迎加入
  2. html语言hr的用法,HTML hr noshade 属性 | Paoo教程
  3. 用Python实现归并排序
  4. linux 脚本 试题,10个Linux脚本面试题,看看你能答出几个?
  5. java filechannel 性能_FileChannel 和 MappedByteBuffer 实现上有什么不同?为什么性能差这么多?...
  6. web前端设计必备网页特效案例 - 轮播图
  7. 转录组学分析之基因芯片的预处理
  8. 红外遥控器的驱动函数
  9. [ffmpeg][vaapi][goav][golang] ffmpeg使用vaapi示例代码(基于goav-incr)
  10. Android 实现图片倒影效果
  11. 【OI生涯】我学OI是为了什么?
  12. csgo社区自建服务器,CSGO官方社区服黄页测试版上线
  13. mysql 提取字符串首字母_SQL获取字段字符串中文首字母
  14. 二手 IBM 3650M4 IMM 无法正常访问的解决方法
  15. 计算机科学专业和商科专业排名,2017年QS世界大学专业排名权威发布
  16. JavaScript 日期和时间的格式化大汇总(收集)
  17. 【单片机】矩阵键盘函数
  18. 计算机毕业做项目管理,计算机专业毕业论文-项目管理系统.doc
  19. 函数的四种特性——1、有界性2、单调性3、奇偶性4、周期性
  20. Oracle入门教程与实战

热门文章

  1. 电商运营工作中要经历的十件事
  2. Thinkphp3全漏洞分析
  3. 关于主板的零散整理(不定期更新)
  4. 【推荐算法】今日头条推荐系统原理
  5. Mac rar文件解压
  6. vue获取tr内td里面所有内容_vue的v-for循环添加tr,取tr中的某两个td进行运算
  7. VS2008如何更改序列号再安装
  8. 服务器磁盘IO性能调优
  9. 百变星君---用户模块
  10. 数字加逗号函数PHP函数,php实现数字格式化,数字每三位加逗号的功能函数