【JS】深入理解JS原型和继承

说明:内容总结自王福朋的博客内容,原博客链接为:http://www.cnblogs.com/wangfupeng1988/tag/%E5%8E%9F%E5%9E%8B%E9%93%BE/

前言

 在学习JS中的原型,原型链,继承这些知识之前,我们先学习下基础知识:函数和对象的关系
 我们一直都知道,函数也是对象的一种,因为通过instanceof就可以判断出来。但是函数和对象的关系并不是简单的包含和被包含的关系,这两者之间的关系还是有点复杂的。接下来我们就来捋一捋。

首先,阐述一点,对象都是通过函数创建的
对于下面这种类型的代码,一般叫做“语法糖”

var obj = {a:10,b:20};
var arr = [5, 'x', true];

但是,其实上面这段代码的实质是下面这样的:

//var obj = { a: 10, b: 20 };
//var arr = [5, 'x', true];

 var obj = new Object();
 obj.a = 10;
 obj.b = 20;

 var arr = new Array();
 arr[0] = 5;
 arr[1] = 'x';
 arr[2] = true;

ObjectArray都是函数,可以自己用typeof函数进行验证。

所以,可以得出:对象都是通过函数创建的


正文

说完了前言,接下来我们进入正题。

1. 原型prototype

在前言中,我们说了函数也是一种对象,所以函数也是属性的集合,同时,也可以对函数进行自定义属性。
每个函数都有一个属性——prototype。这个prototype的属性值是一个对象(属性的集合),默认只有一个叫做constructor的属性,指向这个函数本身。 如下图所示:
这里写图片描述
上图中,SuperType是一个函数,右侧的方框就是它的原型

原型既然作为对象(属性的集合),除了constructor外,还可以自定义许多属性,比如下面这样的:
这里写图片描述

当然了,我们也可以在自己定义的方法的prototype中增加我们自己的属性,比如像下面这样的:

function Fn() { }
    Fn.prototype.name = '张三';
    Fn.prototype.getAge = function () {
       return 12;
};

这里写图片描述

那么问题来了:函数的prototype到底有何用呢?

在解决这个问题之前,我们还是先来看下另一个让人迷糊的属性:_proto_

2. “隐式原型”proto

我们先看一段非常常见的代码:

function Fn() { }
   Fn.prototype.name = '张三';
    Fn.prototype.getAge = function () {
       return 12;
};
   var fn = new Fn();
   console.log(fn.name);
   console.log(fn.getAge ());

即,Fn是一个函数,fn对象是从Fn函数new出来的,这样fn对象就可以调用Fn.prototype中的属性。

但是,因为每个对象都有一个隐藏的属性——“_proto_”,这个属性引用了创建这个对象的函数的prototype。即:fn._proto_ === Fn.prototype
那么,这里的_proto_到底是什么呢?

其实,这个__proto__是一个隐藏的属性,javascript不希望开发者用到这个属性值,有的低版本浏览器甚至不支持这个属性值。

var obj = {};
console.log(obj.__proto__);

这里写图片描述

console.log(Object.prototype);

这里写图片描述
从上面来看,obj.__proto__Object.prototype的属性一样!为什么呢?

原因是:obj这个对象本质上是被Object函数创建的,因此obj.proto=== Object.prototype。我们可以用一个图来表示。(图片来源于王福明博客)
图片来源于王福明博客
即,每个对象都有一个_proto_属性,指向创建该对象的函数的prototype。

说一下自定义函数的prototype:
自定义函数的prototype本质上就是和 var obj = {} 是一样的,都是被Object创建,所以它的__proto__指向的就是Object.prototype

但是,Object.prototype确实一个特例——它的__proto__指向的是null,切记切记!!!(图片来源于王福朋博客)
这里写图片描述

另外一个问题:函数也是一种对象,函数也有__proto__吗?
答:当然也不例外啦!
下面用一段代码和一张图来说明这个问题,看完相信就有个比较直观的理解啦!

function fn(x, y) {
        return x+y;
}
  console.log(fn(10,20));

//以下只是为了演示函数被Function创建的
  var fn1 = new Function("x","y","return x+y;");
  console.log(fn1(5,6));

