《JavaScript模式》一书中,对于JavaScript的几种继承模式讲解得很清楚,给我提供了很大帮助。总结一下,有如下7种模式。

继承模式1——设置原型(默认模式)

实现方式:

// 父构造函数
function Parent(name) {
this.name = name || 'Adam';
} // 向原型中添加方法
Parent.prototype.say = function() {
return this.name;
} // 子构造函数(空白)
function Child(name) {} // 继承:设置原型
Child.prototype = new Parent(); // 测试
var kid = new Child();
kid.say(); // Adam

原型链:

注意:

  • __proto__属性仅用来解释原型链,不可用于开发中。
  • 若子对象#3定义属性name,并不会修改父对象#2的name属性,而是直接在子对象#3上创建一个自身属性。如果使用delete删除子对象#3的name属性,那么父对象#2的name属性将表现出来。
  • 优点:
    • 子对象继承了:父构造函数中的this属性、父原型中的属性。
  • 缺点:
    • 不支持将参数传递到子构造函数中,而子构造函数然后又将参数传递到父构造函数中。
    • 如果父类构造函数中的this属性为引用类型,可能存在子对象意外覆盖父对象属性的风险。
// 演示缺点1

var s = new Child('Seth');
s.say(); // Adam
// 演示缺点2

// 父构造函数
function Article() {
this.tags = ['js', 'css'];
}
var article = new Article(); // 子构造函数及继承
function Blog() {}
Blog.prototype = article; // 子对象意外修改父对象的引用属性
var blog = new Blog();
blog.tags.push('html');
console.log(article.tags.join(' ')); // js css html

继承模式2——借用构造函数

实现方式:

// 父构造函数
function Parent(name) {
this.name = name || 'Adam';
} // 向原型中添加方法
Parent.prototype.say = function() {
return this.name;
} // 子构造函数
function Child(name) {
// 继承:借用构造函数
Parent.apply(this, arguments);
} // 测试
var kid = new Child('Partrick');
kid.name; // Partick
typeof kid.say; // undefined

原型链:

注意:

  • 缺点:只能继承父构造函数中的this属性,不能继承父原型中的属性。
  • 优点:
    • 本模式解决了从子构造函数到父构造函数的参数传递问题。
    • 子对象可以获得父对象自身成员的副本(而非引用),并且不会存在子对象意外覆盖父对象属性的风险。

继承模式3——设置原型&借用构造函数

实现方式:

// 父构造函数
function Parent(name) {
this.name = name || 'Adam';
} // 向原型中添加方法
Parent.prototype.say = function() {
return this.name;
} // 子构造函数
function Child(name) {
// 继承:借用构造函数
Parent.apply(this, arguments);
} // 继承:设置原型
Child.prototype = new Parent(); // 测试
var kid = new Child('Partrick');
kid.name; // Partick
kid.say(); // Partick
delete kid.name;
kid.say(); // Adam

原型链:

注意:

  • 优点:

    • 能够获得父对象自身成员的副本。子对象可以安全地修改自身属性,且不会带来修改其父对象的风险。
    • 子对象继承了:父构造函数中的this属性、父原型中的属性。
    • 子构造函数可将任意参数传递到父构造函数中。
  • 缺点:
    • 父构造函数被调用了两次,导致其效率低下。自身的属性(name)被继承了两次,删除了子类本身的name属性的副本后,原型链上的name属性将表现出来。

继承模式4——共享原型

实现方式:

// 父构造函数
function Parent(name) {
this.name = name || 'Adam';
} // 向原型中添加方法
Parent.prototype.say = function() {
return this.name;
} // 子构造函数
function Child() {} // 继承:共享原型
child.prototype = Parent.prototype;

原型链:

注意:

  • 本模式适用于:可复用成员应转移到原型中,而不是放置在父类this中。任何值得继承的东西都应该放置在原型中实现。
  • 不能继承父构造函数中的this属性,只能继承父原型中的属性。
  • 缺点:
    • 如果在继承链下方的某处存在一个子对象或孙子对象修改了原型,将会影响到所有父对象和祖先对象。

继承模式5——临时构造函数

实现方式:

