JavaScript原型链及常见的继承方法

目录
  • 原型链
    • 原型链的概念
    • 原型链的问题
  • 几种常见的继承方法
    • 盗用构造函数
    • 组合继承
    • 原型式继承
    • 寄生式继承
    • 寄生组合式继承

原型链

原型链的概念

在JavaScript中,每一个构造函数都有一个原型,这个原型中有一个属性constructor会再次指回这个构造函数,这个构造函数所创造的实例对象,会有一个指针(也就是我们说的隐式原型__proto__或者是浏览器中显示的[[Prototype]])指向这个构造函数的原型对象。如果说该构造函数的原型对象也是由另外一个构造函数所创造的实例,那么该构造函数的原型对象也会存在一个指针指向另外一个构造函数的原型对象,周而复始,就形成了一条原型链。 最特别的是所有的没有经过再继承函数都是由Function实例化来的,所有的除了函数外的对象都是由Object实例化来的,其中Object也是由Function实例化来的,但是Object.prototype.__proto__ === null 是成立的。

再强调一遍:原型链是沿着对象的隐式原型一层层的去寻找的,找到的是构造函数所创造的实例。例如下:

这个就是相当于由Studentnew 出来的实例s,查找自身的 name 属性,然后沿着原型链查找,找到Student中的prototype当中,然后找到了name这个属性。

而这个例子,由红框框起来的代码(寄生继承的关键代码),代替注释掉的部分,最终s是找不到name属性的,这是因为红框中的代码,仅仅是将Student的隐式原型指向了Person的显示原型对象,未能创建任何的实例,当然就不会存在属性这个说法。

原型链的问题

原型链的问题主要有两个方面,第一个问题是,当原型中出现包含引用值(比如数组)的时候,所有在这条原型链中的实例会共享这个属性,造成“一发而动全身”的问题。第二个问题就是子类在实例化时,不能够给父类型的构造函数传参,即

无法在不影响所有对象实例的情况下把参数传递进父类型的构造函数传参

几种常见的继承方法

盗用构造函数

function SuperType() {
    this.friends = ['张三','李四']
}

function SubType() {
    SuperType.call(this);
}

const p1 = new SubType();
p1.friends.push('王武');

const p2 = new SubType();
console.log(p2.friends); // ['张三','李四', '王武']

盗用构造函数实现继承在这个例子中有了充分的体现: 首先在子类的构造函数中调用父类的构造函数。因为毕竟函数就是特定上下文中执行代码的简单对象,所以可以使用call()方法以创建的对象为上下文执行的构造函数。

盗用构造函数的主要问题,也是创建对象的几种方式中构造函数模式自定义类型的问题:必须在构造函数中定义方法,造成内存浪费。另外,子类也不能访问父类原型上定义的方法,因此,盗用构造函数也不会单独使用。

组合继承

组合继承也称为伪经典继承,综合了原型链和构造函数,将两者的有点结合起来。基本的思路就是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实现实例的属性。这样就可以把方法定义在原型上实现复用,又可以让每个实例有自己的属性。

function SuperType(name) {
    this.name = name;
    this.friends = ['张三','李四'];
}
SuperType.prototype.sayName = function() {
    console.log(this.name)
}
// 继承方法
SubType.prototype = new SuperType();
function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}
const p1 = new SubType('赵六', 12);
const p2 = new SubType('赵六2', 22);
// 创建的 p1 和 p2 能够拥有自己的属性并且引用值属性也是独立的,此外,每一个实例能够公用父类的方法。

组合继承已经接近完美了,但是,我们发现,实现组合继承就要调用两次父类构造函数。在本质上,子类型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了,这就为减少一次调用父类构造函数提供了思路。

原型式继承

const person = {
    name: 'zs',
    friends: ['ls','ww']
}
// 创造出一个实例,这个实例的隐式原型指向 person
const anotherPerosn = Object.create(person);
anotherPerosn.name = 'xm'
anotherPerosn.friends.push('zl')

console.log(anotherPerosn.name) // xm
console.log(anotherPerosn.friends) // ['ls','ww', 'zl'];

const anotherPerosn2 = Object.create(person);
anotherPerosn.name = 'xh'
anotherPerosn.friends.push('dd')

console.log(anotherPerosn2.name) // xh
console.log(anotherPerosn2.friends) // ['ls','ww', 'zl', 'dd'];

对于原型链继承就不再过多的解释了。。。。

寄生式继承

寄生式继承与原型式继承比较相似,都会存在属性引用值共享的问题。

function createAnotherPerson(original) {
    const clone = Object.create(original); //通过调用函数创建一个新的对象
    clone.sayHi = function() { // 以某种方式增强这个对象
        console.log('Hi');
    }
    return clone;
}

寄生式继承,不仅存在着属性引用值共享的问题而且函数还不能进行复用。

寄生组合式继承

