首页 > 其他分享 >JS实现继承的方法

JS实现继承的方法

时间:2022-10-13 17:02:12浏览次数:37  
标签:console log Parent 继承 JS Child prototype 方法 name


方法一:借助call

function Parent (sex) {
this.name = 'fx'
this.sex = sex
}
Parent.prototype.test = function () {
console.log('我是函数')
}
Parent.prototype.why = 'not'
function Child (sex) {
Parent.call(this, sex)
this.age = 18
}
console.log(new Parent())
console.log(new Child())
console.log(new Child('女'))
console.log(new Child().name) // fx
console.log(new Child().why) // undefined
console.log(new Child().test()) // test is not a function

这样写的时候子类虽然能够拿到父类的属性值,但是问题是父类原型的属性无法继承

方法二: 借助原型链

function Parent (sex) {
this.name = 'fx'
this.sex = sex
}
Parent.prototype.test = function () {
console.log('我是函数')
}
Parent.prototype.why = 'not'
function Child (sex) {
this.age = 18
}
Child.prototype = new Parent()
console.log(new Parent())
console.log(new Child())
console.log(new Child('女'))
console.log(new Child().name) // fx
console.log(new Child().why) // not
console.log(new Child().test()) // 我是函数

存在问题:

① 引用类型的属性被所有实例共享。
② 在创建 Child 的实例时,不能向Parent传参

var c1 = new Child()
var c2 = new Child()
c2.character.push(999)
console.log(c1.character) // [1, 5, 9, 2, 999]
console.log(c2.character) // [1, 5, 9, 2, 999]

方法三:将前两种组合

function Parent () {
this.name = 'fx'
this.character = [1, 5, 9, 2]
}
Parent.prototype.test = function () {
console.log('我是函数')
}
Parent.prototype.why = 'not'
function Child () {
Parent.call(this) // 注意这里的坑,在这里,我们又会调用了一次 Parent 构造函数。
this.age = 18
}
Child.prototype = new Parent() // 父类的构造函数是被执行了两次的,第一次:Child.prototype = new Parent();第二次:实例化的时候会被执行;
var c1 = new Child() // 第二次调用父构造函数
var c2 = new Child()
c2.character.push(999)
console.log(c1.character) // [1, 5, 9, 2]
console.log(c2.character) // [1, 5, 9, 2, 999]

存在问题:会调用两次父构造函数。一次是设置子类型实例的原型的时候;一次在创建子类型实例的时候。 

方法四:组合继承优化

function Parent () {
this.name = 'fx'
this.character = [1, 5, 9, 2]
}
Parent.prototype.test = function () {
console.log('我是函数')
}
Parent.prototype.why = 'not'
function Child () {
Parent.call(this)
this.age = 18
}
Child.prototype = Parent.prototype
var c1 = new Child()
var c2 = new Child()
console.log(c1)
console.log(c2)

这里让将父类原型对象直接给到子类,父类构造函数只执行一次,而且父类属性和方法均能访问,但是子类实例的构造函数是Parent,而不是Child,这也是不对的。

方法五(推荐使用): 组合继承的优化1(寄生组合式继承)

这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Child.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。

function Parent () {
this.name = 'fx'
this.character = [1, 5, 9, 2]
}
Parent.prototype.test = function () {
console.log('我是函数')
}
Parent.prototype.why = 'not'
function Child () {
Parent.call(this)
this.age = 18
}
Child.prototype = Object.create(Parent.prototype);
/*等价于
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Object.create的底层实现
Object.create = function (o) {
var F = function () {}
F.prototype = o
return new F()
}
*/
Child.prototype.constructor = Child;
var c1 = new Child()
console.log(c1)
console.log(c1 instanceof Child) // true;
console.log(c1 instanceof Parent) // true;
console.log(Child.prototype.isPrototypeOf(c1)); // true
console.log(Parent.prototype.isPrototypeOf(c1)); // true
console.log(c1.constructor.name); // Child,注意不加Child.prototype.constructor = Child;结果是Parent

方法六:ES6-Class继承

三个关键字:① class关键字。②extends关键字。③super关键字

class FxFn {
// 类的构造方法
constructor (name, age) {
this.name = name
this.age = age
}
showName () {
console.log('调用父类的构造方法')
console.log(this.name)
}
}
class FxSubFn extends FxFn {
constructor (name, age, salary) {
super(name, age); // 调用父类的构造方法
this.salary = salary
}
// 父类的方法重写
showName () {
console.log('调用子类的构造方法')
console.log(this.name, this.salary)
}
}
let fx1 = new FxSubFn('fx', 18, 88888888)
console.log(fx1) // FxSubFn {name: "fx", age: 18, salary: 88888888}
fx1.showName() // fx

1、子类构造函数中必须调用super方法,否则在新建对象时报错。

constructor(name, age,job) {
// 报错
}

