对原型及原型链的简单理解

发布于 2024-03-29 09:07:41 字数 4610 浏览 14 评论 0

JavaScript 的语言特点

不同于 Java 和 C++ 这些基于类的面向对象语言,JavaScript 是一种基于原型的面向对象语言,因为 JavaScript 没有类的概念。

万物皆对象?

JavaScript 中一共有 6 种主要类型: stringnumberbooleannullundefinedobject ,常见到的关于**“JavaScript 中万物皆对象”**的说法其实是错误的, stringbooleannumbernullundefined 本身并不是对象,即使 typeof null 会返回 object 也是因为不同的对象在底层都表示为二进制,在 JavaScript 中如果二进制前三位都为 0 的话会被判断为 object 类型, null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“ object ”。

造成万物皆对象的错觉实际上是因为 JavaScript 中有许多特殊的对象子类型,也被称为内置对象,包括 StringNumberBooleanObjectFunctionArrayDateRegExpError 。这些内置函数可以通过 new 调用来当作构造函数,引擎可以根据实际情况将 stringnumber 等字面量转换成对象。

__proto__和 prototype

在 JavaScript 中,对象是拥有属性和方法的数据。每一个对象都有一个叫做 __proto__ 的内置属性,它包含对指定对象的内部原型的引用。

对象的创建基本有 3 种方法:

1、字面量创建

var foo = {
    name:blackstar
}

2、构造函数

function Foo(){
    this.name = blackstar
}
var foo  = new Foo()

3、Object.create()

var foo1 = {
    name:blackstar
}
var foo2 = Object.create(foo1)

补充一个小 Tips: var foo = {} 其实是 var foo = new Object() 的语法糖。相似的, function Foo(){}var Foo = new Function() 的语法糖、 var arr = []var arr = new Array() 的语法糖。

所以前两种方法都是借助了构造函数创建对象,这种方式创建的对象 __proto__ 属性指向其构造函数的 prototype 方法。每一个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。要注意:只有函数对象才有 prototype,普通对象并没有

说得再多可能都没有一个例子来得更直观易懂,来吧!

function Foo(){
    this.name = 'blackstar'
}
var foo = new Foo()
console.log(foo.__proto__)
console.log(Foo.prototype)
console.log(foo.__proto__===Foo.prototype) 

通过构造函数 Foo 我们创建了一个对象 foo,让我们到控制台里去看一看它的 __proto__ 指向什么?

shot1

确实对象 foo 的 __proto__ 指向了其构造函数 Foo 的 prototype ,Foo.prototype 就是 foo 的原型对象。

构造函数的 prototype 中有一个 constructor 属性指回到该构造函数,实例对象也有一个 constructor 属性指向创建它的构造函数。

Foo.prototype.constructor === Foo

foo.constructor === Foo

大部分情况 foo.__proto__ === foo.constructor.prototype (不包括 Object.create() 创建的) ,因为 Object.create() 的作用是创建一个具有指定原型且可选择性地包含指定属性的对象,它所创建的对象的 __proto__ 指向的是这个指定原型。

这其中指向来指向去的关系结合图示去理解会更容易一些:

prototype

有一个很有趣的事情, Object.__proto__===Function.prototype ,这是因为 Object 的构造函数是 Function。

原型链

使用原型对象的好处是可以让所有的对象实例共享它所包含的属性和方法,也就是说可以不必在构造函数中定义对象实例的信息,而是直接将它们添加到原型对象中。js 引擎查找对象属性时先查找对象本身是否存在,如果没有就去它的原型对象上查找,由于所有的对象都有 __proto__ 属性,这就形成了一个链条,也就是原型链。

改造一下上面的例子:

function Foo(){
    this.name = 'blackstar'
}
Foo.prototype.age = 25
var foo = new Foo()
console.log(foo.age)

shot2

我们为 foo 的原型对象添加 age 这个属性,寻找 age 属性时由于 foo 自身并没有,于是向上查找它的原型对象,诶,真好 Foo.prototype 上有,完毕。

这个原型链如下图所示,原型链的顶端是 Object.prototype ,而 __proto__ 最终指向 null

prototype chain

原型链继承

原型对象可以让所有的对象实例共享它所包含的属性和方法,利用这一特点我们可以实现继承。

function Cat(name){
    this.name = name
}
function Animal(){
    this.eat = function(){
        console.log('eat food')
    }
}
Cat.prototype = new Animal()
var miao = new Cat('miao')
console.log(miao.eat())

shot3

通过将 miao 的原型对象指向 Animal 便继承了原本自身没有的 eat 方法。关于继承,这里只是引申一个简单的例子,更详细的值得另开篇章讨论,这里暂且不细说。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

这个俗人

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

内心激荡

文章 0 评论 0

JSmiles

文章 0 评论 0

左秋

文章 0 评论 0

迪街小绵羊

文章 0 评论 0

瞳孔里扚悲伤

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文