一天一个知识点 - 浅谈 JavaScript 原型链

284 阅读3分钟

前言

前些日子,在掘金上看到一片热门文章《在酷家乐做面试官的日子》。该文作者以面试官的角度,详细阐述了作为一名 web 应聘者应该具有哪些技能,才会更让人青睐。

在对比自身的过程中,发现有一些问题,或许了解,但不全面,这也是本系列文章诞生的缘由。

什么是原型与原型链

JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法(源自 MDN)


给大家讲个恐怖故事帮助理解:

"有一个伟大的科学家,

找到一个恐龙的化石,

克隆了一只小母鸡,

小母鸡拥有了吃人的能力,

科学家被吃了..."

在这个恐怖故事中,小母鸡是实例,恐龙化石的原型就是小母鸡原型链的第一层。

再给大家讲个幽默故事深化理解:

"有一个伟大的科学家,

找到一个恐龙的化石,

克隆了一只小母鸡,

没想到恐龙化石生前竟然是哈士奇,

小母鸡成为了科学家的二哈... "

在这个幽默故事中,小母鸡是实例,恐龙化石的原型就是小母鸡原型链的第一层,恐龙和哈士奇的原型合起来是它的原型链 (/ □ )

原型链和继承的关系

大家都知道面向对象的三大浪漫是继承、封装和多态。

JavaScript 虽然不能通过类来实现的面向对象,但通过原型链一样轻松实现继承。

继承属性

MDN上 这一段讲的特别通俗


/*
 * 创建一个构造函数,初始化时,对 this (this 指向实例对象) 赋值
 */
function func1(){
    this.a = 1
    this.b = 2
}

/*
 * 给构造函数的原型定义属性
 */
func1.prototype.b = 3
func1.prototype.c = 4

// 我们看看实例对象现在拥有了什么?
var f1 = new func1()

// a 是 f1 的自身属性,因此打印1
console.log(f1.a) => 1

// b 是 f1 的自身属性,因此打印2
// b 同时也是 f1 原型属性,它的值为3,但是不会被打印。官方解释是因为属性遮蔽 (property shadowing),3不会被访问到。
// 说人话就是,当从原型链查找属性时,优先找到了自身属性的 b ,因此原型属性的 b 不会再去找了。
console.log(f1.b) => 2

// c 不是 f1 的自身属性
// 继续找 f1 的原型,f1 的原型上有 c,因此打印3
console.log(f1.c) => 4

// 开始愚公移山...
// d 不是 f1 的自身属性
// 继续找 f1 的原型,f1 的原型上也没有 c
// 继续找 f1 的原型的原型,f1 的原型的原型是 object , object 也没有 d
// 继续找 object 的原型,object 的原型是 null,停止搜索
// 停止搜索,打印 undefined
console.log(f1.d) => undefined

继承方法

继承方法与继承属性没有区别


function func1(){
    this.a = 1
    this.b = 2
    this.func = function(){
        console.log('hello world')
    }
}

// f1 拥有了 func1 的 func 方法
var f1 = new func1()

f1.func() => hello world

参考

系列文章