// 父构造函数
function Parent(name) {
this.name = name || 'Adam';
} // 向原型中添加方法
Parent.prototype.say = function() {
return this.name;
} // 子构造函数
function Child(name) {} // 继承:设置原型
inherit(Child, Parent); // 实现:
function inherit(C, P) {
var F = function() {};
F.prototype = P.prototype;
C.prototype = new F();
C.prototype.constructor = C;
} // 优化:避免在每次需要继承时,都创建临时(代理)构造函数。
// 实现:即时函数+闭包
var inherit2 = (function() {
var F = function() {};
return function(C, P) {
F.prototype = P.prototype;
C.prototype = new F();
C.prototype.constructor = C;
}
})(); // 测试
var kid = new Child();
kid.say(); // undefined
kid.name = "Peter";
kid.say(); // Peter

原型链:

注意:

  • 子对象仅继承了原型的属性。原型仅用来放置可复用的功能。父构造函数的this中的任何成员都不会被继承。
  • 需要重置子构造函数的指针:C.prototype.constructor = C

继承模式6——原型继承

实现方式:

function object(P) {
var F = function() {};
F.prototype = P;
return new F();
}

对象字面量方式创建父对象

var parent = {
name: "papa"
}
var child = object(parent); // 测试
console.log(child.name);

构造函数方式创建父对象

// 父构造函数
function Parent() {
this.name = "papa";
}
Parent.prototype.getName = function() {
return this.name;
} // 创建一个父对象
var papa = new Parent(); // 继承方式1:父构造函数中的this属性、父原型的属性都被继承
var kid = object(papa);
console.log(typeof kid.name); // string
console.log(typeof kid.getName); // function // 继承方式2:仅继承父原型的属性
var kid = object(Parent.prototype);
console.log(typeof kid.name); // undefined
console.log(typeof kid.getName); // function

ES5: Object.create()


继承模式7——复制属性

浅复制

在使用浅复制时,如果改变了子对象的属性,并且该属性恰好是一个对象,那么这种操作也将修改父对象。

function extend(parent, child) {
var i;
child = child || {};
for (i in parent) {
if (parent.hasOwnProperty(i)) {
child[i] = parent[i];
}
}
return child;
} // 测试
var dad = {
counts: [1, 2, 3],
reads: { paper: true }
};
var kid = extend(dad);
kid.counts.push(4);
dad.counts.toString(); // 1,2,3,4
dad.reads === kid.reads; // true

深复制

检查父对象的某个属性是否为对象,如果是,则需要递归复制出该对象的属性。

function extendDeep(parent, child) {
var i,
toStr = Object.prototype.toString,
astr = "[object Array]";
child = child || {}; for (i in parent) {
if (parent.hasOwnProperty(i)) {
if (typeof parent[i] === 'object') {
child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i]; }
}
}
return child;
} // 测试
var dad = {
counts: [1, 2, 3],
reads: { paper: true }
};
var kid = extendDeep(dad);
kid.counts.push(4);
dad.counts.toString(); // 1,2,3
dad.reads === kid.reads; // false

混入

从多个对象中复制出任意成员,并将这些成员组合成一个新的对象。

遇到同名属性,总是使用靠后对象的值,即越往后优先级越高。

function mix() {
var i, prop, child = {};
for (i = 0; i<arguments.length; i++) {
for (prop in arguments[i]) {
if (arguments[i].hasOwnProperty(prop)) {
child[prop] = arguments[i][prop]; }
}
}
return child;
} // 测试
var cake = mix(
{ eggs: 2, large: true },
{ buter: 1, saleted: true },
{ flour: "3 cups" },
{ sugar: "sure!" },
{ eggs: 3 } // 同名属性,越往后优先级越高
);
console.dir(cake);

