js基础:原型和原型链

时间: 2019-08-23阅读: 426标签: 原型

本文研究一下JavaScript的核心基础——原型链和继承。

对于使用过基于类的语言(如Java或C#)的人来说,JavaScript的继承有点难以搞懂,因为它本身没有class这种东西。(ES6中引入了class关键字,看上去也像传统的OOP语言,但是那只是语法糖,底层还是基于原型)。


原型链

MDN上对于原型链的解释:

当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 __proto__)指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。

这段话可能难以理解,我们来举个例子:

const list = []; // 定义数组
list.__proto__ === Array.prototype; // true
list.__proto__.__proto__ === Object.prototype; // true
list.__proto__.__proto__.__proto__===null; // true
// 继承关系为
// list -> Array.prototype -> Object.prototype -> null

结合MDN的解释,我们来解释一下上述例子:

list是Array的实例对象,使用了字面量的方式创建了对象实例。

每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype )。
// list的构造函数是Array,所以list.__proto__指向构造函数Array的原型对象。
list.__proto__ === Array.prototype; // true
该原型对象也有一个自己的原型对象( __proto__ )
// Array.prototype也是对象,也有自己的原型对象,原型是Object.prototype
// 下面是数学运算(等量代换)
// list.__proto__ = Array.prototype
// Array.prototype.__proto__ = Object.prototype
list.__proto__.__proto__ === Object.prototype; // true
层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
// 目前我们来到了Object.prototype,根据规范,Object.prototype的原型对象为null
// list.__proto__ = Array.prototype
// Array.prototype.__proto__ = Object.prototype
// Object.prototype.__proto__ = null;
list.__proto__.__proto__.__proto__ === null; // true


原型链查找

当我们访问对象的属性或者方法时,会先从对象本身开始查找,如果查找不到,则查找对象的__proto__,层层向上查找,直到查找到属性,否则抛出错误。
const list = [];
list.toString();

属性查找过程如下:

查找list.toString()方法,没找到
继续查找list.__proto__,也就是Array.prototype,找到了
调用Array.prototype.toString


原型链结论

对象实例.__proto__ = 对象构造函数.prototype
几乎所有对象的原型都是Object.prototype
null是对象,但是null没有原型
属性/方法查找采用优先返回机制。


函数

经过原型链的简单介绍,相信大家对原型和原型链有了一个比较直观的了解了,现在要说到的是函数

我们知道,JavaScript函数也是对象,所以Function.__proto__指向Object.prototype。

上面的结论在JavaScript中是有问题的。我们来聊一聊函数

先看看简单一点的例子,大家知道,Object是对象的构造函数,构造函数也是函数,所有的函数的原型都是Function.prototype,所以Object.__proto__是等于Function.prototype的。

事实证明,也是如此。

image-20190923170248951

那么Function.__proto__为什么不等于Object.prototype呢?Function不是对象吗?

Function确实是对象,同时还是构造函数,可以通过new Function()来得到函数实例。

上面我们说到所有函数的原型是Function.prototype,所以Function这个构造函数的原型__proto__等于Function.prototype。

基于以上原理,还有以下相等关系:

    Object.__proto__ === Function.prototype
    Array.__proto__ === Function.prototype


引申的问题

我们知道Function.__proto__是指向Function.prototype,那个Function.prototype这个Function哪里来的?Function自己创造自己?那不是会死循环吗?

这个问题不是纯js层面能解决的,牵涉到底层实现,下面是网络上别人整理的结论,有需要的可以研究一下V8的源码,这样可以彻底解决这个问题。

用C/C++ 构造内部数据结构创建一个 OP 即(Object.prototype)以及初始化其内部属性但不包括行为。
用 C/C++ 构造内部数据结构创建一个 FP 即(Function.prototype)以及初始化其内部属性但不包括行为。
将 FP 的[[Prototype]]指向 OP。
用 C/C++ 构造内部数据结构创建各种内置引用类型。
将各内置引用类型的[[Prototype]]指向 FP。
将 Function 的 prototype 指向 FP。
将 Object 的 prototype 指向 OP。
用 Function 实例化出 OP,FP,以及 Object 的行为并挂载。
用 Object 实例化出除 Object 以及 Function 的其他内置引用类型的 prototype 属性对象。
用 Function 实例化出除Object 以及 Function 的其他内置引用类型的 prototype 属性对象的行为并挂载。
实例化内置对象 Math 以及 Grobal
至此,所有 内置类型构建完成。


