什么是原型语言

  1. 只有对象,没有类;对象继承对象,而不是类继承类。

  2. “原型对象”是核心概念。原型对象是新对象的模板,它将自身的属性共享给新对象。一个对象不但可以享有自己创建时和运行时定义的属性,而且可以享有原型对象的属性。

  3. 每一个对象都有自己的原型对象,所有对象构成一个树状的层级系统。root节点的顶层对象是一个语言原生的对象,只有它没有原型对象,其他所有对象都直接或间接继承它的属性。

原型语言创建有两个步骤

  1. 使用”原型对象”作为”模板”生成新对象 :这个步骤是必要的,这是每个对象出生的唯一方式。以原型为模板创建对象,这也是”原型”(prototype)的原意。

  2. 初始化内部属性 :这一步骤不是必要的。通俗点说,就是,对”复制品”不满意,我们可以”再加工”,使之获得不同于”模板”的”个性”。

所以在JavaScript的世界里,万物皆对象这个概念从一而终。

js高级---本地对象、内置对象、宿主对象

全局对象:一般全局对象会有两个,一个是ecma提供的Global对象,一个是宿主提供。如在浏览器中是window、在nodejs中是global。【所以啊,在浏览器中全局对象Global+window】

通常情况下ecma提供的Global对象对是不存在的,没有具体的对象

宿主对象-host object:即由 ECMAScript 实现的宿主环境提供的对象,包含两大类,一个是宿主提供,一个是自定义类对象,ECMAScript官方未定义的对象都属于宿主对象,所有非本地对象都是宿主对象。宿主提供对象原理--->由宿主框架通过某种机制注册到ECscript引擎中的对象,如宿主浏览器(以远景为参考)会向ECscript注入window对象,构建其实现javascript。

内置对象-Build-in object:由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现,即在引擎初始化阶段就被创建好的对象。这意味着开发者不必明确实例化内置对象,它已被实例化了Global(全局对象)、Math、JSON

基本包装类型对象:ECMAScript还提供了3个特殊的引用类型: Boolean、Number、String。这些类型与其他内置对象类型相似,但同时具有各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值得时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。 包装类型,是一个专门封装原始类型的值,并提供对原始类型的值执行操作的API对象

其他内置对象与基本包装类型对象的区别?

普通的内置对象与基本包装类型的主要区别就是对象的生命期,使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中,而自动创建的基本包装类型的对象,则只是存在于一行代码的执行瞬间,然后立即被立即销毁。这意味着我们不能再运行时为基本包装类型值添加属性和方法。

var s1="some text"; s1.color="red"; var s2=new String("some text"); s2.color="red"; console.log(s1.color);//undefined console.log(s2.color);//red console.log(s1==s2);//true console.log(s1===s2);//false

在第二行为s1添加一个color属性,第三行代码执行时,再次访问s1,结果s1的color属性被销毁了。详情推荐阅读《JavaScript内置对象--基本包装类型(Boolean、Number、String)详解

原生对象-native object:也叫内部对象、本地对象。独立于宿主环境的ECMAScript实现提供的对象。与宿主无关,在javascript(远景浏览器)、nodejs(node平台)、jscript(ie浏览器)、typescript(微软平台)等等中均有这些对象。简单来说,本地对象就是 ECMA-262 定义的类(引用类型)。

Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError、Global

在运行过程中动态创建的对象,需要new,以Number为例:

var n1=Number('1'); var n2=1; n2.xxx=2;console.log(n2); //undefined console.log(n1===n2)//false console.log(n1.toString()===n2.toString())//true console.log(n1.__proto__===Number)//false console.log(n2.__proto__===Number)//false console.log(n1.__proto__===n2.__proto__)//true

n1和n2虽然数值都是1,但n2的类型属于'object',n1则为'number',身为基本类型number的n1直接指向了数字1,而n2指向了一个地址,这个地址中存放了数值1,这就是对象和基本类型的区别。

但是原型对象只存在于函数对象。也就是本质上只要是通过new Function创建的函数对象会有一个原型对象。

而对于其他非Function的引用类型归根结底也是通过new Function创建的。

如上面提到的Array类型、Object类型。

实际上,在每个函数对象创建的时候,都会自带一个prototype的属性,这个属性相当于一个指针,指向他本身的原型对象,这个原型对象里包含着自定义的方法属性,