JavaScript的7种继承模式的更多相关文章

  1. JavaScript的3种继承方式

    JavaScript的继承方式有多种,这里列举3种,分别是原型继承.类继承以及混合继承. 1.原型继承 优点:既继承了父类的模板,又继承了父类的原型对象: 缺点:不是子类实例传参,而是需要通过父类实例 ...

  2. JavaScript 对象的创建和对6种继承模式的理解和遐想

      JS中总共有六种继承模式,包括原型链.借用构造函数.组合继承.原型式继承寄生式继承和寄生组合式继承.为了便于理解记忆,我遐想了一个过程,对6中模式进行了简单的阐述. 很长的一个故事,姑且起个名字叫 ...

  3. JavaScript常用八种继承方案

    更新:在常用七种继承方案的基础之上增加了ES6的类继承,所以现在变成八种啦,欢迎加高级前端进阶群一起学习(文末). --- 2018.10.30 1.原型链继承 构造函数.原型和实例之间的关系:每个构 ...

  4. JavaScript的几种继承方式

    看<JavaScript高级程序设计>做的一些笔记 ECMAScript只支持实现继承,不支持接口继承(因为函数没有签名) 原型链(实现继承的主要方法): function SuperTy ...

  5. Javascript的四种继承方式

    在Javascript中,所有开发者定义的类都可以作为基类,但出于安全性考虑,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击. 选定基类后,就可 ...

  6. 都0202年了,你还不知道javascript有几种继承方式?

    前言     当面试官问你:你了解js哪些继承方式?es6的class继承是如何实现的?你心中有很清晰的答案吗?如果没有的话,可以通过阅读本文,帮助你更深刻地理解js的所有继承方式.       js ...

  7. javascript的几种继承

    1.原型链继承:构造函数.原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针.确认原型和实例之间的关系用instanceof. ...

  8. 浅谈 JavaScript 中的继承模式

    最近在读一本设计模式的书,书中的开头部分就讲了一下 JavaScript 中的继承,阅读之后写下了这篇博客作为笔记.毕竟好记性不如烂笔头. JavaScript 是一门面向对象的语言,但是 ES6 之 ...

  9. JavaScript几种继承方式的总结

    1.原型链继承 直接将子类型的原型指向父类型的实例,即"子类型.prototype = new 父类型();",实现方法如下: //父类构造函数 function father(n ...

随机推荐

  1. linux文件系统(ext2)

    一个磁盘可以划分成多个分区,每个分区必须先用格式化工具(例如某种mkfs命令)格式化成某种格式的文件系统,然后才能存储文件,格式化的过程会在磁盘上写一些管理存储布局的信息.下图是一个磁盘分区格式化成e ...

  2. libevent 多线程

    对于evbuffer,如果libevent使用了evthread_use_pthreads();那么所有的单个evbuffer操作就已经是原子的了,调用操作相关的接口进去就上锁,出来解锁,那么 evb ...

  3. C#重载和重写

    Overload:重载就是在同一个类中,方法名相同,参数列表不同.参数列表不同包括:参数的个数不同,参数类型不同. using System; using System.Collections.Gen ...

  4. [51nod1482]部落信号 单调栈

    ~~~题面~~~ 题解: 可以发现这是一道单调栈的题目,首先来考虑数字没有重复时如何统计贡献. 因为这是一个环,而如果我们从最高的点把环断开,并把最高点放在链的最后面(顺时针移动),那么因为在最高点两 ...

  5. 停课day2

    感觉今天好颓啊,我才把昨晚那五道题a了,(但我明明一直在学啊,为啥这么慢,难道是我太笨了?) 闲话少叙,先说做法 问题 A: C Looooops 题目描述 对于C的for(i=A ; i!=B ;i ...

  6. bzoj 1977 洛谷P4180 严格次小生成树

    Description: 给定一张N个节点M条边的无向图,求该图的严格次小生成树.设最小生成树边权之和为sum,那么严格次小生成树就是边权之和大于sum的最小的一个 Input: 第一行包含两个整数N ...

  7. HDU 2639 01背包求第k大

    Bone Collector II Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  8. [hdu 3949]线性基+高斯消元

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3949 一开始给做出来的线性基wa了很久,最后加了一步高斯消元就过了. 之所以可以这样做,证明如下. 首 ...

  9. Bash 实例,第一部分

    您可能要问:为什么要学习 Bash 编程?好,以下是几条令人信服的理由: 已经在运行它 如果查看一下,可能会发现:您现在正在运行 bash.因为 bash 是标准 Linux shell,并用于各种目 ...

  10. php函数-shuffle

    Shuffle()函数说明: -随机乱序现有数组并不保留键值: -shuffle()函数把数组中的元素按随机顺序重新排列,该函数为数组中的元素分配新的键名,已有键名将被删除. 语法说明: shuffl ...