JavaScript 核心学习——继承
本篇博文讲述如何在 JavaScript 中实现继承,以及原型与原型链的知识,在附录中将会讲述 JavaScript 面向对象
的常见错误。
##原型与原型链
在 JavaScript 中,使用类将会付出很大的代价,因此我们使用原型。原型不同于类,类代表复制。而 Javascript 使用
的是委托行为。
###原型与原型链
JavaScript 中有一个 [[prototype]] 属性,指向对应的原型对象。在一个对象中查找一个属性,如果本对象中有此
属性,那么就会读取本对象的属性,如果本对象中没有,则会往对象的 prototype 对象去找,这个有点像原型对象版的
作用域链。举个例子:
1 |
function (){
|
构建一个名为 person 的对象实例,但是原本并没有在 person 中设置 isChild 方法,这个方法的引用是通过查找对象
的 prototype ,引用了 isChild 方法。而对于对象的 sayParentName 方法,是因为 Child.prototype 继承了 Parent.
prototype 对象,因此在 person 中没有找到 sayParentName, 转而查找 Child.prototype ,发现 Child.prototype 上
也没有,查找 Parent.prototype,查找到了 sayParentName 方法使用。
缺一个说明代码中的关系的原型图
而对于对象的原型链来说,无论它是哪种类型的对象(函数,数组等等),它们都是对象,既然是对象,那么就会有原型
链,对于所有对象来说,一般它们的原型链尽头是 Object.prototype,即是说定义在 Object 上面的方法,对象都可以
使用。
###属性屏蔽
在对象读取属性与设置属性时都会发生属性屏蔽,如果本对象有该属性,则不会向原型链中查找属性,但是如果是设置,
情况就会有所不同。在设置属性的时候,分为 3 种情况。如果本对象中有这个属性,那么就会直接覆盖,但是如果这个
对象中没有,那么就会向原型链中查找,如果属性名既存在本对象又存在原型链中,那么就会发生屏蔽。属性选择总是
会选择最底层的那个属性。
1 |
obj.foo = "bar"; |
但是如果属性存在于原型链的上层时,有三种情况:
- 如果上层的属性为数据属性并且不是只读的,那么就会在这个对象中添加一个屏蔽属性。
1
2
3
4
5
6
7
8
9
10
11
12function Foo(){} Foo.prototype = {
constructor: Foo,
a: 1
} var obj = new Foo();
obj.a = 2;
console.log(obj.hasOwnProperty("a")); //true
console.log(obj.a); // 2
console.log(obj.__proto__.a); // 1
2.如果上层属性存在该属性,但是它为只读,那么就无法修改属性或在对象中添加属性,在严格模式会抛出错误。
1 |
function Foo(){}
Foo.prototype = {
|
3.如果上层存在该属性,并且该属性是访问器属性 [[Set]], 那么就会调用这个 set 函数,属性不会添加到该对象上。
1 |
function Foo(){}
Object.defineProperty(Foo.prototype, "a", {
|
如果想要查看某个实例是否是某个原型的实例,使用 isPrototype() 。
##实现继承
继承的本质在于重写原型对象,往往通过重写构造函数的原型对象去实现继承。
###利用原型链
1 |
function (){}
Parent.prototype = {
|
利用原型链确实可以实现继承,但是缺点在于对于象数组这样的属性的时候,每个实例共享属性,如果在一个实例中
修改,那么在所有实例中都会变化,缺乏安全性。所以有了接下来的借用构造函数。
###借用构造函数
1 |
function (){
|
这样使用 call 方法改变 this 指向,借用构造函数确实可以解决每个实例的属性共用问题,但是有些属性并不需要每
个实例单独拥有,但是每次调用都会为每个实例创建属性,代码复用无从谈起,因此有了组合模式。
###组合继承
1 |
function (){
|
组合继承的核心在于通过 call 方法构造实例独有的属性,并且提高了代码复用率。需要注意的是这里的组合继承与构造
函数的组合模式并不相同,组合继承是其他对象与本对象的联系,而组合模式是构造单个对象时采用的策略。
组合继承是最常用的继承方式。
对象的构造函数
###原型继承
1 |
function object(o) { //Object.create 其实内部类似于这样
|
原型继承在内部创建一个临时的构造函数,内部其实是对传入的参数进行了一次浅复制。如果想要让一个对象与另一个
对象保持类似,原型式继承完全可以满足,而不用去创建组合模式。而 ES5 对原型模式规范化后更为简单,提供了一个
Object.create() 方法,该方法接受两个参数,第一个是基对象,第二个是重写属性集合。
1 |
Object.create(obj, {
|
###寄生继承
1 |
function createObject (original){
|
这种方式通过封装,在内部增强对象并返回对象。
###寄生组合继承
1 |
function (){
|
对比于组合模式,组合模式在构建 Son 原型对象和 Son 的实例对象的时候,调用了两次 Parent 函数,导致在 Son
原型中也拥有一份 Parent 的属性,但是在实际中,这个并不需要,因为如果不重写,意味着 Son 的原型与 Parent 的
属性相同,但是这个本来就是要通过继承来解决的,因此寄生组合模式就是解决这个问题的,内部的 Object.create
只是将 Son 的原型对象链接到 Parent 的原型对象。
##附录:常见的理解错误
- 实际上,在 JavaScript 中的继承并不叫继承,因为继承代表着复制,但是在 JavaScript 中继承并没有复制操作,而是 委托 。JavaScript 中的继承实际上是通过这个对象与另外一个对象进行关联,之间并没有明确的父子类的关系。
- 在实现继承的时候,推荐使用 Object.create 这个方法,虽然会导致轻微的性能损失(遗弃对象的垃圾回收),但是
相比与 new 命令,免去了创建很多不必要的属性。 - 检查类的关系,检查对象 a 与对象 b 的关联性的话,推荐使用 a.isPrototypeOf(b) 这个方法。这个的意思是 a
是否出现在 c 的原型链中,Object.getPrototypeOf() 这个方法可以获取对象的整个原型链。 - 对象内置的 .proto 属性其实是一个 getter/setter 函数。
1
2
3
4
5
6
7
8
9Object.defineProperty(object.prototype, "__proto__", {
get: function(){
return object.getPrototypeOf(this);
},
set: function(o){
Object.setPrototypeOf(this, o);
return o;
}
});
5.Object.create(null) 会创建 [[prototype]] 链接为空的对象,这样的对象被称为 字典 ,适合用来存储数据。
6.在 ES5 之前的环境中,实现 Object.create() 。
1 |
if(!Object.create) {
|
JavaScript 核心学习——继承的更多相关文章
- JavaScript学习13 JavaScript中的继承
JavaScript学习13 JavaScript中的继承 继承第一种方式:对象冒充 <script type="text/javascript"> //继承第一种方式 ...
- 最新的JavaScript核心语言标准——ES6,彻底改变你编写JS代码的方式!【转载+整理】
原文地址 本文内容 ECMAScript 发生了什么变化? 新标准 版本号6 兑现承诺 迭代器和for-of循环 生成器 Generators 模板字符串 不定参数和默认参数 解构 Destructu ...
- JavaScript 核心参考教程 内置对象
这个标准基于 JavaScript (Netscape) 和 JScript (Microsoft).Netscape (Navigator 2.0) 的 Brendan Eich 发明了这门语言,从 ...
- JavaScript 基础学习1-day14
JavaScript 基础学习1 知识预览JavaScript概述二 JavaScript的基础三 JavaScript的对象BOM对象DOM对象实例练习js扩展 JavaScript概述 JavaS ...
- 最新的JavaScript核心语言标准——ES6,彻底改变你编写JS代码的方式!
原文地址 迁移到:http://www.bdata-cap.com/newsinfo/1741515.html 本文内容 ECMAScript 发生了什么变化? 新标准 版本号6 兑现承诺 迭代器和f ...
- Javascript的学习清单
Javascript的学习清单 Javascript学习资源 程序员必读书籍 深入理解JavaScript系列 es6教程 jQuery中文文档 vue官网 zeptojs中文版 常用的插件与UI组件 ...
- javascript 核心语言笔记- 3 - 类型、值和变量
JavaScript 中的数据类型分为两类:原始类型(primitive type)和对象类型(object type).原始类型包括数字.字符串和布尔值 JavaScript 中有两个特殊的原始值: ...
- 浅谈JavaScript中的继承
引言 在JavaScript中,实现继承的主要方式是通过原型链技术.这一篇文章我们就通过介绍JavaScript中实现继承的几种方式来慢慢领会JavaScript中继承实现的点点滴滴. 原型链介绍 原 ...
- JavaScript 类式继承与原型继承
交叉着写Java和Javascript都有2年多了,今天来总结下自己所了解的Javascript类与继承. Javascript本身没有类似Java的面向对象的类与继承术语,但其基于原型对象的思想却可 ...
随机推荐
- win10远程桌面身份验证错误,要求的函数不受支持
出现原因win10最近安装了一个更新补丁导致的,当然可以直接卸载这个补丁,也可以安装操作来就可以.需要修改注册表里边的项目,添加下面的设置. 解决办法1.win+R 输入 regedit, 打开注册表 ...
- pandas(一)
pandas.io 1.概述,主要从txt,json,pkl,csv,excel中读取数据,读取的数据最终转化为pandas.core.frame.DataFrame类型的df 先来看总的api fr ...
- Ansible部署rsync、nfs及sersync
rsync nfs sersync httpd环境: 角色 外网IP(NAT) 内网IP(LAN) 主机名 Rsync服务端 eth0:10.0.1.51 eth1:172.16.1.51 backu ...
- ThreadPoolExecutor自定义线程池
1.ThreadPoolExecutor创建线程池的构造函数 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long ...
- 前端框架Bootstrap(10.7国庆补写)
框架的官网地址:https://v3.bootcss.com/ 主要学习Bootstrap框架提供的样式.组件.插件的使用. 首先下载到本地,在项目中导入使用: 下载的文件中包含:min.css的是压 ...
- java 之断言
今天用idea的智能提示冒出一个assert关键字,愣是没看懂!!!还是太菜了.上网查了一下,这个关键字是断言. 什么是断言? 我也说不清楚,反正就是对jvm的操作.java的错误分为两种,一种叫er ...
- rest framework-视图和路由-长期维护
############### 三种视图 ############### # 第一种方法:使用mixins # class AuthorView(mixins.ListModelMixin, ...
- Apsara Clouder云计算专项技能认证:网站建设-简单动态网站搭建
一.课程介绍 1.课程目标 了解静态网站和动态网站的区别 掌握动态网站的不同实现方式 在阿里云上如何搭建 wordPress网站以及 wordPress 网站的管理和优化 二.网站搭建的类型 1.网站 ...
- php7 安装mongodb扩展
下载 mongodb-1.6.0.tgz wget https://pecl.php.net/get/mongodb-1.6.0.tgz 版本太低的话有些语法不一样,起码1.5以上吧 进入 mo ...
- Go-Micro框架入门教程(一)---框架结构
Go语言微服务系列文章,使用golang实现微服务,这里选用的是go-micro框架,本文主要是对该框架的一个架构简单介绍. 1. 概述 go-micro是go语言下的一个很好的微服务框架. 1.服务 ...