function a(){       this.name='xiaoming';       this.sayName=function () {           console.log(this.name);       }   }

在默认情况下,a.prototype下会带有一个constructor属性,这个属性指向创建当前函数对象的构造函数,比如这里

constructor指向构造函数a本身也就是说:a.prototypr.constructor==a   //true

另外默认还有一个_proto_属性,这个属性指向由创建这个函数对象的引用类型中继承而来的属性和方法。

当通过构造函数实例化一个对象b时,即new a();

首先这个new出来的对象属于普通对象,所以没有prototype属性。但他有_proto_这个属性,这个属性指向创建它的引用类型的原型对象,在这个例子中指向a.prototype,从而继承来自引用类型a的属性和方法。推荐阅读《JS 的 new 到底是干什么的?

var 对象 = new 函数对象 这个声明形式可以引申出:

函数.__proto__ ===Function.prototype Function.__proto__ === Function.prototype Object.__proto__ === Function.prototype //Objec也是个函数,函数都是由Function构造出来的。 Number.__proto__ === Function.prototype 构造函数.prototype.__proto__ ===Object.prototype Function.prototype.__proto__ ===Object.prototype Number.prototype.__proto__ ===Object.prototype Object.__proto__ .__proto__ ===null

理解了以上的关系后,'__proto__'是对象的属性、'prototype'是函数的属性这句话也就懂了

null是对象原型链的终点,其值既有(是一个对象)又无(不引用任何对象),代表着对象本源的一种混沌、虚无的状态,正与老子《道德经》中的“道”,有着同等的意义(心中一万只艹尼玛奔腾而过,还是写java爽啊)。比如:《undefined与null的区别

在JS中,undefined是全局对象的一个属性,它的初始值就是原始数据类型undefined,并且无法被配置,也无法被改变。undefined从字面意思上理解为“未定义”,即表示一个变量没有定义其值。

而null是一个JS字面量,表示空值,即没有对象。与undefined相比,null被认为是“期望一个对象,但是不引用任何对象的值”,而undefined是纯粹的“没有值”。

// null为对象原型链的终点 console.log(Object.getPrototypeOf(Object.prototype)); // null // null是一个对象 console.log(typeof null); // object // null 为空 console.log(!null); // true

JS中的所有事物都是对象,对象是拥有属性和方法的数据。

为了描述这些事物,JS便有了“原型(prototype)”的概念。

原型模式是js对继承的一种实现:使用原型,能复用代码,节省内存空间 (java类的代码在内存只有一份,然后每个对象执行方法都是引用类的代码,所有子类对象调用父类方法的时候,执行的代码都是同一份父类的方法代码。但是JS没有类,属性和方法都是存在对象之中,根本没有办法做到java那样通过类把代码共享给所有对象)。

推荐阅读《深刻理解JavaScript基于原型的面向对象

从一张图看懂原型对象、构造函数、实例对象之间的关系

  • prototype:构造函数中的属性,指向该构造函数的原型对象。

  • constructor:原型对象中的属性,指向该原型对象的构造函数

  • _proto_:实例中的属性,指向new这个实例的构造函数的原型对象

在JavaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)。

要清楚原型链,首先要弄清楚对象

普通对象

最普通的对象:有__proto__属性(指向其原型链),没有prototype属性。

原型对象(Person.prototype 原型对象还有constructor属性(指向构造函数对象))

函数对象:

凡是通过new Function()创建的都是函数对象。

拥有__proto__、prototype属性(指向原型对象)。

JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依此层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

原型-显式原型-隐式原型-共享原型链

显式原型(explicit prototype property )每一个函数在创建之后都会拥有一个名为prototype的属性,这个属性指向函数的原型对象。用来实现基于原型的继承与属性的共享。

隐式原型 (implicit prototype link) JS中任意对象都有一个内置属性__proto__(部分浏览器为[[prototype]]),指向创建这个对象的函数(即构造函数)constructor的prototype。用来构成原型链,同样用于实现基于原型的继承。

当我们「读取」 obj.toString 时,JS 引擎会做下面的事情:

1. 看看 obj 对象本身有没有 toString 属性。没有就走到下一步。

2. 看看 obj.__proto__ 对象有没有 toString 属性,发现 obj.__proto__ 有 toString 属性,于是找到了