2、子类构造函数中必须在使用this前调用super,否则报错。

constructor (name, age, salary) {
this.salary = salary
super(name, age); // 报错
}

 super调用属性:(有坑)

class FxFn {
// 类的构造方法
constructor (name, age) {
this.name = name
FxFn.prototype.age = age
}
showName () {
console.log('调用父类的构造方法')
console.log(this.name)
}
}
class FxSubFn extends FxFn {
constructor (name, age, salary) {
super(name, age); // 调用父类的构造方法
this.salary = salary
}
// 父类的方法重写
showName () {
console.log(super.name) // undefined
// super.name报了undefined,表示没有定义。
// super是指向父类的prototype对象,即Person.prototype,
// 父类的方法是定义在父类的原型中,而属性是定义在父类对象上的,所以需要把属性定义在原型上。
console.log(super.age) // 18
console.log(this.name, this.salary) // fx 88888888
}
}
let fx1 = new FxSubFn('fx', 18, 88888888)
console.log(fx1) // FxSubFn {name: "fx", age: 18, salary: 88888888}
fx1.showName()

this指向问题

class FxFn {
// 类的构造方法
constructor (name, age, sex) {
this.name = name
FxFn.prototype.age = age
this.sex = '女'
}
showSex () {
console.log(this.sex)
}
}
class FxSubFn extends FxFn {
constructor (name, age, sex) {
super(name, age); // 调用父类的构造方法
this.sex = sex
}
// 父类的方法重写
showSex () {
super.showSex()
}
}
let fx1 = new FxSubFn('fx', 18, '男')
fx1.showSex()
// 子类在调用父类构造函数时,父类的原型this值已经指向了子类,
// 即FxFn.prototype.call(this),故输出的子类的值。

ES6的extends被编译后的JavaScript代码

ES6的代码最后都是要在浏览器上能够跑起来的,这中间就利用了babel这个编译工具,将ES6的代码编译成ES5让一些不支持新语法的浏览器也能运行。

核心是_inherits函数,可以看到它采用的依然也是第五种方式————寄生组合继承方式,同时证明了这种方式的成功。不过这里加了一个Object.setPrototypeOf(subClass, superClass),是用来继承父类的静态方法。这也是原来的继承方式疏忽掉的地方。

function _inherits (subClass, superClass) { 
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

继承的最大问题在于:无法决定继承哪些属性,所有属性都得继承。

 

标签:console,log,Parent,继承,JS,Child,prototype,方法,name
From: https://blog.51cto.com/u_13028258/5754022

相关文章

  • arguments详解,类数组转数组方法
    为什么需要arguments对象由于​​JavaScript​​​允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是​​arguments​​对象的由来。通......
  • js判断手机系统是android还是ios?
    varu=navigator.userAgent;//识别各种浏览器varisAndroid=u.indexOf('Android')>-1||u.indexOf('Adr')>-1;//android终端varisiOS=!!u.match(/\(i[^;]......
  • js倒计时
    倒计时//倒计时输入时间-现在时间varinputTime=prompt('请输入当前时间,格式为YYYY-MM-DD或YYYY/MM/DD');//prompt返回值是字符串要进行数据类型转换......
  • 普通用户服务使用perf命令解决小方法
    perf是Linux下的一款性能分析工具,能够进行函数级与指令级的热点查找,其使用方法很多,最常用的有以下四种:报错如图所示:根据提示需要在内核添加相关配置,配置如下:perf很多用法需......
  • js逆向案例
    js逆向案例目录零、概述一、请求参数|Cookie|Referer校验(⭐)1、案例1_有道翻译2、案例2_百度翻译二、参数响应如何获取AES、DES、RSA(⭐)1、案例3_建筑市场_AES2、案例4_毛毛租......
  • Golang复杂json结构体解析
    1、示例一{"id":"8667597b-bcd9-51de31b655cd","name":"ali-redis-analyse","category":"db","category_display":"数据库","type":"redis","type_display":......
  • JS 中的require 和 import 区别
    这两个都是为了JS模块化编程使用.遵循规范require 是AMD规范引入方式import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法调用时间require是运......
  • 【Nodejs】240-有助于理解前端工具的 node 知识
    缘起平时写惯了业务代码之后,如果想要了解下webpack或者vue-cli,好像是件很难上手的事情......
  • 【JS】167-JavaScript设计模式——装饰者模式
    四、装饰者模式(DecoratorPattern)1.概念介绍装饰者模式(DecoratorPattern):在不改变原类和继承情况下,动态添加功能到对象中,通过包装一个对象实现一个新的具有原对象相同接口......
  • 【JS】169-JavaScript设计模式——外观模式
    六、外观模式(FacadePattern)1.概念介绍外观模式(FacadePattern) 是一种简单又常见的模式,它为一些复杂的子系统接口提供一个更高级的统一接口,方便对这些子系统的接口访问......