IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 原型链与继承 -> 正文阅读

[JavaScript知识库]原型链与继承

目录

原型链

继承

Javascript为什么没有方法签名?

原型链继承

构造函数继承

组合继承

原型式继承

寄生继承

寄生组合继承


原型链

原型链是一种原型对象和实例对象的关系,通过属性__proto__进行联系。

继承

继承是一种允许我们在已有的类的基础上创建新类的机制,它可以使用父类的所有功能,而且可以在这些功能的基础上进行扩展。继承的方式有两种,一种是接口继承,继承方法签名(方法名称和参数类型组成),另一种是实现继承,继承实际方法,javascript没有方法签名,只能支持实现继承。

Javascript为什么没有方法签名?

首先javascript是一种松散类型或动态语言,不必提前声明变量的类型,处理程序时,自动确定类型,而方法签名又是由方法名称和参数类型组成的,原生javascript没有类型检查,传递多少个参数,参数是什么类型,解析器都不会去验证,javascript的参数是由0或者多个值的数组表示,不像其他语言,要事先创建函数签名,后面调用函数也要跟函数签名设置的一样,所以javascript没有方法签名。

function Target(param) {
  console.log(param)  //输出 hi ,定义函数的参数数量与调用函数的传入的参数数量可以不一样
}

Target("hi",'world')

我们继续讲回继承。

继承又分为:原型链继承、构造函数继承、组合继承、寄生组合继承

原型链继承

function Target() {
    this.params = ["a","b","c"]
}

function Follow() {}

Follow.prototype = new Target()

let follow_first = new Follow()
follow_first.params.push("f")

let follow_second = new Follow()
follow_second.params.push("g")

console.log(follow_first.params)   // 输出 ["a", "b", "c", "f", "g"]
console.log(follow_second.params)  // 输出 ["a", "b", "c", "f", "g"]

我们使用构造函数Follow的属性prototype指向构造函数Target创建出来的实例对象,然后再用构造函数Follow创建出两个实例对象follow_first、follow_second,由于原型对象是可以被构造函数创建出来实例对象所共享,所以实例对象follow_first、follow_second向自身属性params数组添加元素,其实是在向原型对象的属性params数组添加元素,所以最后它们的属性params数组输出的结果是一样的。

再然后就是关于我在网上看到的:

在javascript的原型链继承方式中,子类在调用父类的构造函数时不能传参数。

但是我自己写代码去尝试:

function Target(name) {
    this.params = ["a","b","c"]
    this.name = name
}

function Follow() {}

Follow.prototype = new Target("Ben")

console.log(Follow.prototype)

输出结果:

表明参数是可以传进去构造函数的,于是我就在网上寻找答案,找到一个比较合理的答案:

并不是语法不能实现对构造函数的参数传递,而是这样做不符合面向对象编程的规则,对象才是属性的拥有者,如果在调用构造函数Target提前将name这个属性赋了值,那么后面构造函数Follow创建出来的实例对象都会拥有这个name属性的值,但是这个name属性的值可能并不是实例对象想要的,而是被迫拥有这个属性的值。

原型链继承的优点:

子类可以通过查找原型链,找到父类的属性。

原型链继承的缺点:

构造函数创建出来的实例共用原型指向的对象,操作原型指向的对象的属性可能会引发问题。

在实例对象定义方法,防止跟父类定义的方法重名。

子类在调用父类的构造函数时不能传参数。

构造函数继承

function Target(name) {
    this.params = ["a","b","c"]
    this.name = name
    this.fun = function () {
        return this.params
    }
}

Target.prototype.fun_extra = function () {
    console.log("go")
}

function Follow(name) {
    Target.call(this,name)
}

let follow_first = new Follow("Ben")
follow_first.params.push("g")
console.log(follow_first.params)     // 输出 ["a", "b", "c", "g"]
console.log(follow_first.name)       // 输出 Ben
console.log(follow_first.fun)        // 输出 ? () {
                                               return this.params
                                             }
console.log(follow_first.fun())      // 输出 ["a", "b", "c", "g"]
console.log(follow_first.fun_extra)  // 输出 undefined
console.log(follow_first.fun_extra()) //输出 Uncaught TypeError: follow_first.fun_extra 
                                      //    is not a function