3. 如果 obj.__proto__ 没有,那么浏览器会继续查看 obj.__proto__.__proto__,如果 obj.__proto__.__proto__ 也没有,那么浏览器会继续查,obj.__proto__.__proto__.proto__

直到找到 toString 或者 __proto__ 为 null(不管你从那个属性开始,连续引用__proto__的指针,最后输出的那个值就是null)。

上面的过程,就是「读」属性的「搜索过程」。

而这个「搜索过程」,是连着由 __proto__ 组成的链子一直走的。

这个链子,就叫做「原型链」。

要搞清楚 valueOf / toString / constructor 是怎么来的,就要用到 console.dir 了。

共享原型链(Shared prototype chain)此模式所有子对象及后代对象都共享一个原型(都是通过b.prototype=a.prototype;这种模式连接的对象),在这些后代对象上修改原型,会影响所以处在同一共享原型链上的所有对象。而且此模式只继承原型链上的属性和方法,通过this定义的属性和方法无法访问和继承

那么 obj.toString 和 obj2.toString 其实是同一个东西,也就是 obj2.__proto__.toString。

这有什么意义呢?

如果我们改写 obj2.__proto__.toString,那么 obj.toString 其实也会变!

这样 obj 和 obj2 就是具有某些相同行为的对象,这就是意义所在。

如果我们想让 obj.toString 和 obj2.toString 的行为不同怎么做呢?

直接赋值就好了:

obj.toString = function(){ return '新的 toString 方法' }

原型对象

每创建一个函数都会有一个prototype属性,这个属性是一个指针,指向一个对象(通过该构造函数创建实例对象的原型对象)。原型对象是包含特定类型的所有实例共享的属性和方法。原型对象的好处是,可以让所有实例对象共享它所包含的属性和方法

原型对象属于普通对象。Function.prototype是个例外,它是原型对象,却又是函数对象,作为一个函数对象,它又没有prototype属性。

对象与函数

拥有了描述事物的能力,却没有创造事物的能力,显然是不完整的,因此需要一个Object的生成器来进行对象的生成。

JS将生成器以构造函数constructor来表示,构造函数是一个指针,指向了一个函数。

函数(function) 函数是指一段在一起的、可以做某一件事的程序。构造函数是一种创建对象时使用的特殊函数。

对象的构造函数function Object同时也是一个对象,因此需要一个能够描述该对象的原型,该原型便是Function.prototype,函数的原型用来描述所有的函数。对象的构造函数的__proto__指向该原型。

函数的原型本身也是对象,因此其__proto__指向了对象的原型。同样,该对象也需要一个对应的生成器,即其构造函数function Function。

函数的构造函数是由函数生成的一个对象,所以其原型即为函数的原型,其隐式原型也同样为函数的原型Function.prototype。

instanceof操作符的内部实现机制和隐式原型、显式原型有直接的关系。instanceof的左值一般是一个对象,右值一般是一个构造函数,用来判断左值是否是右值的实例。它的实现原理是沿着左值的__proto__一直寻找到原型链的末端,直到其等于右值的prototype为止。

instanceof 的作用是判断一个对象是不是一个函数的实例。比如 obj instanceof fn, 实际上是判断fn的prototype是不是在obj的原型链上。所以

instanceof运算符的实质:用来检测 constructor.prototype是否存在于参数 object的原型链上。

根据上图展示的Object和Function的继承依赖关系,我们可以通过instanceof操作符来看一下Object和Function的关系:

console.log(Object instanceof Object); // true console.log(Object instanceof Function); // true console.log(Function instanceof Object); // true console.log(Function instanceof Function); // true

函数与对象相互依存,分别定义了事物的描述方法和事物的生成方法,在生成JS万物的过程中缺一不可。

Function instanceof Function    // true, why? Function.prototype是原型对象,却是函数对象
  • Object特殊在Object.prototype是凭空出来的。语法上,所有的{}都会被解释为new Object();

  • Function特殊在__proto__ == prototype。语法上,所有的函数声明都会被解释为new Function()。

我们来看Function和Object的特殊之处:

  1. Object是由Function创建的:因为Object.__proto__ === Funciton.prototype;

  2. 同理,Function.prototype是由Object.prototype创建的;

  3. Funciton是由Function自己创建的!

  4. Object.prototype是凭空出来的!

推荐阅读 《JavaScript 内置对象与原型链结构》与《JavaScript中的难点之原型和原型链

