知识点

面向对象编程

  • 我们熟悉的Java和C#里,面向对象的两个基本概念是类class和实例instance,而ES6以前的Javascript并没有设计class。
  • Javascript里的class是通过原型来实现的,通过将某个对象的__proto__属性指向另外一个对象来实现继承,如classA.__proto__ = classB,看上去就像B是A的父类,A继承自B类。

隐式原型、显式原型、原型链

  • 通常我们说的隐式原型指的是__proto__,而显式原型指的是prototype。
  • 所有对象都有__proto__,只有function对象拥有prototype,因此function对象既有__proto__又有prototype。
  • __proto__在低版本的IE里不提供,因此考虑到兼容性的话可以通过Object.getPrototypeOf(对象)来获得。
var obj = {};
console.log(obj.__proto__.constructor);//ƒ Object() { [native code] }
console.log(obj.prototype);//undefined var foo = function () { return 'hello world';};
console.log(foo.__proto__.constructor);//Function
console.log(foo.prototype.construtor);//foo
console.log(Object.getPrototypeOf(foo));//与foo.__proto__一样
  • 原型链通常指的是隐式原型链,JavaScript里任何东西都是对象,任何一个对象内部都有一个内置对象叫__proto__,即隐式原型,它可以包含任何东西让对象继承。当然__proto__本身也是一个对象,它自己也有自己的__proto__,这样一级一级向上,就构成了一个__proto__链,即原型链。原型链不会无限向上,它有个终点,可以称为原型链的顶端,或者root,它是一个特殊的对象,它的__proto__为null,如下所示:
obj.__proto__.__proto__......__proto__ === null;

第一个特殊对象:原型链的顶端root_prototype

  • 天地洪荒,宇宙玄黄,Javascript世界诞生了第一个特殊对象root_prototype,它是[native code],也就是内部实现。
  • 所有的孩子都是它创造的,也就是说所有的原型链最终都指向它。
obj.__proto__.__proto__......__proto__ === root_prototype;
  • root_prototype作为万物的爸爸,没有比他更大的了(也就是说他没有爸爸),所以root_prototype.__proto__ = null。
obj.__proto__.__proto__......__proto__.__proto__ === null;
  • 它定义了一些最基本的field和method,如toString之类的,所以它的孩子不用自己再实现就自动都拥有这些field和method。
console.log(obj.toString());//[object Object]
console.log(obj.godNotDefined());//obj.godNotDefined is not a function
  • 系统将root_prototype挂载在Object函数的prototype上,注意只是挂载在上面,而并不是说Object.prototype创造了所有对象,Object.prototype只是提供了一个指针让我们访问到root_prototype而已。
console.log(Object.prototype);//root_prototype, ƒ Object() { [native code] }
console.log(Object.prototype.__proto__);//root_prototype.__proto__, null

第二个特殊对象:函数的顶端function_root_prototype

  • 孤阴不生,独阳不长,光有root_prototype还不够,因此世界诞生了第二个特殊对象function_root_prototype,它也是[native code],也是内部实现。
  • 作为root_prototype的孩子,它同样有function_root_prototype.__proto__ = root_prototype;
  • 系统将function_root_prototype挂载在Function函数的prototype上,与root_prototype一样,Function.prototype只是提供了一个指针让我们访问到function_root_prototype而已,并不是说Function.prototype创造了所有函数。
console.log(Function.prototype);//function_root_prototype, ƒ () { [native code] }
console.log(Function.prototype.__proto__);//root_prototype, ƒ Object() { [native code] }
  • 所有函数都是由function_root_prototype创建的,Function函数也不例外,因此:
console.log(Function.__proto__);//function_root_prototype, ƒ () { [native code] }
  • 而对于Object函数来说,它也是function_root_prototype直接创建的,所以有:
console.log(Object.__proto__);//function_root_prototype, ƒ () { [native code] }
  • 不管是Function函数也好,Object函数也好,自定义函数也好,他们都是由function_root_prototype创建的,所以它们的__proto__必定都是function_root_prototype。
console.log(foo.__proto__); //function_root_prototype, ƒ () { [native code] }

