对于那些熟悉基于类的面向对象语言(Java 或者 C++)的开发者来说,JavaScript 的语法是比较怪异的,这是由于 JavaScript 是一门动态语言,而且它没有类的概念( ES6 新增了class 关键字,但只是语法糖,JavaScript 仍旧是基于原型)。
涉及到继承这一块,Javascript 只有一种结构,那就是:对象。在 javaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)。
虽然,原型继承经常被视作 JavaScript 的一个弱点,但事实上,原型继承模型比经典的继承模型更强大。举例来说,在原型继承模型的基础之上建立一个经典的继承模型是相当容易的。
一、基于原型链的继承
1.继承属性
JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依此层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。下面的代码将演示,当访问一个对象的属性时会发生的行为:
// 假定有一个对象 o, 其自身的属性(own properties)有 a 和 b: // {a: 1, b: 2} // o 的原型 o.[[Prototype]]有属性 b 和 c: // {b: 3, c: 4} // 最后, o.[[Prototype]].[[Prototype]] 是 null. // 这就是原型链的末尾,即 null, // 根据定义,null 没有[[Prototype]]. // 综上,整个原型链如下: // {a:1, b:2} ---> {b:3, c:4} ---> null console.log(o.a); // 1 // a是o的自身属性吗?是的,该属性的值为1 console.log(o.b); // 2 // b是o的自身属性吗?是的,该属性的值为2 // o.[[Prototype]]上还有一个'b'属性,但是它不会被访问到.这种情况称为"属性遮蔽 (property shadowing)". console.log(o.c); // 4 // c是o的自身属性吗?不是,那看看o.[[Prototype]]上有没有. // c是o.[[Prototype]]的自身属性吗?是的,该属性的值为4 console.log(o.d); // undefined // d是o的自身属性吗?不是,那看看o.[[Prototype]]上有没有. // d是o.[[Prototype]]的自身属性吗?不是,那看看o.[[Prototype]].[[Prototype]]上有没有. // o.[[Prototype]].[[Prototype]]为null,停止搜索, // 没有d属性,返回undefined
创建一个对象它自己的属性的方法就是设置这个对象的属性。唯一例外的获取和设置的行为规则就是当有一个 getter或者一个setter 被设置成继承的属性的时候。
2.继承方法
JavaScript 并没有其他基于类的语言所定义的“方法”。在 JavaScript 里,任何函数都可以添加到对象上作为对象的属性。函数的继承与其他的属性继承没有差别,包括上面的“属性遮蔽”(这种情况相当于其他语言的方法重写)。
新闻热点
疑难解答
图片精选