资讯专栏INFORMATION COLUMN

JS对象继承与原型链

QLQ / 2512人阅读

摘要:此用来定义通过构造器构造出来的对象的原型,构造器内部的代码用来给对象初始化。

对象继承 VS 类继承

在 class-based 的面向对象的世界里,要出现对象,必须先有类。类之间可以继承,类再使用 new 操作创建出实体,父子对象之间的继承体现在父类和子类上。你不能说 对象 a 继承了对象 b,只能说 class A 继承了 class B,然后他们各自有一个实例a、b。

JS中实现的是原型继承。在 prototype-based 的面向对象的世界里,继承是对象之间的事情,就像现实世界里,儿子继承父亲,没必要非得整出父亲类、儿子类。你有一个对象 a,通过 b = Object.create(a) (或者用类似的polyfill)创建出一个继承了 a 对象的实体 b。a 对象的属性和方法甚至后期对 a 的属性和方法修改,b 对象都直接继承过来。这里,我们说,a对象是b对象的原型。我们发现,这样的继承方式不需要,继承完全在对象间完成。

原型继承的工作机制

在对象属性查找时,如果对象本身存在这个属性,会优先使用它自己的(也就是概念 ownProperty);如果没有,就会查找对象的原型上有没有这个属性;如果原型对象也有自己的原型,递归查找,直到原型的根部,没有原型了停止查找,如果还是不存在,则返回 undefined。这就是传说中的 原型链,这也是 JS 中对象继承的体现方式,原型存在的意思。

这里顺带说一下如何获取一个对象的原型。ES5 提供了 Object.getPrototypeOf(obj) 方法来获取一个对象的原型,在 Chrome 中也可以使用非标准的 obj.__proto__

JS 在 prototype-based 的面向对象的基础上,引入了 构造器 来模拟 class-based 的模式, 配合 new 操作符使用。 构造器和 已有的 prototype 概念如何配合工作呢?

我们知道,JS 中的构造器就是一个普通函数,但是这个函数有一个特殊的属性(函数也是对象,所以也有属性) ———— prototype。此 prototype 用来定义通过构造器构造出来的对象的原型,构造器内部的代码用来给对象初始化。

function Ctor() {}
console.dir(Ctor.prototype);
// 构造器的 prototype 属性,默认值是
// { constructor: Ctor    }

Ctor.prototype.method = function() {
    console.log(1)
}

instance = new Ctor();

instance.constructor // Ctor
instance.method() // console.log(1)

instance 是如何获得 Ctor 的 prototype 属性上的数据的呢?好,还记得 JS 中的继承都是对象之间的继承吗?我们翻译一下 new 操作符到底干了什么。

instance = new Ctor() // 等价于
instance = Object.create(Ctor.prototype) // 用 Ctor 的 prototype 作为原型来创建一个新对象
Ctor.apply(instance) // 执行构造器用来初始化,构造器中的 this 指向 instance

我们称, instance 的原型是 Ctor.prototype, instance 是 Ctor 构造出来的 (new 出来的).

为了让 instance.constructor 能正确指向 instance 的构造器,一个构造器默认的 prototype 上已经存在 constructor 属性,并且指向构造器本身了。在我们覆盖构造器的 prototype 属性时,记得要把 prototype.constructor 属性定义了,让它指回到构造器,否则构造出来的 instance 的 constructor 属性就出问题了。所以我们可以看出,instance.constructor 其实是不是 instance 自己的属性,是原型链上定义的。

这里千万不要把 Ctor.prototype 误理解为是 Ctor 的原型。Ctor 的原型是 Object.getPrototypeOf(Ctor)(非标准写法:Ctor.__proto__),它是 Function.prototype, 因为 Ctor 是一个函数对象,所有函数都构造自 Function,原型是 Function.prototype。Ctor.prototype 是 Ctor 构造出来的实例的原型,不是 Ctor 的原型。

Object & Function 鸡生蛋蛋生鸡

有代码如下:

Object instanceof Function // true
Function instanceof Object // true
// what???

我们来挖掘一下 instanceof 操作符底层逻辑:

instance instanceof Ctor // 等价于