函数特有的属性prototype

  • function_root_prototype是所有函数的爸爸,它带有一个特殊属性prototype。
  • 这个属性一共有三个作用:对于Function函数,它指向function_root_prototype;对于Object函数,它指向root_prototype;而对于自定义函数,它指向函数的实际函数体。
console.log(Function.prototype);//function_root_prototype, ƒ () { [native code] }
console.log(Object.prototype);//root_prototype, ƒ Object() { [native code] }
console.log(foo.prototype);//{constructor: ƒ}

JavaScript里经常说到的蛋鸡问题:

Object instanceof Function === true
Function instanceof Object === true
  • 这个问题主要是因为instanceof操作符和系统挂靠root_prototype到Object函数上导致的。形如“a instanceof b”指的是a的原型链上有没有出现过一个b函数的prototype,如果有,则返回true,如果没有,则返回false。
  • 对于Object instanceof Function === true,其实是:
//Object函数由function_root_prototype构建
Object.__proto__ === function_root_prototype;
//function_root_prototype挂靠在Function.prototype上
function_root_prototype === Function.prototype;
//所以满足instanceof的判断条件
Object.__proto__ === Function.prototype;
Object instanceof Function === true;
  • 对于Function instanceof Object === true,其实是:
//Function函数由function_root_prototype构建
Function.__proto__ === function_root_prototype;
//万物皆由root_prototype构建
function_root_prototype.__proto__ === root_prototype;
//root_prototype挂靠在Object.prototype上
root_prototype === Object.prototype;
//所以满足instanceof的判断条件
Function.__proto__.__proto__ === Object.prototype;
Function instanceof Object === true;
  • 这里要强调Object函数和其它函数的不同之处。Object之所以特殊,就是因为Object的prototype被系统设定为了root_prototype,仅此而已。
  • 而所谓的Object instanceof FunctionFunction instanceof Object的蛋鸡问题,前者应该来说是自然而然、不容置疑的,可以认为Object函数是Function创造出来的;而后者说白了只是因为系统设计上,强行规定了Object函数的特殊性,而导致的一个推论,而Function并非是Object创建的。

new操作符

  • var obj = new Object()其实相当于:
obj.__proto__ = Object.prototype;//根据上文我们知道这里挂靠的是root_prototype,系统内部实现
  • var obj = new Func()其实相当于:
//1、创建一个空对象
var obj = {};
//2、设置原型链
obj.__proto__ = Func.prototype;
//3、让Func中的this指向obj,并执行Func的函数体。
Func.call(obj);
  • new操作符返回的是包装好的新对象:
var Func = function () {}
var obj = new Func();
console.log(Func.__proto__);//function_root_prototype, f() [ native code]
console.log(Func.prototype);//函数体
console.log(obj.__proto__);//函数体
console.log(obj.prototype);//undefined
console.log(obj instanceof Object); //true
console.log(obj instanceof Function); //false console.log(typeof Number(1));//number
console.log(typeof 1);//number
console.log(typeof new Number(1));//object

参考资料

  1. Javascript原型链以及Object、Function之间的关系
  2. 廖雪峰官方网站:Javascript面向对象编程

结束语

无论你犯了多少错,或者进步得有多慢,你都走在了那些不曾尝试的人的前面。