这几句话能解释一切关于原型方面的问题:

  1. 当 new 一个函数的时候会创建一个对象,『函数.prototype』 等于 『被创建对象.__proto__』

  2. 一切函数都是由 Function 这个函数创建的,所以『Function.prototype === 被创建的函数.__proto__』

  3. 一切函数的原型对象都是由 Object 这个函数创建的,所以『Object.prototype === 一切函数.prototype.__proto__』

推荐阅读:《对原型、原型链、 Function、Object 的理解

原型链是实现继承的主要方法

先说一下继承,许多OO语言都支持两张继承方式:接口继承、实现继承。

|- 接口继承:只继承方法签名

|- 实现继承:继承实际的方法

由于函数没有签名,在ECMAScript中无法实现接口继承,只支持实现继承,而实现继承主要是依靠原型链来实现。

原型链基本思路:

利用原型让一个引用类型继承另一个引用类型的属性和方法。

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数想指针(constructor),而实例对象都包含一个指向原型对象的内部指针(__proto__)。如果让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针(__proto__),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是另一个类型的实例……这就构成了实例与原型的链条。

原型链基本思路(图解):

推荐阅读《JS重点整理之JS原型链彻底搞清楚

类-对象冒充-class

类(Class)是面向对象程序设计(OOP,Object-Oriented Programming)实现信息封装的基础。类是一种用户定义类型,也称类类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象。

在ECMAScript 2015 中引入的JS类(classes)之前,要在JS中实现类便是采用原型继承的方式。

当把一个函数作为构造函数,使用new关键字来创建对象时,便可以把该函数看作是一个类,创建出来的对象则是该类的实例,其隐式原型__proto__指向的是该构造函数的原型。

在访问该对象的属性或方法时,JS会先搜索该对象中是否定义了该属性或方法,若没有定义,则会回溯到其__proto__指向的原型对象去搜索,若仍然未搜索到,则会继续回溯该原型的原型,直到搜索到原型链的终点null;

这种特性可以理解为:构造函数生成的实例,继承于该构造函数的原型。

得益于这种特性,我们可以使用定义构造函数的方式来定义类。

function Person() {} // 定义Person构造函数 // 通常以大写字母开头来定义类名 console.log(new Person() instanceof Person); // true

以上定义了Person类,该构造函数是由Function构造而来,所以其隐式原型指向函数的原型,而为了描述该事物,同时生成了该类的原型Person.prototype,该原型又是由Object构造而来,所以其隐式原型指向了对象的原型。

后记:文字有点乱,就是多篇文章的精华提炼。发现把一个自己懂的事情,深入浅出讲明白,并非易事。文有不妥之处,请留言告知,谢谢。

文章首发于:https://www.zhoulujun.cn/html/webfront/ECMAScript/js/2015_0715_119.html​,如果不妥之处,请到官网留言,谢谢!

参考文字:

【道生万物】理解Javascript原型链

js高级---本地对象、内置对象、宿主对象

js原型与原型链

「每日一题」什么是 JS 原型链?

JS理解原型、原型链

一张图弄清Javascript中的原型链、prototype、__proto__的关系

js中的原型、原型链、继承模式

说说原型(prototype)、原型链和原型继承

浅谈JS原型和原型链

原型语言解释

基于类 vs 基于原型