// 实现了寄生式组合继承的核心逻辑
function inheritPrototype(subFn, parentFn){
    subFn.prototype = Object.create(parentFn.prototype); // 创建赋值对象
    Object.defineProperty(subFn.prototype,'constructor', {   // 增强对象
        enumerable: false,
        writable: false,
        configurable: false,
        value: subFn,
    })
}
function Person(name, age, address) {
    this.name = name;
    this.age = age;
    this.address = address;
}
Person.prototype.eating = function() {
        console.log(this.name + "正在吃饭");
} // 共享方法

function Student(name, age, address, sno) {
    Person.call(this, name, age, address); // 绑定 this 确保创建出来的对象是相互独立的
    this.sno = sno;
    this.studing = function() {
        console.log(`${this.name}正在学习`)
    }
}

function Teacher(name, age, address, tno) {
    Person.call(this, name, age, address)
    this.tno = tno;
}

寄生+组合式(构造函数+原型链)完美的解决了其他继承出现的问题。

到此这篇关于JavaScript原型链及常见的继承方法的文章就介绍到这了,更多相关JS原型链内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript原型和原型链与构造函数和实例之间的关系详解

    目录 原型 原型链 原型 如图所示: 1.instanceof检测构造函数与实例的关系: function Person () {.........} person = new Person () res = person instanceof Person res  // true 2.实例继承原型上的定义的属性: function Person () {........} Person.prototype.type = 'object n' person = new Person () re

  • 一文搞懂JavaScript中原型与原型链

    目录 1.构造函数原型prototype 2.对象原型__proto__ 3.constructor构造函数 4.原型链 5.原型对象中的this指向 6.扩展内置对象(原型对象的应用) 在ES6之前,我们面向对象是通过构造函数实现的.我们把对象的公共属性和方法放在构造函数里 像这样: function student(uname,age) { this.uname = uname; this.age = age; this.school = function() { console.log('

  • Javascript 原型与原型链深入详解

    目录 前言 对象 原型 原型链 javascript中的类 new的实现 instanceof的实现 javascript的继承 总结 前言 在前端这块领域,原型与原型链是每一个前端 er 必须掌握的概念.多次在面试或者一些技术博客里面看见这个概念.由此可见,这个玩意对于前端来说有多重要.其实它本身理解起来不难,但是很多刚入行前端的同学,看到prototype.__proto__理解起来还是有点吃力,然后脑子里面就乱成一锅粥,就像我一样. 但是这是很正常的事情,没什么大不了的,就像我们想要学会跑

  • JavaScript原型与原型链深入探究使用方法

    目录 原型(prototype) 显示原型与隐式原型 原型链 原型链属性问题 原型链 instanceof 使用 练习 原型(prototype) 每一个函数都有一个 prototype 属性,它默认指向一个Object空对象(即称为:原型对象). <script> console.log(Date.prototype, typeof Date.prototype); function fun(){ } fun.prototype.test = function(){ //给原型对象添加一个方

  • 三张图带你搞懂JavaScript的原型对象与原型链

    对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__proto__混淆,二来它们之间的各种指向实在有些复杂,其实市面上已经有非常多的文章在尝试说清楚,有一张所谓很经典的图,上面画了各种线条,一会连接这个一会连接那个,说实话我自己看得就非常头晕,更谈不上完全理解了.所以我自己也想尝试一下,看看能不能把原型中的重要知识点拆分出来,用最简单的图表形式说清楚. 我们知道原型是一个对象,其他对象可以通过它实现属性继承.但是尼玛除了prototype,又有一个__

  • JavaScript三大重点同步异步与作用域和闭包及原型和原型链详解

    目录 1. 同步.异步 2. 作用域.闭包 闭包 作用域 3. 原型.原型链 原型(prototype) 原型链 如图所示,JS的三座大山: 同步.异步 作用域.闭包 原型.原型链 1. 同步.异步 JavaScript执行机制,重点有两点: JavaScript是一门单线程语言 Event Loop(事件循环)是JavaScript的执行机制 JS为什么是单线程 最初设计JS是用来在浏览器验证表单操控DOM元素的是一门脚本语言,如果js是多线程的,那么两个线程同时对一个DOM元素进行了相互冲突

  • 一文彻底理解JavaScript原型与原型链

    目录 前言 new对函数做了什么? 原型和原型链 借用原型方法 实现构造函数之间的继承 方案一 方案二 class extends实现继承 结语 前言 JavaScript中有许多内置对象,如:Object, Math, Date等.我们通常会这样使用它们: // 创建一个JavaScript Date实例 const date = new Date(); // 调用getFullYear方法,返回日期对象对应的年份 date.getFullYear(); // 调用Date的now方法 //

  • JavaScript原型链中函数和对象的理解

    目录 __ proto__ prototype.__ proto__ 理解 __ proto__ 最近在看高程4,原型链肯定是绕不过的,本瓜之前一直认为,只要记住这句话就可以了: 一个对象的隐式原型(__proto__)等于构造这个对象的构造函数的显式原型(prototype) 确实,所有对象都符合这句真理,在控制台打印一试便知: const str = new String("123") str.__proto__ === String.prototype // true const

  • JavaScript原型链及常见的继承方法

    目录 原型链 原型链的概念 原型链的问题 几种常见的继承方法 盗用构造函数 组合继承 原型式继承 寄生式继承 寄生组合式继承 原型链 原型链的概念 在JavaScript中,每一个构造函数都有一个原型,这个原型中有一个属性constructor会再次指回这个构造函数,这个构造函数所创造的实例对象,会有一个指针(也就是我们说的隐式原型__proto__或者是浏览器中显示的[[Prototype]])指向这个构造函数的原型对象.如果说该构造函数的原型对象也是由另外一个构造函数所创造的实例,那么该构造

  • javascript原型链学习记录之继承实现方式分析

    本文实例讲述了javascript原型链学习记录之继承实现方式.分享给大家供大家参考,具体如下: 在慕课网学习继承的笔记: 继承的几种方式: ① 使用构造函数实现继承 function Parent(){ this.name = 'parent'; } function Child(){ Parent.call(this); //在子类函数体里面执行父类的构造函数 this.type = 'child';//子类自己的属性 } Parent.call(this),this即实例,使用this执行

  • javascript原型链继承用法实例分析

    本文实例分析了javascript原型链继承的用法.分享给大家供大家参考.具体分析如下: 复制代码 代码如下: function Shape(){   this.name = 'shape';   this.toString = function(){    return this.name;   }  }    function TwoDShape(){   this.name = '2D shape';  }  function Triangle(side,height){   this.n

  • JavaScript原型链与继承操作实例总结

    本文实例讲述了JavaScript原型链与继承操作.分享给大家供大家参考,具体如下: 1. JavaScript继承 JavaScript继承可以说是发生在对象与对象之间,而原型链则是实现继承的主要方法: 1.1 原型链 利用原型让一引用类型继承另一个引用类型的属性和方法. 构造函数中有个prototype(每个函数中都有),指向他的原型对象,每个原型对象中也有一个constructor属性,指向原构造函数.通过构造函数创建的新对象中都有一个无法直接访问的[[proto]]属性,使得对象也指向构

  • Javascript原型链的原理详解

    本文实例分析了Javascript原型链的原理.分享给大家供大家参考,具体如下: 一.JavaScript原型链 ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法.其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.在JavaScript中,用 __proto__ 属性来表示一个对象的原型链.当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止! 比如现在有如下的代码: 扩展Object类,添加Clone和Extend

  • 深入浅出理解javaScript原型链

    本文实例讲述了javaScript的原型链.分享给大家供大家参考.具体分析如下: 对于javascript原型链,以前都觉得是个很深的东西,一直没有理解很明白,今天看了一些介绍后,发现这张图,表示再没有什么语言能比这张图说得清楚了. 看了这张图后突然对javascript有了质的理解. javascript的原型链有显式和隐式两种: 显式原型链:即我们常见的prototype: 隐式原型链:在一般环境下无法访问,即不可见,在FireFox下可以通过__proto__方式访问:隐式原型链用于jav

  • JavaScript原型链详解

    目录 1.构造函数和实例 2.属性Prototype 3.属性__proto__ 4.访问原型上的方法 5.构造函数也有__proto__ 6.构造函数的原型也有__proto__ 7.Object.prototype这个原型对象很特殊 8.总结 1.构造函数和实例 假设你声明一个方法叫做Foo() ,那么我们可以通过new Foo()来声明实例. function Foo() { console.log("我是一个构造方法"); } const f1 = new Foo(); 现在你

  • JavaScript 防盗链的原理以及破解方法

    HTTP Referer是header的一部分, 当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器藉此可以获得一些信息用于处理.通过该头域的值,我们可以检测到访问目标资源的源地址 背景 前段日子 在自学做项目的时候发现 明明在项目中引用了线上存在的图片 但是在自己的项目中却怎么也显示出来 查阅资料后发现是这些第三方网站设置了防盗链 破解防盗链 先说说防盗链的原理,http 协议中,如果从一个网页跳到另一个网页,http 头字段里面会带个

  • javascript 原型链维护和继承详解

    一.两个原型 很多人都知道javascript是原型继承,每个构造函数都有一个prototype成员,通过它就可以把javascript的继承演义的美轮美奂了. 其实啊,光靠这一个属性是无法完成javascript的继承. 我们在代码中使用的prototype完成继承在这里就不多说了.大家可以查一下资料. 另外一个看不见的prototype成员. 每一个实例都有有一条指向原型的prototype属性,这个属性是无法被访问到的,当然也就无法被修改了,因为这是维护javascript继承的基础. 复

随机推荐