详解Javascript中的原型与原型链的更多相关文章

  1. 详解javascript中的this对象

    详解javascript中的this对象 前言 Javascript是一门基于对象的动态语言,也就是说,所有东西都是对象,一个很典型的例子就是函数也被视为普通的对象.Javascript可以通过一定的 ...

  2. (转载)详解Javascript中prototype属性(推荐)

    在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不存在类(Class)的概念的,javascript中不 ...

  3. 【转】详解JavaScript中的this

    ref:http://blog.jobbole.com/39305/ 来源:foocoder 详解JavaScript中的this JavaScript中的this总是让人迷惑,应该是js众所周知的坑 ...

  4. 详解 javascript中offsetleft属性的用法(转)

    详解 javascript中offsetleft属性的用法 转载  2015-11-11   投稿:mrr    我要评论 本章节通过代码实例介绍一下offsetleft属性的用法,需要的朋友可以做一 ...

  5. 详解JavaScript中的原型

    前言 原型.原型链应该是被大多数前端er说烂的词,但是应该还有很多人不能完整的解释这两个内容,当然也包括我自己. 最早一篇原型链文章写于2019年07月,那个时候也是费了老大劲才理解到了七八成,到现在 ...

  6. 详解JavaScript中的原型和继承-转自颜海镜大大

    本文将会介绍面向对象,继承,原型等相关知识,涉及的知识点如下: 面向对象与继承 CEOC OLOO 臃肿的对象 原型与原型链 修改原型的方式 面向对象与继承 最近学习了下python,还写了篇博文&l ...

  7. 详解Javascript中的Array对象

    基础介绍 创建数组 和Object对象一样,创建Array也有2种方式:构造函数.字面量法. 构造函数创建 使用构造函数的方式可以通过new关键字来声明,如下所示: 12 var arr = new ...

  8. this详解:JAVASCRIPT中的this到底是谁?

    语法 this 全局对象 在全局执行上下文(函数之外),this引用的是全局对象. console.log(this.document === document); // true // In web ...

  9. 详解JavaScript中的Object对象

    Object是在javascript中一个被我们经常使用的类型,而且JS中的所有对象都是继承自Object对象的.虽说我们平时只是简单地使用了Object对象来存储数据,并没有使用到太多其他功能,但是 ...

随机推荐

  1. Python笔记_第一篇_面向过程_第一部分_1.Python环境的设置(含虚拟机)

    *Python环境的设置 Python的环境设置也就是所需工作平台的软件搭建.常用的搭建平台IOS系统+Linux系统和Windows+Linux系统的搭建.这里主要说明的是Windows+Linux ...

  2. 深入分析Java反射(五)-类实例化和类加载

    前提 其实在前面写过的<深入分析Java反射(一)-核心类库和方法>已经介绍过通过类名或者java.lang.Class实例去实例化一个对象,在<浅析Java中的资源加载>中也 ...

  3. PHPCMS 1分钟快速搭建

    一.下载CMS源码 https://www.yzmcms.com/xiazai 下载完成后得到一个这样的压缩包 接着下载PHPStudy,安装只要一直点下一步就可以了 安装完成后打开,如下图启动两个地 ...

  4. .NET 请求认证之access-token(oauth2.0与owin的结合)

    公司对外开放的接口,大多需要请求认证,如微信,阿里等.最近我刚好在搭建webapi框架.记录一下自己的理解. 一:请求认证的目的   之所以要对我们的接口进行请求认证,就是为了安全考虑的.以前都是在用 ...

  5. Xpath编码问题解决

    使用Xpath获取属性时,出现乱码问题,解决办法找了好多,终于解决,特将办法贴在这,供大家尝试 不要直接简单的将爬取的网页设置为utf-8, 先通过print(r.encoding)输出看看爬取的是什 ...

  6. 面向对象 part7 class

    类的定义 类实际上是个“特殊的函数“,就像能够定义函数表达式和函数声明一样,类语法 有两个组成部分:类表达式和类声明式 类声明 类声明没有提升 静态方法 只有构造函数名可以调用,实例无法使用.常用于应 ...

  7. ES6 find()

    Array.prototype.find() 返回数组中满足提供测试函数的第一个元素的值,否则返回undefined let b = blogs.find(function(e) => { re ...

  8. LeetCode——542. 01 矩阵

    给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离. 两个相邻元素间的距离为 1 . 示例 1: 输入: 0 0 0 0 1 0 0 0 0 输出: 0 0 0 0 1 0 0 0 ...

  9. Patroni 修改配置

    Patroni 修改配置 背景 使用 Patroni 部署 postgresql 集群的时候,不能单独修改单点的配置,这里需要通过 Patroni 来修改配置. 修改步骤 1. 修改 postgres ...

  10. Papa开启“点播孙子”模式,新型老年人服务能在国内扎根吗?

    "互联网+"对多个行业的全面赋能和渗入,的确让我们的生活与工作处处充满了便利.很多"跑断腿"才能办的事,现在只要在PC.智能手机上滑动鼠标.点击屏幕就能轻松搞定 ...