Javascript---原型及原型链

196 阅读5分钟

什么是原型

关于构造函数

什么是构造函数

在JS当中,函数分为构造函数和普通函数,而构造函数与普通函数的区别就是可以通过new关键字来创造新的实例。

eg:
function person(){
}
var person1 = new person()

构造函数的缺点

由于将所有的属性和方法都写在了构造函数当中,所以每创建一个新的实例就会将每个属性都创建一遍,但是往往我们在创建构造函数时,对于每个实例会具有相同的属性和方法。这也就造成了极大的内存的浪费。

原型(prototype)

我们所创建的每个函数都有个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象就是原型对象。

对于每个实例来说,也拥有它的prototype属性,它指向该实例构造函数的原型对象

原型对象

由于构造函数浪费内存的缺点,所以存在了原型对象这一概念。

原型对象的用途就是包含所有实例所共享的属性和方法的对象。

构造函数属性(constructor)

只要创建了一个新函数,就会对应的创建一个prototype属性,这个属性指向该函数的原型对象,而每个原型对象都会拥有一个constructor(构造函数)属性,这个属性包含一个指针,该指针指向prototype属性所在的构造函数。

构造函数与prototype和constructor关系

小栗子:

function person(){
}

person.prototype.name = "Nicholas";
person.prototype.age = 29;
person.prototype.job = "Software Engineer";
person.prototype.sayName = function (){
    alert(this.name);
}

var person 1 = new person();
var person 2 = new person();

对于这个构造函数,我们来分析他的原型以及constructor属性。

prototype的相关方法

isPrototypeOf()

用这个方法来判断某个prototype对象与某个实例之间是否有关系

object.prototype.isPrototypeOf(实例名)

hasOwnProperty()

每个实例都有hasOwnProperty()方法,用来判断某一个属性到底是本地属性还是继承自prototype对象的属性

实例名.hasOwnProperty("属性名")

是本地属性则为true,否则则为flase

in运算符

in运算符可以用来判断某个实例是否含有某个属性,不论是本地属性还是继承自prototype的属性

"属性名" in 实例名

是则返回true,否则返回flase

还可以用来遍历某个对象的所有属性

eg:
for(var per in person)

<!--在for循环内遍历整个person对象,将所包含的属性一一赋给per-->

什么是原型链

除了object的原型是null外,其余所有的对象和原型都有自己的原型,对象的原型指向原型对象。在JS中,往往具有多个层级,多个原型的层层嵌套就构成了原型链。

在查找一个对象的属性时,倘若在当前对象找不到该属性,则会沿着原型链一直往上查找,直至找到为止,若查找完整个原型链都没有找到,则返回undefined。

-proto-属性

对于所有的JS对象而言,都具有一个-proto-属性。-proto-属性指向实例对象的构造函数的原型。

而原型链的形成就是-proto-实现对象与对象之间的连接,直至最后指向null。

  • Object是作为众多new出来的实例的基类
  • Function是作为众多function出来的函数的基类
  • 构造函数的proto都指向Function.prototype
  • 原型对象的proto都指向Object.prototype
  • Object.prototype的proto指向null

注:

  • Object可以看为所有对象的集合
  • Function可以看为所有函数的集合
  • 函数也是一种特殊的对象

小栗子(原型链)

function Foo(){
    ...
}
let f1 = new Foo()

分析:

f1是Foo()的实例,所以f1的-proto-指向Foo的原型,也就是Foo.prototype。

Foo.prototype是Foo的原型,所以它的-proto-指向Object.prototype。

Object.prototype的-proto-指向null。

Foo()是构造函数,他的-proto-指向Function.prototype

Function.prototype是函数的原型,则指向Object.prototype。

Object.prototype的-proto-则最终指向null

原型的作用

  • 实现数据的共享,节约内存空间
  • 实现继承

节约空间

通常我们将构造函数的公共属性放置在原型当中,这样就节省了内存空间,防止了没新建一个实例就新建许多重复的属性的问题。

关于继承

关于对象之间的继承涉及到了原型链的继承和其他方式的继承,在这里一并整理。

原型链继承

对于每一个构造函数来说,都是一个prototype属性,他类似于一个指针,指向另一个对象,对于这一个对象中的所有内容,该构造函数的每一个实例都会继承。所以一般将构造函数中的公共属性以及方法写入这个对象当中,这样可以节省内存并提高效率。而对于每一个prototype都有他对应的constructor属性,指向他的构造函数。每个实例也有一个constructor属性,他指向prototype的constructor值。

原型链的继承其实就是使子对象的prototype与父对象的一个实例的prototype相等。由于这种相等之后的子对象的constructor指向了父对象,所以为了保持一致,在修改了子对象的prototype之后,应该紧接着修改子对象prototype的constructor值,使子对象整体保持一致。 小栗子:

构造函数继承

使用构造函数的继承即是使用call或apply的方法,将父构造函数绑定早子对象上,相当于在子对象中添加一行内容。 小栗子:

组合继承

组合继承,顾名思义,即是将构造函数继承与原型链继承组合使用,这样的继承结果包含了两种继承方式的特点,是较为常用的一种继承方式。 小栗子:

原型式继承

原型式继承实际就是建立了一个空对象作为中间量,我们使这个空对象的prototype等于父对象的prototype,再使子对象的prototype等于这个空对象的一个实例,达到了继承的目的。我们一般在使用的过程中,将这种方法封装为一个函数,方便我们在使用的过程中对其进行调用。 小栗子:

参考博客

blog.csdn.net/weixin_4469…

blog.csdn.net/wsymcxy/art…

www.jianshu.com/p/f30fa2799…

www.ruanyifeng.com/blog/2010/0…

www.ruanyifeng.com/blog/2010/0…

以及《Javascript高级程序设计》p147起