function instanceOf(instance, prototype) {
    var proto = Object.getPrototype(instance); // 取对象原型
    if( proto === null) return false; // 空
    if( proto === prototype) return true; // 原型匹配
    return instanceOf(proto, prototype); // 递归检查原型的原型
}

instance(instance, Ctor.prototype);

JS 中的继承终归是原型的继承,所以 class-based 中的 instanceof 概念最终也需要映射到 prototype 上。但是 JS 中的构造器名称有一个特殊之处,这个名称既表示了构造器这个函数,又表示了 class-based 概念中的 的概念, 而函数本身又是一种特殊的对象。

Object instanceof Function 之所以为 true,我们是把 Object 当做构造器看待,它是一个函数,它是 Function 的实例,所以同时这里我们把 Function 当作类型来看待,它是所有 function 的类。

Function instanceof Object 之所以为 true,我们是把 Function 当作对象看待,它虽然是一个构造函数,但是它也是对象,它是 Object 的实例,所以同时我们又把 Object 当作类型来看待,它是所有对象的类。

从原型角度:

// Object 是一个构造函数,它的原型是 Function.prototype
// Function.prototype 是所有函数的原型,call, apply 就挂在这里
Object.getPrototypeOf(Object) === Function.prototype 

// Function 也是一个构造函数
Object.getPrototypeOf(Function) === Function.prototype
// Function.prototype 本身是一个对象,所有对象的原型根部都是 Object.prototype
Object.getPrototypeOf(Function.prototype) === Object.prototype

这也印证了 JS 中函数是对象的概念。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91836.html

相关文章

  • 理解js原型继承

    摘要:相当于在用原型继承编写复杂代码前理解原型继承模型十分重要。同时,还要清楚代码中原型链的长度,并在必要时结束原型链,以避免可能存在的性能问题。 js是一门动态语言,js没有类的概念,ES6 新增了class 关键字,但只是语法糖,JavaScript 仍旧是基于原型。 至于继承,js的继承与java这种传统的继承不一样.js是基于原型链的继承. 在javascript里面,每个对象都有一...

    wthee 评论0 收藏0
  • 你是否理解js的ObjectFunction原型

    摘要:原型对象是由创建的,因此原型对象的构造函数是构造函数也可以是称为对象,原型对象也就继承了其生父构造函数中的数据,也同时继承了原型对象的数据。当然这条原型链中的数据,会被还是还是这类构造函数继承,但是不会被这些继承,他们不处于同一个链条上。 js中,Function的本质是什么?Object的本质又是什么?js中有几条原型链? showImg(https://segmentfault.c...

    itvincent 评论0 收藏0
  • JS学习笔记(第6章)(面向对象继承——JS继承的六大方式)

    摘要:除此之外,在超类型的原型中定义的方法,对子类型而言也是不可兼得,结果所有类型都只能用构造函数模式。创建对象增强对象指定对象继承属性这个例子的高效率体现在它只调用了一次构造函数。 1、原型链 原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。构造函数、原型和实例的关系:每个构造函数都有一个原型对象;原型对象都包含着一个指向构造函数的指针;实例都包含一个指向原型对象的...

    lscho 评论0 收藏0
  • 深入理解 js继承原型

    摘要:原型链与继承当谈到继承时,只有一种结构对象。如果对该图不怎么理解,不要着急,继续往下看基于原型链的继承对象是动态的属性包指其自己的属性。当使用操作符来作用这个函数时,它就可以被称为构造方法构造函数。 原型链与继承 当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object )都有一个私有属性(称之为proto)指向它的原型对象(prototype)。该原型对象也...

    xingqiba 评论0 收藏0
  • 进击JavaScript之(四)原型原型

    摘要:每一个由构造函数创建的对象都会默认的连接到该神秘对象上。在构造方法中也具有类似的功能,因此也称其为类实例与对象实例一般是指某一个构造函数创建出来的对象,我们称为构造函数的实例实例就是对象。表示该原型是与什么构造函数联系起来的。 本文您将看到以下内容: 传统构造函数的问题 一些相关概念 认识原型 构造、原型、实例三角结构图 对象的原型链 函数的构造函数Function 一句话说明什么...

    XBaron 评论0 收藏0

发表评论

0条评论

QLQ

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<