JS对象继承篇

ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的

  1. 原型链

其基本思路是利用原型让一个引用类型继承另一个引用类型的属性和方法

function Person(){
this.name = "Person";
}
Person.prototype.getName = function(){
return this.name;
};
function SuperPerson(name,sex){
this.name = name;
this.sex = sex;
}
SuperPerson.prototype = new Person();//重写原型 //在添加新方法或重写原型中的方法时,一定要在重写原型的语句后面,而且不能使用字面量添加新方法,不然又会再一次重写原型,原型链被切断
SuperPerson.prototype.getSex = function(){
return this.sex;
};
var Tom = new SuperPerson("Tom","man");
console.log(Tom.getName());//Tom
console.log(Tom.getSex());//man

在上述代码中,先定义了一个Person类型,其中有一个属性和定义在其原型对象上的一个方法,接着定义了一个SuperPerson类型,其中有两个属性,然后重写了它的原型对象,将Person的实例赋值给SuperPerson的原型。然后又在新的原型对象上添加了一个方法。

当然别忘记了默认的原型,因为所有引用类型默认都继承了Object

console.log(Tom instanceof Object);//true
console.log(Tom instanceof Person);//true
console.log(Tom instanceof SuperPerson);//true

原型链存在的问题:1.包含引用类型的原型属性被所有的实例所共享,我们在继承的时候,原先的实例属性就顺理成章的变成了现在的原型属性,那么后来的其中一个实例中修改值,其他实例都会反映出来 2.在创建子类型的实例时,不能向其父类型的构造函数传递参数。所以综上问题,实践中很少单独使用原型链

  1. 借用构造函数

其基本思路是在子类型构造函数的内部调用父类型的构造函数

function Person(name){
this.name = name;
this.friends = ["Jack","John","Kim"];
}
function SuperPerson(name,sex){
//继承Person
Person.call(this,name);//call()将Person的运行作用域绑定到了SuperPerson上
//如果要屏蔽父类型中的属性,要在继承语句之后添加
//实例属性
this.sex = sex;
}
var Tom = new SuperPerson("Tom","man");
Tom.friends.push("Amy");
console.log(Tom.friends);// ["Jack", "John", "Kim", "Amy"]
var David = new SuperPerson("David","man");
console.log(David.friends);//["Jack", "John", "Kim"]
console.log(David.name);//David

通过调用call()方法(apply()方法也可以),实际上是在将来要新创建SuperPerson的实例上调用了Person构造函数,这样就会在新SuperPerson对象上执行Person构造函数中定义的所有对象初始化代码,结果SuperPerson的每个实例都会具有自己的friends属性的副本
借用构造函数存在的问题:方法函数无法复用,实践上也很少单独使用

  1. 组合继承

其基本思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承

function Person(name){
this.name = name;
this.friends = ["Jack","John","Kim"];
}
Person.prototype.getName = function(){
console.log(this.name);
}
function SuperPerson(name,sex){
//继承Person
Person.call(this,name);/*第二次调用父类型的构造函数,在实例上又创建了一次属性name和frieds,屏蔽第一次调用时创建的*/
//实例属性
this.sex = sex;
}
SuperPerson.prototype = new Person();//重写原型,第一次调用父类型的构造函数
SuperPerson.prototype.constructor = SuperPerson;
//添加新方法
SuperPerson.prototype.getSex = function(){
console.log(this.sex);
};
var Tom = new SuperPerson("Tom","man");
Tom.friends.push("Amy");
console.log(Tom.friends);// ["Jack", "John", "Kim", "Amy"]
Tom.getName();// Tom
Tom.getSex();// man var David = new SuperPerson("David","man");
console.log(David.friends);// ["Jack", "John", "Kim"]
David.getName();// David
David.getSex();// man

这样,所有的实例都能拥有自己的属性,并且可以使用相同的方法
组合继承避免了原型链和借用构造函数的缺陷,结合了两个的优点,是最常用的继承方式,但是无论什么情况下都会调用两次父类型的构造函数

  1. 原型式继承

