javascript的继承与原型链
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript的继承与原型链相关的知识,希望对你有一定的参考价值。
由于js功底一直很差劲,所以觉得自己很多基础知识都不牢靠,现在花点时间了解下,尽管完成这篇博文我可能还是没有了如指掌,但是记录下来至少我还会再三翻阅的,并加深印象或者获得与现在不一样的理解。
什么是继承?
往往很多代码你已经完成构造后,又需要构造另一个与之雷同的功能函数;很多类似的举例比如:交通工具/汽车,Person/man,建筑物/学校,等;个人理解成:继承?子类继承父类?一个对象复制另一个对象?差不多,这样容易理解,类与类之间的复制(覆盖)行为,一个对象拥有另一个对象的属性和方法。这也解决了如何实现继承的思路及逻辑。
什么是原型链?
原型?我简单理解成===> Object.prototype
某个对象的一个属性,外界一般是不可见(在chrome中可以通过__proto__
获取),我们一般把它叫作[[Prototype]]
。原型链?obj1.[[Prototype]]
===> obj2.[[Prototype]]
===> obj3.[[Prototype]]
…. ===> Object.prototype。
基于原型链的继承
继承属性
js对象是动态的属性包(有自己的属性),有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。如上面。
遵循ECMAScript标准,someObject.[[Prototype]] 符号是用于指向 someObject的原型。从 ECMAScript 6 开始,[[Prototype]] 可以用Object.getPrototypeOf()和Object.setPrototypeOf()访问器来访问。这个等同于 javascript 的非标准但许多浏览器实现的属性 __proto__。 它不应该与函数(function)的func.prototype属性相混淆,func.prototype的作用是使用 new func() 创建的对象的实例的 [[Prototype]]。Object.prototype属性表示Object的原型对象。
举个栗子(尝试访问属性时会发生什么?将属性设置为对象将创建自己的属性。获取和设置属性的唯一限制是内置 getter 或 setter 的属性。):
// 让我们假设我们有一个对象 o, 其有自己的属性 a 和 b: // {a: 1, b: 2} // o 的原型 o.__proto__有属性 b 和 c: // {b: 3, c: 4} // 最后, o.__proto__.__proto__ 是 null. // 这就是原型链的末尾,即 null, // 根据定义,null 没有__proto__. // 综上,整个原型链如下: // {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.__proto__上还有一个‘b‘属性,但是它不会被访问到.这种情况称为"属性遮蔽 (property shadowing)". console.log(o.c); // 4 // c是o的自身属性吗?不是,那看看o.__proto__上有没有. // c是o.__proto__的自身属性吗?是的,该属性的值为4 console.log(o.d); // undefined // d是o的自身属性吗?不是,那看看o.__proto__上有没有. // d是o.__proto__的自身属性吗?不是,那看看o.__proto__.__proto__上有没有. // o.__proto__.__proto__为null,停止搜索, // 没有d属性,返回undefined
继承方法
在 JavaScript 里,任何函数都可以添加到对象上作为对象的属性。函数的继承与其他的属性继承没有差别,包括上面的“属性覆盖”(这种情况相当于其他语言的方法重写)。
注意的是,当继承的函数被调用时,this 指向的是当前继承的对象,而不是继承的函数所在的原型对象。
var o = { a: 2, m: function(){ return this.a + 1; } }; console.log(o.m()); // 3 // 当调用 o.m 时,‘this‘指向了o. var p = Object.create(o); // p是一个对象, p.__proto__是o. p.a = 4; // 创建 p 的自身属性a. console.log(p.m()); // 5 // 调用 p.m 时, ‘this‘指向 p. // 又因为 p 继承 o 的 m 函数 // 此时的‘this.a‘ 即 p.a,即 p 的自身属性 ‘a‘
使用不同的方法来创建对象和生成原型链
下面的方法就简单举个例子便于自己理解
普通
var o = {a: 1}; // o这个对象继承了Object.prototype上面的所有属性 // 所以可以这样使用 o.hasOwnProperty(‘a‘). // hasOwnProperty 是Object.prototype的自身属性。 // Object.prototype的原型为null。 // 原型链如下: // o ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; // 数组都继承于Array.prototype // (indexOf, forEach等方法都是从它继承而来). // 原型链如下: // a ---> Array.prototype ---> Object.prototype ---> null function f(){ return 2; } // 函数都继承于Function.prototype // (call, bind等方法都是从它继承而来): // f ---> Function.prototype ---> Object.prototype ---> null
构造器(即new)
new仅仅是函数的调用一种方式,
用new来调用函数有什么不同的呢?new其实做了三件事:
- 创建一个新对象
- 将这个新对象的
[[Prototype]]
连接到调用函数的prototype
上 - 绑定调用函数的
this
并调用
这样能解决很多思路比如先举个简单易懂的栗子:
function A(){ } var b = { show:function(){ console.log("这是b的show") } } //怎样让对象a和对象b的__proto__相连实现a继承b? //a的“构造函数”的[[prototype]]链接b A.prototype = b; A.prototype.construtor = A; var a = new A();//已经实现继承了; a.show();
楼下与楼上互不联系,只是多举一例而已
function Graph() { this.vertices = []; this.edges = []; } Graph.prototype = { addVertex: function(v){ this.vertices.push(v); } }; var g = new Graph(); // g是生成的对象,他的自身属性有‘vertices‘和‘edges‘. // 在g被实例化时,g.__proto__指向了Graph.prototype.
Object.create
var a = {a: 1}; // a ---> Object.prototype ---> null var b = Object.create(a); // b ---> a ---> Object.prototype ---> null console.log(b.a); // 1 (继承而来) var c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null); // d ---> null console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype
模拟类继承
/** * 实现 A 继承 B */ function B(b) { this.b = b } function A(a, b) { // 调用B并绑定this B.call(this, b) this.a = a } A.prototype = Object.assign({}, B.prototype) A.prototype.constructor = A var c = new A(1, 2) console.log(c.a) // 1 // c 拥有了只有B的实例才拥有的 b 属性 console.log(c.b) // 2
以上是关于javascript的继承与原型链的主要内容,如果未能解决你的问题,请参考以下文章