对象(一)--对象的继承

聊一聊JavaScript中的对象的继承

吐槽

时间过得是真的快,感觉才更新博客怎么就快一个礼拜了...这两天看了点pythonflask框架了解了下,最后还是打算去系统地学习下node,又看了MongoDB,之后觉得Linux挺有意思的又去找资料学习(选发行版本,装虚拟机...), 感觉把时间都折腾在这儿了,有点不务正业(想了想还是日后抽时间学习吧,现在还是把前端知识巩固好,毕竟目前是个连实习都找不到的渣渣...

不过node还是要学的,计划之后手撸一个个人博客练练手...

前言

在前一篇对象的创建中,文中提到过原型模式,其实呢在JavaScript中对象的继承就是以原型模式作为基础的。

每个构造函数都有一个prototype属性指向的它的原型对象,而构造函数创建的实例对象可以顺势访问到原型对象中的属性

原型继承

原型对象,它也可以是另外一个构造函数的实例对象,因此它就可以访问更高层次的原型对象的属性,这样原型对象与原型对象以某种形式相连就构成了一条原型链, 而继承其实就是依赖于原型链。

打个比方PersonStudent两个类,根据常识Student肯定是Person的一个子集,即Person具有的属性和方法Student按道理都应该有,那么我们在已经有Person的基础上不应该再在Student中编写重复的代码,而是用Student去继承Person来实现代码的复用

function Person(name="name", age="20") {
  this.name = name
  this.age = age
  this.hobbies = [‘readBooks‘, ‘swimming‘]
}
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`)
}

function Student(homework) {
  this.homework = homework
}
<!-- 将Student的原型对象去引用Person的实例化对象 -->
Student.prototype = new Person()

Student.prototype.doHomework = function() {
  console.log(`i am doing ${this.homework} homework`)
}

let stu1 = new Student(‘math‘)
console.log(!!(stu1.name && stu1.age && stu1.sayHello)) // true

我们可以发现是在Student的实例化对象可以顺着原型链(绿色)找到这几个属性,nameage分布在Student.prototype中, sayHello分布在Person.prototype中,参照如下图

另外,构造函数的原型对象都继承自Object.prototype, 这就是为什么所有对象都具有toString 等方法

原型继承通过将原型对象赋值为一个父类的实例对象来实现继承,实质就是原型链的搭建

需要注意的地方是在将原型对象重新赋值后,它的constructor 需要手动指正

另外大家有没有注意到在上面这个例子中name, age 等属性都包含在同一个对象中,也就是说所有的子类对象在访问这些属性的时候实际上都是同一个值,如若它们是引用类型,任何实例对象对其的修改都会在其他对象访问时得到反映

let stu1 = new Student()
console.log(stu1.hobbies) // [‘readBooks‘, ‘swimming‘]
stu1.hobbies.push(‘running‘)
let stu2 = new Student()
console.log(stu2.hobbies) // [‘readBooks‘, ‘swimming‘, ‘runnning‘]

并且在创建子类型的实例时不能对父类型的构造函数传参,这也是一个问题

借用构造函数组合继承

为了解决以上两个问题:

  1. 所有子类型实例访问父类型属性时其实都是在访问原型属性
  2. 在创建子类型的实例时不能对父类型的构造函数进行传参

    借用构造函数闪亮登场了

function Student(name, age, homework) {
  // 使用call()(or apply())方法绑定this
  Person.call(this, name, age)
  this.homework = homework
}

// 相当于下面
function Student(name, age, homework) {
  this.name = name
  this.age = age
  this.homework = homework
}

<!-- 将Student的原型对象去引用Person的实例化对象 -->
Student.prototype = new Person()

这样将属性重新定义在子类的实例上,方法还是放置在原型链上,很好地解决了以上两个问题, 自身的nameage遮蔽了原型链上的nameage

还要一个问题就是子类型已经可以创建父类属性了,原型对象还是会再次创建这些属性,在这里Student.prototype对象中nameage依旧会被创建,因为调用了一个Person的构造函数, 但是nameage在原型对象中已经是多余的了

直接继承prototype

之前将父级构造函数的实例赋值给子构造函数的原型对象Student.prototype = new Person(), 会造成属性在原型对象中的冗余

这次我们选择直接继承父构造函数的原型对象

Student.prototype = Person.prototype

避免了不必要的属性定义在原型对象中,并且少了一次构造函数的调用节省了内存

但是又有一个问题暴露出来了,就是此时子构造函数的原型对象和父构造函数的原型对象同时指向一个对象, 其中任何一个添加修改属性都会影响对方

比如上面,当我们想要指正Student.prototypeconstructor时,Person.prototypeconstructor也发生了改变

Student.prototype.constructor = Student
console.log(Perosn.prototype.constructor) // Student

寄生组合式继承

利用空对象作为中介

function object(o) {
  function F() {}
  F.prototype = o
  return new F()
}
function Student(name, age, homework) {
  Person.call(this, name, age)
  this.homework = homework
}

Student.prototype = object(Person.prototype)

既避免的父级构造函数的多余调用,又将父子构造函数的原型对象区分开来,且空对象的占有的内存很少,因此寄生组合式继承棒棒的!

小结

JavaScript主要通过原型链实现继承,原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现。这样,子类型就能够访问父类型的所有属性和方法。原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用超类型的构造函数。这样就可以做到每个实例都有自己的属性。使用最多的继承模式是组合继承,即使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性

但是,寄生组合式继承是实现基于类型继承的最有效方式

原文地址:https://www.cnblogs.com/guanine/p/9311037.html

时间: 2024-10-13 13:40:38

对象(一)--对象的继承的相关文章

MDN——javascript——入门——第三章对象——对象.构造函数.原型链.继承——知识点总结

对象Object 由属性property(变量).方法method(函数)组成 var objectName = { member1Name : member1Value, member2Name : member2Value, member3Name : member3Value } member(成员)的值是任意的, 一个如上所示的对象被称之为对象的字面量(literal)——手动的写出对象的内容来创建一个对象.不同于从类实例化一个对象,我们会在后面学习这种方式. 访问对象成员 1.点表示法

Unit03 - 对象内存管理 、 继承的意义(上)

Unit03 - 对象内存管理 . 继承的意义(上) 1.内存管理:由JVM来管理的  1)堆:    1.1)存储所有new出来的对象(包含成员变量)    1.2)没有任何引用所指向的对象就是垃圾,        垃圾回收器(GC)不定时清理垃圾,        回收过程是透明的(看不到的),        调用System.gc()建议GC尽快来回收    1.3)成员变量的生命周期:          创建对象时存在堆中,对象被回收时一并被回收    1.4)内存泄露:不再使用的对象没有

类和对象、封装、继承、多态

? 类和对象 ? 类和对象的概念 类:客观存在的,抽象的,概念的东西.一个类可以被定义为描述行为的模板: 对象:对象是具体的,实际的,代表一个事物.对象具有状态和行为. 类是对象的模板,对象是类的一个具体实体. 定义Java中的类 一个类可以包含以下任意变量类型. a)局部变量: 方法里面,构造函数或块中定义的变量称为局部变量.该变量将被声明和初始化的方法中,当该方法完成该变量将被销毁. b)实例变量: 实例变量都在一个类,但任何方法之外的变量.这些变量在类被加载的实例化.实例变量可以从内部的任

【JavaScript】【学习】对象的创建和继承

下午刚刚看完了<JavaScript高级程序设计>中的第六章,面向对象的程序设计,因为自己以前没有面向对象程序设计的基础,所以理解得有些困难,但是通过自己的努力研读+上网查资料+反复实践,总算是答题上理解了,对我的编程思维算是一个很大的提高吧,这里把学习笔记和心得发一下,方便以后自己查阅. 一.理解对象 在JavaScript中,一切皆是对象,前面学习引用类型,基本都是JavaScript中的内置对象,而基本类型,则都是这些内置对象的实例,BOM,DOM也是对象,全局变量可以视为window的

Javascript高级程序设计笔记(很重要尤其是对象的设计模式与继承)

var obj = {'a':'a'}; var fun = function (){} console.log(typeof obj);//object console.log(typeof fun);//function var a; console.log(a === undefined);//true console.log(typeof b);//true 未声明的变量使用会报错,但是他的typeof=undefined var c=null; console.log(typeof c

javascript对象创建及原型继承的研究

今天总结了下javascript关于原型继承和对象创建方面的东西,因为javascript的原型继承在使用传统面向对象语言开发的同学看来比较怪异,原型继承确实比传统OOP语言的继承理解和运用起来困难一些,当然个人觉得传统OOP的继承相对比较简单,因为中规中矩. 下面逐个的用示例说明javascript中对象创建方式,专业一点叫什么模式,主要有直接单个创建:工厂模式:提出方法类函数公用方式:构造函数模式:构造函数+原型方式:使用原型本质的方式构建(这种受过李站的<悟透javascript>一书的

【JavaScript】类继承(对象冒充)和原型继承__深入理解原型和原型链

JavaScript里的继承方式在很多书上分了很多类型和实现方式,大体上就是两种:类继承(对象冒充)和原型继承. 类继承(对象冒充):在函数内部定义自身的属性的方法,子类继承时,用call或apply实现对象冒充,把类型定义的东西都复制过来,这样的继承子类与父类并没有多少关联,不互相影响,有利于保护自身的一些私有属性. 原型继承:每个函数都有自己的原型(prototype)属性,这个属性是在生成实例对象时自动创建的.它本身又是一个对象,拥有能够在实例间共享的属性和方法.而实例本身的属性和方法,则

在es6的语法下 对象的构造和继承实例

es6新增了class 类 可以更方便的创建和继承对象的属性和方法 但是这个class 只是一个语法糖 让创建对象的形式更加简便和直观下面是对该方法的简单实践 //es6 实例化对象和继承 class 新语法 class person{ constructor(name,age){ this.name=name this.age=age } } const person1=new person('bob',25) console.log(person1.age)//25 //extends 继承

39.C#--面对对象构造函数及构造函数继承使用

//一.新建Person类namespace _39.面对对象构造函数及构造函数继承使用{public class Person{//字段.属性.方法.构造函数//字段:存储数据//属性:保护字段,对字段的取值和设值进行限定//方法:描述对象的行为//构造函数:初始化对象(给对象的每个属性依次的赋值)//类中的成员,如果不加访问修饰符,默认都是privateprivate string _name; //字段public string Name //属性{get { return _name;