其基本思路是借助原型可以基于已有的对象创建新的对象

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

object()对传入的对象执行了一次浅复制

var person = {
name: "Tom",
friends: ["Jack", "John", "Kim"]
};
var David = object(person);
David.name = "David";
David.friends.push("Amy");
console.log(David.friends);// ["Jack", "John", "Kim", "Amy"]
var Rob = object(person);
Rob.name = "Rob";
console.log(Rob.friends);// ["Jack", "John", "Kim", "Amy"]


这意味着person.friends会被其他对象共享
ECMAScript5新增的Object.create()规范化了原型式继承,这个方法接受两个参数:一个用于新对象原型的对象和(可选)一个为新对象定义额外属性的对象,在传入一个参数时跟object()方法行为相同

var person = {
name: "Tom",
friends: ["Jack", "John", "Kim"]
};
var David = Object.create(person);
/*
使用两个参数:
var David = Object.create(person,{
name: {
value:"David"
});
*/
David.name = "David";
David.friends.push("Amy");
console.log(David.friends);//["Jack", "John", "Kim", "Amy"]
var Rob = Object.create(person);
Rob.name = "Rob";
console.log(Rob.friends);// ["Jack", "John", "Kim", "Amy"]

存在和原型模式一样的问题,包含引用类型属性都会被共享

  1. 寄生式继承

其基本思路是类似创建对象时的工厂模式,将继承过程封装在一个函数里,然后返回一个对象

function createObject(o){
var clone = Object.create(o);
clone.sayHi = function(){
console.log("Hi~");
};
return clone;
}
var person = {
name: "Tom",
friends: ["Jack", "John", "Kim"]
};
var David = createObject(person);
David.sayHi();//Hi~

寄生式继承存在的问题:方法函数无法复用.适用于不考虑自定义类型和构造函数的情况下

  1. 寄生组合式继承

其基本思路是通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,就是为了不必为了子类型的原型去调用父类型的构造函数

function inheritPrototype(superPerson,person)
{
var prototype=Object.create(person.prototype);
prototype.construtor = superPerson;
superPerson.prototype = prototype;
}
function Person(name)
{
this.name = name;
this.frieds = ["Jack", "John", "Kim"];
}
Person.prototype.getName = function(){
console.log(this.name);
};
function SuperPerson(name,sex){
Person.call(this,name);
this.sex = sex;
}
inheritPrototype(SuperPerson,Person);
SuperPerson.prototype.getSex = function(){
console.log(this.sex);
};
var Tom=new SuperPerson("Tom","man");
Tom.getName();//Tom

这个的高效率体现在它值调用了一次Person构造函数,避免了创建不必要的属性,开发人员普遍认为寄生组合继承是引用类型最理想的继承范式

参考书籍《Javascript高级程序设计》

