原型和原型链梳理
在javascript中,经常会说到原型(prototype
)和原型链(prototype chain
)。我曾经以为我完全理解了,但是后来总是会发现不同的地方,这次进行一次系统的整理。
原型
什么是原型?
看一下权威指南中的说明
每一个javascript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。
如果是通过关键字new
和构造函数(constructor)
调用创建的对象,比如let x = new date()
,此时x
的原型就是constructor的prototype属性。
如果是通过对象直接量创建的对象的时候,比如let y = {}
。此时的y
会和object.prototype
相关联。
如果是通过object.create()
创建的对象,比如let z = object.create(obj)
。此时z
的原型就是它的第一个参数obj
(也可以是null)。
let x = new date(), y1 = {}, y2 =[], obj = {a:1}, z = object.create(obj); object.getprototypeof(x) === date.prototype // true object.getprototypeof(y1) === object.prototype // true // 注意这里是数组 object.getprototypeof(y2) === array.prototype // true object.getprototypeof(z) === obj // true
可以在浏览器调试工具中打印下object.prototype
究竟是什么样,会看到很多常见的方法。
上面都是内置构造函数,自定义的函数也是一样
function test(){ console.log('constructor test'); } let test = new test(); object.getprototypeof(test) === test.prototype // true
实例在被创建的时候,就已和自己的原型相关联。原型指向constructor的prototype,当然也可以是指定的对象。那么如此重要的一个特性是做什么的呢?简单来说,是用来实现继承的。
原型有什么作用呢?
思考一个简单的问题,定义一个对象let x = {a:1}
。x
中没有定义任何方法,但是却可以使用一些方法(比如:tostring
),那么这些方法来自哪里呢?
let x = {a:1}, y = new string('123'); // tostring继承自object.prototype x.tosting() // "[object object]" // split继承自string.prototype y.split('') // ["1", "2", "3"]
实例对象的原型中会有公共的方法,每个实例都可以访问到,这样无需再重复实现,也就是继承。所以javascript中的继承,不是通过复制而来的,而是通过原型继承的,而访问的过程,就是所谓的“原型链”了。
我们可以把需要实例继承的方法定义在它的原型上。
function test(){ } test.prototype.sayhi = function(){ console.log('hi world!') } let test1 = new test(); let test2 = new test(); // 实例使用原型上的方法 test1.sayhi() // 'hi world!' test2.sayhi() // 'hi world!'
原型链
每个实例对象(
object
)都有一个私有属性(可称之为__proto__
)指向它的构造函数的原型对象(prototype
)。该原型对象也有一个自己的原型对象(__proto__
) ,层层向上直到一个对象的原型对象为null
。根据定义,null
没有原型,并作为这个原型链中的最后一个环节。
注意: __proto__
是javascript 的非标准属性,但许多浏览器都已实现。
以上面的对象为例进行分析:对x={a:1}
这个对象调用tostring
方法,在x
对象中没有找到此方法,就会去x.__proto__
指向的原型对象(也就是object.prototype对象)中找,在object.prototype中找到了tostring
,就调用了。如果调用x.aa()
方法,x
自身没有,x.__proto__
中也没有,再往上一层x.__proto__.__proto__
发现是null
(object.prototype.__proto__ === null
)了,在自身和原型链中都没有找到,就会报错了。
实例对象在创建的时候就和原型对象相关联,除了__proto__
这个非标准属性外,可以通过以下方法访问原型对象。
-
object.getprototypeof()
方法返回指定对象的原型(内部[[prototype]]属性的值)。 -
object.setprototypeof()
方法设置一个指定的对象的原型 ( 即, 内部[[prototype]]属性)到另一个对象或 null。 -
isprototypeof()
方法用于测试一个对象是否存在于另一个对象的原型链上。prototypeobj.isprototypeof(object)
__proto__
和 prototype
属性
要注意区分__proto__
和prototype
:
-
__proto__
是实例对象的私有属性,指向它的构造函数的prototype
属性 -
prototype
是构造函数的属性,在调用构造函数创建实例对象时,实例对象通过__proto__
和prototype
相关联。同样的,prototype
对象中会有__proto__
,指向它自己的原型,这样一层层链接,直到指向null
,形成原型链 - 构造函数都是
function
的实例 - 函数默认有
prototype
属性,是定义公共方法的地方;prototype
中有constructor
属性,是指向函数本身。
// test为构造函数 function test(){}; // x为test的实例对象 let x = new test(); // x的原型链为:x -> test.prototype -> object.prototype -> null x.__proto__ === test.prototype; // true x.__proto__.__proto__ === object.prototype; // true x.__proto__.__proto__.__proto__ === null; // true // 构造函数也算是function的实例 // test的原型链为:test -> function.prototype -> object.prototype -> null test.__proto__ === function.prototype // true test.__proto__.__proto__ === object.prototype // true // 内置的构造函数也都算是function的实例 array.__proto__ === function.prototype // true date.__proto__ === function.prototype // true function.__proto__ === function.prototype // true object.__proto__ === function.prototype // true //constructor test.prototype.constructor === test // true x.constructor === test // true 注意,这里用的x原型(test.prototype中的属性)
最后放一张网图来总结下
上一篇: 单位组织旅游
下一篇: 基于hadoop的多个reduce 输出