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其实做了三件事:

  1. 创建一个新对象
  2. 将这个新对象的[[Prototype]]连接到调用函数的prototype
  3. 绑定调用函数的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的继承与原型链的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript继承与原型链

JavaScript中原型与原型链

5.JavaScript原型链和继承详解

关于JavaScript的原型继承与原型链

JavaScript原型链的理解

Javascript中的多重继承与原型链