js原型链和继承的理解

JavaScript07

js原型链和继承的理解,第1张

Object() Function() Array() 等等都为构造函数。

Js 面向对象与传统oop 有些不同,语法较高级 语法糖封装。

This 为指针。指向this作用域的调用者

1.原型继承链顶端为Object 。Js函数是对象

2.当读取对象中的属性时,先去实例本身中搜索,如搜索不到则去指向的原型中搜索

1.原型的存在意义在于实现继承共享,是在构造函数中定义的一个成员对象,在下次实例化时不需要在构造函数中定义成员 就可实现实例共享方法属性。

例子:通常为。构造函数.prototype.xxx=我想实现实例继承的东西 -》 new 构造函数 -》新实例a对象.原型指针指向构造函数的xxx对象(引用类型)

例子:Array 不等于 Array() 原因 Array为一个函数,而Array()为一个构造函数调用语句,故Array拥有prototype对象用于实例的共享继承,Array()产生一个实例 故只能拥有prototype对象的私有指针 proto

2.在使用原型继承时 不能使用字面量 构造函数.prototype={} 的方式重写原型对象 。因为会导致该原型对象的constructor属性被重写,在生成的实例中导致constructor指向Object并且会切断之前原型对象的联系,破坏原型链。

3.JavaScript 主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的

例子:

xxx实例. proto -》function xxx()构造函数.prototype对象故xxx. proto === xxx.prototype

xxx.prototype. proto -》Object.prototype因为所有对象都为Object函数构造来的。故xxx.prototype. proto ===Object.prototype。

Object.prototype. proto 为原型链顶端 proto 定义了尚未使用所以为null故Object.prototype. proto ===null约定俗成。

instanceof 用来判断某实例是否为某构造函数的实例

isPrototypeOf 用于判断某实例是否拥有某构造函数的原型对象指针

1.原型模式有忽略构造函数定义初始值步骤及原型中操作引用类型的缺点。

所以需要组合使用 构造函数模式+原型模式 来创建实例。在构造函数中定义实例的属性,而需共享的方法就定义在原型对象中。

继承:在子构造函数中调用 父.call(this, name)实现构造函数之间的属性继承。使用 子.prototype = new 父()子.prototype.constructor = 子实现方法的继承。

2.如要在生产环境下的构造函数新增方法(如Array) 需要使用一个类似于工厂函数的寄生构造函数模式 在构造函数中返回一个修改后的对象

本文主要参考了 MDN文档 和 知乎讨论 。

在js中,大部分东西都是对象,数组是对象,函数也是对象,对象更加是对象。 不管我们给数组和函数定义什么内容,它们总是有一些相同的方法和属性 。比如说hasOwnProperty(),toString()等:

这说明一个对象所拥有的属性不仅仅是它本身拥有的属性,它还会从其他对象中继承一些属性。当js在一个对象中找不到需要的属性时,它会到这个对象的父对象上去找,以此类推,这就构成了对象的原型链 。理解js的原型链对使用js的对象非常有帮助。

让我们通过一个例子由浅到深地理解原型链:

这是我们经常使用的创建对象的方式,将共同的方法放到Foo.prototype中,所有实例都共有这个方法了。

这是怎么实现的呢?我们看下面这张图的第一行:

我们定义的show函数在Foo.prototype中,当我们执行f1.show()时,js发现f1本身没有show这个属性,所以它就到f1的原型(也就是__proto__指向的对象)去找,找到了就可以调用。

图片第一行告诉了我们4点:

我们先看看Foo的原型吧!Foo是一个函数,它的构造函数是js内部的function Function(),Function的prototype指向了一个对象Function.prototype,因此Foo的__proto__就指向了Function.prototype,如图。

我们继续深入下去,Function.prototype这个对象,它就是一个普通的对象,它的构造函数是js内置的function Object(),function Object()的prototype指向Object.prototype,因此Function.prototype.__proto__就指向Object.prototype,这个对象中定义了所有对象共有的属性,比如我们之前说的hasOwnProperty()和toString()等。

Object.prototype就是原型链的终点了,它的__proto__是null,js查找属性时,如果到这里还没有找到,那就是undefined了。

到这里就不难理解为什么我们说在js中,函数也是对象了,它就是继承自对象的!

如果如有疑问,欢迎指出!

1.、js的对象可以分为函数对象和普通对象,每个对象都有 proto 属性,但是只有函数对象才有prototype属性(prototype=原型对象)

2、Object是最高级的函数对象,Function、Array、RegExp、Date、Boolean、Number、String等等是第二高级的函数对象,我们自己手写的function是再低一级的函数对象

3、 proto 是一个对象,它指向某一个prototype,并且有2个属性:constructor和 proto

先来看第一种写法

我们声明了一个构造函数Person,并为它的2个实例都定义了sayName方法

如果我们希望所有的实例都有sayName方法,减少重复的定义

可以像下面这么写:(第二种写法)

我们在构造函数Person中定义了一个数据成员和一个函数成员

每当我们创造一个新的Person实例时,都会在内存中单独为它分配私有的空间

也就是说,p1和p2都有自己的name属性和sayName方法

这个name属性和sayName方法是实例自己私有的

在控制台直接打印p1得到如下结果:(第一、第二种写法是等价的)

再来看第三种写法:

这样的话,每个Person实例依然都会有name属性和sayName方法

不同的地方在哪呢?

在控制台直接打印p1得到如下结果:

不难发现,当前这种写法是将sayName定义在Person的原型对象上的

也就是说,sayName方法不再是每个Person实例私有的,而是所有Person实例共享的

这样做 既不影响每个实例打印/输出自己的name值,又可以节省内存

还是相同的例子

当我们在程序中调用 p1.sayName方法时,编译器会 沿着原型链(自底向上) 地寻找原型对象上是否有这个方法

如上图所示,编译器会先从p1自身(最外层)开始找

然后去 p1. proto (即Person的原型对象 = 外层红色方框)里面找

最后再到 p1. proto . proto (即Object的原型对象 = 内层红色方框)里面找