博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JS对象继承篇
阅读量:5150 次
发布时间:2019-06-13

本文共 4634 字,大约阅读时间需要 15 分钟。

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());//Tomconsole.log(Tom.getSex());//man

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

enter description here

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

console.log(Tom instanceof Object);//trueconsole.log(Tom instanceof Person);//trueconsole.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();// manvar David = new SuperPerson("David","man");console.log(David.friends);// ["Jack", "John", "Kim"]David.getName();// DavidDavid.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"]

enter description here

这意味着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高级程序设计》

转载于:https://www.cnblogs.com/bluey/p/4943323.html

你可能感兴趣的文章
【翻译】7个ASP.NET MVC最佳实践
查看>>
EF Core 2.1变化
查看>>
转:C++ Applications
查看>>
win7 64位机ODBC的数据源DSN添加和移除问题
查看>>
一个不错的逻辑回归的实例
查看>>
Python第一天
查看>>
css position
查看>>
【bzoj2788】Festival
查看>>
执行gem install dryrun错误
查看>>
Java SE之正则表达式一:概述
查看>>
广义表
查看>>
HTML5简单入门系列(四)
查看>>
AndroidStudio快捷键
查看>>
实现字符串反转
查看>>
转载:《TypeScript 中文入门教程》 5、命名空间和模块
查看>>
苹果开发中常用英语单词
查看>>
[USACO 1.4.3]等差数列
查看>>
Shader Overview
查看>>
Reveal 配置与使用
查看>>
Java中反射的学习与理解(一)
查看>>