用图表示就是:(图片来源于王福朋博客)
这里写图片描述
从上图可以看出:自定义函数Foo.__proto__指向Function.prototypeObject.__proto__指向Function.prototype

但是,为什么有Function.__proto__指向Function.prototype呢?
其实原因很简单:Function也是一个函数,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建。所以Function是被自身创建的。所以它的__proto__指向了自身的Prototype

最后一个问题:Function.prototype指向的对象,它的__proto__是不是也指向Object.prototype
答案是肯定的。因为Function.prototype指向的对象也是一个普通的被Object创建的对象,所以也遵循基本的规则。如下图:(图片来源于王福朋博客)
这里写图片描述

说了这么多,我们将上面这些图片整合到一整个图片,便于整体理解,图片如下:(图片来源于王福明博客)
这里写图片描述

3. instanceof

主要是说明下instanceof的判断规则是如何进行的。先看如下代码和图片:(图片来源于王福朋博客)

function fn() {
}
var f1 = new fn();

console.log(f1 instanceof fn);//true
console.log(f1 instanceof Object);//true

这里写图片描述

instanceof的判断规则为:
假设instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。

instanceof的判断规则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false

结合这个判断规则,上面的代码和图示相信很容易看懂了。

4. 原型继承

首先说一下什么是原型链:
 访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着_proto_这条链向上找,这就是原型链。

举一个例子说明下吧:
在实际应用中如何区分一个属性到底是基本的还是从原型中找到的呢?
答案就是:hasOwnProperty这个函数,特别是在for…in…循环中,一定要注意。(图片来源于王福明博客)
这里写图片描述

但是!!f1本身并没有hasOwnProperty这个方法,那是从哪里来的呢?答案很简单,是从Object.prototype中来的。看下图:(图片来源于王福明博客)
这里写图片描述
对象的原型链是沿着__proto__这条线走的,因此在查找f1.hasOwnProperty属性时,就会顺着原型链一直查找到Object.prototype

 由于所有对象的原型链都会找到Object.prototype,因此所有对象都会有Object.prototype的方法。这就是所谓的“继承”。


后记

现在只是理论上面了解了些,还是需要多多练习啊。。。

参考资料

  1. 你不知道的JavaScript
  2. 王福朋系列博客,链接如下:
    http://www.cnblogs.com/wangfupeng1988/tag/%E5%8E%9F%E5%9E%8B%E9%93%BE/
  • 54
    点赞
  • 211
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,我来回答你的问题。以下是一些JS原型继承原型链的面试题及其答案: 1. 什么是原型继承? 答:原型继承是通过将一个对象的原型作为另一个对象的原型来实现继承。在JS中,每个对象都有一个原型,它可以通过__proto__属性来访问。 2. 什么是原型链? 答:原型链是一种通过原型继承来实现对象间继承关系的机制。当我们访问一个对象的属性时,JS会首先在该对象自身查找,如果没有找到,就会在它的原型对象上查找,如果还没有找到,就会在原型对象的原型对象上查找,一直到Object.prototype对象为止,这个查找的过程就是原型链。 3. 如何实现一个简单的原型继承? 答: ```javascript function Person(name) { this.name = name; } Person.prototype.sayHi = function() { console.log('Hi, my name is ' + this.name); } function Student(name, grade) { Person.call(this, name); this.grade = grade; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.sayGrade = function() { console.log('My grade is ' + this.grade); } var tom = new Student('Tom', 3); tom.sayHi(); tom.sayGrade(); ``` 4. 如何判断一个对象是否是另一个对象的实例? 答:可以使用instanceof运算符来判断,例如: ```javascript var obj = {}; console.log(obj instanceof Object); // true ``` 5. 如何判断一个属性是在对象自身定义的还是继承原型? 答:可以使用JS提供的hasOwnProperty方法来判断,例如: ```javascript var obj = {}; console.log(obj.hasOwnProperty('toString')); // false console.log(Object.prototype.hasOwnProperty('toString')); // true ``` 希望这些问题和答案能够帮助你更好地理解JS原型继承原型链。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值