再谈javascriptjs原型与原型链及继承相关问题的更多相关文章

  1. 再谈javascript原型继承

    Javascript原型继承是一个被说烂掉了的话题,但是自己对于这个问题一直没有彻底理解,今天花了点时间又看了一遍<Javascript模式>中关于原型实现继承的几种方法,下面来一一说明下 ...

  2. 小谈js原型链和继承

    原型(prototype)在js中可是担当着举足轻重的作用,原型的实现则是在原型链的基础上,理解原型链的原理后,对原型的使用会更加自如,也能体会到js语言的魅力. 本文章会涉及的内容 原型及原型对象 ...

  3. JS原型链与继承别再被问倒了

    原文:详解JS原型链与继承 摘自JavaScript高级程序设计: 继承是OO语言中的一个最为人津津乐道的概念.许多OO语言都支持两种继承方式: 接口继承 和 实现继承 .接口继承只继承方法签名,而实 ...

  4. 浅谈javascript的原型及原型链

    浅谈javascript的原型及原型链 这里,我们列出原型的几个概念,如下: prototype属性 [[prototype]] __proto__ prototype属性 只要创建了一个函数,就会为 ...

  5. JS三座大山再学习(一、原型和原型链)

    原文地址 ## 前言 西瓜君之前学习了JS的基础知识与三座大山,但之后工作中没怎么用,印象不太深刻,这次打算再重学一下,打牢基础.冲鸭~~ 原型模式 JS实现继承的方式是通过原型和原型链实现的,JS中 ...

  6. JS三座大山再学习 ---- 原型和原型链

    本文已发布在西瓜君的个人博客,原文传送门 ## 前言 西瓜君之前学习了JS的基础知识与三座大山,但之后工作中没怎么用,印象不太深刻,这次打算再重学一下,打牢基础.冲鸭~~ 原型模式 JS实现继承的方式 ...

  7. 浅谈Javascript中的原型、原型链、继承

    构造函数,原型,实例三者的关系 构造函数: 构造函数是创建对象的一种常用方式, 其他创建对象的方式还包括工厂模式, 原型模式, 对象字面量等.我们来看一个简单的构造函数: function Produ ...

  8. 浅谈JavaScript原型与原型链

    对于很多前端开发者而言,JavaScript的原型实在是很让人头疼,所以我这边就整理了一下自己对应原型的一点理解,分享给大家,供交流使用 原型 说起原型,那就不得不说prototype.__proto ...

  9. JS进阶系列之原型、原型链

    最近在看 JavaScript忍者秘籍 这本书的时候,再加上最近遇到的关于原型.原型链的面试题,所以萌生了要把这些知识梳理一遍的想法,所以以下是我自己对原型.原型链的看法 什么是原型 提到原型,我们可 ...

随机推荐

  1. Angularjs 跨域请求

    不知道什么意思修改了service 参考http://blog.csdn.net/hj7jay/article/details/51767805 http://blog.csdn.net/tangsl ...

  2. js异步

    1.定时器都是异步操作 2.时间绑定都是异步操作 3.AJAX中一般我们采用异步操作 4.回调函数可以理解为异步操作 异步:指的是每一个任务有一个或多个回调函数,前一个任务结束后,不是执行后一个任务, ...

  3. vue引入外部.css文件,webpack将其与.vue中的样式混合打包了,怎么办?

    我使用vue-cli搭自己的博客,希望引入公共样式: // main.js import './assets/styles/common.css' 我本来是希望webpack打包后,能将这个样式独立打 ...

  4. python中的包与模块

    '''模块与模块之间的调用''' import first #调用整个变量 print(first.Index) # #调用函数 print(first.hello()) # per = first. ...

  5. WinForm打包或部署

    一.新建InstallShield项目 二. 三. 四. 五. 六. 七. 最后重新生成,安装包一般在E\Setup1\Setup1\Express\SingleImage\DiskImages\DI ...

  6. Linux下Python模式下【Tab】自动补全

    注:此文为转载他人博客,如有侵权,请联系我删除 1.我们需要一个tab补全的功能脚本  #!/usr/bin/python # python tab file import sys import re ...

  7. 人工智能--AI篇

    AI背景 在当今互联网信息高速发展的大背景下,人工智能(AI)已经开始走进了千家万户,逐渐和我们的生活接轨,那具体什么是AI呢? 什么是人工智能(AI)? 人工智能:简单理解就是由人制造出来的,有一定 ...

  8. SQL 序列-DML-DML-数据类型-用户管理、权限-事务-视图

    --DML--insert关键字--作用:往表中插入一条(多条)记录 --元祖(tuple)值式的插入(一次插入一条记录)--语法1:insert into tablename(column1,col ...

  9. 聊天机器人開發好消息!!DIALOGFLOW與微信的天作之合!!

    虽然DIALOGFLOW暂未能够与微信(WECHAT)或企业微信(ENTERPRISE WECHAT)进行任何技制上的连接INTERGRATION),确实限制了我们这些聊天机器人开发者的创作空间,因为 ...

  10. worker pool Handling 1 Million Requests per Minute with Golang

    小结: 1. 我们决定使用 Go 通道的一种常用模式构建一个两层的通道系统,一个通道用作任务队列,另一个来控制处理任务时的并发量. 这个办法是想以一种可持续的速率.并发地上传数据至 S3 存储,这样既 ...