JS对象继承篇的更多相关文章

  1. JS对象继承与原型链

    1.以复制方式实现的继承 1.1浅拷贝 基本类型的复制 var parent = { lanage: "chinese" } var child = { name: "x ...

  2. Js对象继承小结

    1.继承 对象的定义好用一些的一般是把实例对象的属性定义在类里面,通过this指针指向具体实例属性.定义对象的public方法时将其绑定到prototype中.子类在继承父类时可以通过对象冒充来继承父 ...

  3. js对象继承的问题

    js继承方法有多种: 1.利用call()和apply()的方法. call()里面接受多个参数:apply()只能接受两个参数. call()第一个参数是本身,后面都是他的属性和方法. apply( ...

  4. js对象继承

    方法: 1.原型链继承 2.使用对象冒充继承

  5. JS中对象继承方式

    JS对象继承方式 摘自<JavaScript的对象继承方式,有几种写法>,作者:peakedness 链接:https://my.oschina.net/u/3970421/blog/28 ...

  6. Javascript高级编程学习笔记(22)—— 对象继承

    继承是所有面向对象的语言最让人津津乐道的概念 许多面向对象的语言都支持两种实现继承的方式: 1.接口继承 2.实现继承 由于ECMAScript中没有函数签名,所以自然也是不支持接口继承 所以JS中能 ...

  7. js的继承实现方式

    1. 使用call或者apply来实现js对象继承 function Animal(age){ this.age = age; this.say = function(){ console.log(' ...

  8. js 对象深复制,创建对象和继承

    js 对象深复制,创建对象和继承.主要参考高级编程第三版,总结网上部分资料和自己的代码测试心得.每走一小步,就做一个小结. 1.对象/数组深复制 一般的=号传递的都是对象/数组的引用,如在控制台输入 ...

  9. js对象的几种创建方式和js实现继承的方式[转]

    一.js对象的创建方式 1. 使用Object构造函数来创建一个对象,下面代码创建了一个person对象,并用两种方式打印出了Name的属性值. var person = new Object(); ...

随机推荐

  1. opencv中Mat与IplImage,CVMat类型之间转换

    opencv中对图像的处理是最基本的操作,一般的图像类型为IplImage类型,但是当我们对图像进行处理的时候,多数都是对像素矩阵进行处理,所以这三个类型之间的转换会对我们的工作带来便利. Mat类型 ...

  2. ABP教程-打造一个《电话簿项目》-目录-MPA版本-基于ABP1.13版本

    此系列文章会进行不定期的更新,应该会有6章左右. 感兴趣的朋友可以跟着看看,本教程适合已经看过ABP的文档但是又无从下手的小伙伴们. 初衷: 发布系列教程的原因是发现ABP在园子火了很久,但是发现还是 ...

  3. Java数据库连接技术——JDBC

    大家好,今天我们学习了Java如何连接数据库.之前学过.net语言的数据库操作,感觉就是一通百通,大同小异. JDBC是Java数据库连接技术的简称,提供连接各种常用数据库的能力. JDBC API ...

  4. 逆天Kali带你游遍大江南北~安全之前人铺路!

    0.Linux基础学习(基本指令) http://www.cnblogs.com/dunitian/p/4822807.html 1.Kali安装到移动硬盘或者U盘中~Linux系列通用方法(包括An ...

  5. ZKWeb网页框架1.1正式发布

    发行日志 https://github.com/zkweb-framework/ZKWeb/blob/master/ReleaseNotes/ReleaseNote.1.1.md 主要改动 添加EFC ...

  6. 免费高效实用的.NET操作Excel组件NPOI(.NET组件介绍之六)

    很多的软件项目几乎都包含着对文档的操作,前面已经介绍过两款操作文档的组件,现在介绍一款文档操作的组件NPOI. NPOI可以生成没有安装在您的服务器上的Microsoft Office套件的Excel ...

  7. Spring中Bean的作用域、生命周期

                                   Bean的作用域.生命周期 Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).protot ...

  8. 漫谈C#编程语言在游戏领域的应用

    0x00 前言 随着微软越来越开放,C#也变得越来越吸引人们的眼球.而在游戏行业中,C#也开始慢慢地获得了关注.这不, 网易绝代双娇手游团队已经全面使用.Net Core支持前后端统一C#开发,跨平台 ...

  9. MongoDB系列(二):C#应用

    前言 上一篇文章<MongoDB系列(一):简介及安装>已经介绍了MongoDB以及其在window环境下的安装,这篇文章主要讲讲如何用C#来与MongoDB进行通讯.再次强调一下,我使用 ...

  10. C++常见笔试面试要点以及常见问题

    1. C++常见笔试面试要点: C++语言相关: (1) 虚函数(多态)的内部实现 (2) 智能指针用过哪些?shared_ptr和unique_ptr用的时候需要注意什么?shared_ptr的实现 ...