函数结论

函数的原型都是Function.protype,构造函数也是函数,所以构造函数的原型也是Function.prototype


来自灵魂的拷问1

下面是一道有点难度的js基础题,可以感受一下:

function A() {
  
}

function B(a) {
  this.a = a;
}

function C(a) {
  if(a) {
    this.a = a;
  }
}

A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);
console.log(new B().a);
console.log(new C().a);

输出是

1
undefined
1

解释

为什么输出1?

因为new A()这个对象上没有属性a,所以去查找原型链,查到了F.prototype.a

为什么输出undefined?

因为new B时没有传递a,所以a是undefined,new B()这个对象是有a属性的,只不过值是undefined,所以不查原型链

为什么输出1?

因为new C()未传递a,所以a是undefined,由于if(a)的判断,new C()这个对象内部没有a属性,所以去查原型链


来自灵魂的拷问2

function F() {
  this.a = 1;
}
F.prototype.b = 2;

var f = new F();
console.log(f.hasOwnProperty('a'));
console.log(f.hasOwnProperty('b'));

输出是

true
false


解释

为什么输出true`?
输出true比较好理解,因为构造函数F声明了属性a,所以F的实例有a属性
为什么输出false?
b是f的原型对象F.prototype的属性,不是b自己的,不能拿别人的说成自己的。


结尾

本文研究了原型和原型链之间的关系以及常见对象的原型和原型链,对于特殊对象Function也研究了一下,如果能搞懂后面两个问题,那本文对你来说没什么问题了。


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/5485

Js原型对象

这里不介绍原型链。javascript 中有若干长得跟prototype,proto很想的属性/函数,这里简单总结一下他们都是啥,哪个是原型对象,哪个不是。[[Prototype]]这个对象的一个内置槽,对程序员是不可见

js里的__proto__和prototype

在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。即:对象具有属性proto,可称为隐式原型

JavaScript 原型的深入指南

不学会怎么处理对象,你在 JavaScript 道路就就走不了多远。它们几乎是 JavaScript 编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。

js原型与继承

继承是面向对象语言的一个重要概念。许多面向对象语言都支持两种继承方式:接口继承和实现继承;接口继承只继承方法签名,而实现继承则继承实际的方法。由于函数没有签名,所以ECMAScript只支持实现继承,而实现继承主要是依靠原型链来实现的。

js原型链的看法

对象:1,函数对象:有function创造出来的函数2,普通对象:除开函数对象之外的对象,都是普通对象**即普通对象obj是构造函数Object的一个实例,因此:

javascript中的prototype和__proto__的理解

在工作中有时候会看到prototype和__proto__这两个属性,对这两个属性我一直比较蒙圈,但是我通过查阅相关资料,决定做一下总结加深自己的理解

JavaScript 深入之从原型到原型链

每个函数都有一个prototype属性,就是我们经常在各种例子中看到的那个prototype,其次是__proto__, 绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在与Person.prototype中

JavaScript原型深入浅出

不学会怎么处理对象,你在 JavaScript 道路就就走不了多远。它们几乎是 JavaScript 编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。

理解Js原型和原型链

在JS中,函数的本质就是对象,它与其他对象不同的是,创建它的构造函数与创建其他对象的构造函数不一样。那产生函数对象的构造函数是什么呢?是一个叫做Function的特殊函数,通过new Function 产生的对象就是一个函数。

JS的原型链的理解。

JS是一个非常有魅力的语言也是一个比较烦人的语言,主要就是因为他的特殊性灵活性。JS的原型链,需要深刻的研究才能搞懂。不要纠结细节吧。实在不行就按这个死背住,慢慢就理解了。总之吧就是一句话万物皆对象。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!

文章投稿关于web前端网站点搜索站长推荐网站地图站长QQ:522607023

小程序专栏: 土味情话心理测试脑筋急转弯幽默笑话段子句子语录成语大全运营推广