let follow_second = new Follow("Ken")
follow_second.params.push("f")
console.log(follow_second.params)    // 输出 ["a", "b", "c", "f"]
console.log(follow_second.name)      // 输出 Ken
console.log(follow_second.fun)       // 输出 ? () {
                                               return this.params
                                             }
console.log(follow_second.fun())     // 输出 ["a", "b", "c", "f"]
console.log(follow_second.fun_extra)  // 输出  undefined
console.log(follow_second.fun_extra())  // 输出 prototype1.js:33 Uncaught TypeError: 
                                        // follow_second.fun_extra is not a function

构造函数继承解决了子类在调用父类的构造函数时不能传参数与多个实例对象共同操作原型指向的对象的属性的问题,同时也带来父类的原型定义方法对于子类是不可用,子类的实例只能使用父类this绑定的属性的问题。

构造函数继承的优点:

解决了子类在调用父类的构造函数时不能传参数

避免了多个实例对象共同操作原型指向的对象的属性可能会引起问题

构造函数继承的缺点:

父类的原型定义方法对于子类是不可用,子类的实例只能使用父类this绑定的属性

在构造函数定义的方法不能对其进行覆盖

组合继承

function Target(name) {
    this.params = ["a","b","c"]
    this.name = name
}

Target.prototype.fun = function () {
    return this.params
}

function Follow(name) {
    Target.call(this,name)
}

Follow.prototype = new Target()
Follow.prototype.constructor = Follow

let follow_first = new Follow("Ben")
follow_first.params.push("g")
console.log(follow_first.params)     //输出 ["a", "b", "c", "g"]
console.log(follow_first.fun())      //输出 ["a", "b", "c", "g"]

let follow_second = new Follow("Ken")
follow_second.params.push("f")
console.log(follow_second.params)   //输出 ["a", "b", "c", "f"]
console.log(follow_second.fun())    //输出 ["a", "b", "c", "f"]

组合继承的优点:

解决了原型链继承的实例对象共享原型对象的属性,导致实例对象的属性隔离性差。

解决了构造函数继承父类的原型定义方法对于子类是不可用的问题。

组合继承的缺点:

父类的构造函数被调用了两次,一次是call函数调用,一次是new关键字调用,导致原型对象与实例对象拥有相同的属性。

原型式继承

function Target(object) {
    function Follow() {}
    Follow.prototype = object
    return new Follow()
}

原型式继承将原型链继承进行封装,还不需要自己去创建新的构造函数,减少代码量。但是还是会有原型链继承的缺点。

寄生继承

function Target(object) {
    function Follow() {}
    Follow.prototype = object
    return new Follow()
}

function createTarget(originObject) {
    let newObject = Target(originObject)
    newObject.method = function () {
        console.log("hello")
    }
    return newObject
}

寄生继承同样也是一个封装的过程,在原型继承的基础上,给新创建的实例对象增加属性或方法,不过这些属性都是私有属性与方法都是私有,并不能共享。

寄生组合继承

function create(object) {
    function A() {}
    A.prototype = object
    return new A()
}

function Target(name) {
    this.params = ["a","b"]
    this.name = name
}

Target.prototype.fun = function () {
    return this.params
}

function Follow(name) {
    Target.call(this,name)
}

function inheritPrototype(follow,target) {
    let prototype = create(target.prototype)
    prototype.constructor = follow
    follow.prototype = prototype
}
inheritPrototype(Follow,Target)

let follow_first = new Follow("Ben")
console.log(follow_first.name)   //输出 Ben

关系图:

寄生组合继承减少调用父类构造函数的次数,用空对象替换掉父类构造函数创建出来的实例对象,使得子类的属性prototype指向这个空对象,避免原型对象与实例对象拥有相同的属性,同时子类创建出来的实例对象可以对父类的方法进行覆盖。寄生组合继承拥有原型链继承和构造函数继承的优点。

?还有一个class继承,后面我再来进行介绍。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-02 11:15:56  更:2021-09-02 11:16:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年6日历 -2024/6/27 5:22:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码