文章直接从原型图解开始的,如果对一些概念不太清除,可以结合后面几节查看

1. 图解原型链

1.1 “铁三角关系”(重点)

function Person() {};
var p = new Person();

这个图描述了构造函数,实例对象和原型三者之间的关系,是原型链的基础:
(1)实例对象由构造函数new产生;
(2)构造函数的原型属性实例对象的原型对象均指向原型
(3)原型对象中有一个属性constructor指向对应的构造函数

原型链:p --> Person.prototype
描述:实例对象能够访问到 Person.prototype 中不同名的属性和方法
验证:

p instanceof Person; // truep.__proto__ === Person.prototype; // truePerson.prototype.constructor === Person; // true

1.2 以原型为构造函数

原型链:p --> Person.prototype --> Object.prototype --> null
描述:
(1)由于构造函数的原型也是对象,因此:它也有原型对象,指向Object.__proto__
(2)由于构造函数的原型的原型也是对象,因此:它也有原型对象,指向null(特例)
验证:

p instanceof Person; // true
p instanceof Object; // truePerson.prototype instanceof Object; // true

1.3 深入研究,引出Function构造函数

1、原型链1:见1.2中的原型

2、原型链2:Person --> Function.prototype --> Object.prototype --> null
描述:
(1)构造函数Person作为实例对象时,Person = new Function()隐式调用,因此Person --> Function.prototype
(2)由于Function.prototype也是对象,Function.prototype = new Object()隐式调用,因此Function.prototype --> Object.prototype
验证:

Person instanceof Function; // true
Person instanceof Object; // true
Function.prototype instanceof Object; // true

3、原型链3:Function --> Function.prototype --> Object.prototype --> null
描述:
构造函数Function作为实例对象时,Function = new Function()隐式调用,因此Function --> Function.prototype

Function 这条原型链是最为特殊的“铁三角关系”,理解Function = new Function()就非常好理解了
验证:

Function.__proto__ === Function.prototype; // true
Function instanceof Function; // true
Function instanceof Object; // true

1.4 完整的原型链


图中新增了Object = new Function()的逻辑

验证:

Object instanceof Function;// true

几个结论:
(1)对象都有原型对象,对象默认继承自其原型对象
(2)所有的函数都是 Function 的实例
(3)所有的原型链尾端都会指向Object.prototype

下面提几个问题:
(1)上图有几条原型链?分别列出来(上面已给出)
(2)如何在代码层面验证原型链上的继承关系?(见第四节)
(3)图中有几个“铁三角”关系?分别列出来

2. 原型链改写(重点)

当实例对象被创建时,其原型链就已经确定了,当其对应的原型属性指向改变时,也无法改变原型链

function Person({name="小A", age=21}={}) {this.name = name;this.age = age;
};// 情况1:在修改原型属性前实例化对象
var p1 = new Person();// 添加原型属性(方法)
Person.prototype.sayName = function() {console.log(this.name);
}
// Person.prototype.SayHi = function() {}// 情况2:在修改原型属性后实例化对象
var p2 = new Person();p1.sayName(); // "小A"
p2.sayName(); // "小A"

实例对象p1和实例对象p2的原型链相同,为 p1(p2) --> Person.prototype --> Object.prototype
=> 由于是在原有原型对象上添加的方法,相当于对象的扩展,故两个实例对象均能执行该方法

function Person({name="小A", age=21}={}) {this.name = name;this.age = age;
};// 情况1:在修改原型属性前实例化对象
var p1 = new Person();// 重写原型对象
Person.prototype = {sayName: function() {console.log(this.name);}
}// 情况2:在修改原型属性后实例化对象
var p2 = new Person();p2.sayName(); // "小A"
p1.sayName(); // p1.sayName is not a function

重写原型对象的方式,会改变实例对象的原型链,如下图所示:

但是,为什么p1的原型链没有变,而p2的原型链变了呢?

当实例对象被创建时,其原型链就已经确定了,当其对应的原型属性指向改变时,也无法改变原型链
原型链是以实例对象为核心的,不能被原型对象的改变而误导

重写原型对象的方式会在原型链继承中经常使用到!!!

3. 对象与函数(重点)

看到这里,我们可能已经分不清函数与对象了,思考30秒,函数与对象是什么关系?

官方定义: 在Javascript中,每一个函数实际上都是一个函数对象

function fn() {};
var obj = {};fn instanceof Object; // true
fn instanceof Function; // trueobj instanceof Object; // true
obj instanceof Function; // false

原型链解释:
fn对应的原型链:fn --> Function.prototype --> Object.prototype
obj对应的原型链:obj --> Object.prototype

从函数的定义来说: 在javascript中一切函数实际都是函数对象,但对象不一定是函数

Function instanceof Object; // true
Object instanceof Function; // trueFunction instanceof Function; // true

原型链解释:
Function对应的原型链(Function作为实例对象):Function --> Function.prototype --> Object.prototype
Object对应的原型链(Object作为实例对象):Object --> Function.prototype --> Object.prototype

由于Function和Object都是构造函数,在内置对象中,均会调用new Function()的方法

结论:
(1)函数一定是对象,但是对象不一定是函数
(2)对象都是由函数来创建的

针对第一点,这两个原型链可验证:
fn --> Function.prototype --> Object.prototype
obj --> Object.prototype

针对第二点,可这样验证:

var obj = { a: 1, b: 2}
var arr = [2, 'foo', false]// 实际过程
var obj = new Object()
obj.a = 1
obj.b = 2var arr = new Array()
arr[0] = 2
arr[1] = 'foo'
arr[2] = false//typeof Object === 'function'
//typeof Array === 'function

4. 几个定义

4.1 原型的定义和作用

function Person() {};
var p = new Person();

构造函数的prototype属性的值(Person.prototype),也可以说成通过调用构造函数而创建出来的那个实例对象的原型对象(p.__proto__)

  1. 只要是函数就有 prototype 属性,即函数的原型属性(由于对象是由函数创建的,因此对象也有prototype属性)
  2. 函数的原型属性也是对象(因此,这个对象也有对应的prototype属性)
  3. 由构造函数创建出来的对象会默认链接到其构造函数的这个属性上(constructor)
  4. 构造函数的 prototype 属性的作用是:实现数据共享(继承)

4.2 几个术语

实例对象中有一个属性叫 __proto__ ,它是非标准属性,指向构造函数的原型属性

Person.prototype 构造函数的原型属性
p.__proto__ 实例对象的原型对象

构造函数的原型属性实例对象的原型对象是一个东西,只是从不同的角度访问原型

5. 属性搜索原则和属性来源判断

5.1 属性搜索原则(重点)

当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索先从对象实例本身开始,如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回这个属性,如果没有找到,则继续在这个原型对象的原型对象中查找,直到找到这个属性,否则返回undefined

简言之,沿着对象的原型链查找属性,返回最近的属性,这就是属性搜索原则

function Person(){}Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){alert(this.name);
};var person1 = new Person();
var person2 = new Person();person1.name = "Greg";
alert(person1.name); //"Greg" 来自实例
alert(person2.name); //"Nicholas" 来自原型

同样的,这也是属性屏蔽的原则

// 接着上面的例子
delete person1.namel;
alert(person1.name); // "Nicholas" 来自原型

5.2 hasOwnProperty()方法与in操作符

使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是在原型中,这个方法只在给定属性存在于对象实例中时,才会返回true

function Person(){}Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){alert(this.name);
};var person1 = new Person();
var person2 = new Person();alert(person1.hasOwnProperty("name"));  //falseperson1.name = "Greg";
alert(person1.name); //"Greg" 来自实例
alert(person1.hasOwnProperty("name")); //truealert(person2.name); //"Nicholas" 来自原型
alert(person2.hasOwnProperty("name")); //falsedelete person1.name;
alert(person1.name); //"Nicholas" 来自原型
alert(person1.hasOwnProperty("name")); //false

有两种方式使用in操作符:单独使用和在for-in循环中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中

因此,同时使用hasOwnProperty()和in操作符,就可以确定某个属性到底是存在于对象中还是存在于原型中

function hasPrototypeProperty(object, name){return !object.hasOwnProperty(name) && (name in object);
}

顺便一提,由于in操作符会在整个原型链上查找属性,处于性能考虑,在使用for-in循环时,建议多加一层判别

function Person(){}Person.prototype.name = "Nicholas";
Person.prototype.age = 29;var p = new Person();
p.sex = "fale";for(key in p) {console.log(key); // sex name age
}// 实际上,我们一般只是在查找实例中的属性
for(key in p) {if(p.hasOwnProperty(key)) {console.log(key); // sex 屏蔽了原型中的属性}
}

5.3 instanceof操作符

instanceof 用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上

更形象来说,对于 A instanceof B来说,它的判断规则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。不理解没关系,下面会结合图例分析

function Person() {}
var p = new Person();
console.log(p instanceof Object);//true
console.log(p instanceof Person);//true

上一篇:《javascript高级程序设计》笔记:创建对象
下一篇:《javascript高级程序设计》笔记:继承

参考:
JavaScript之原型链的解读
三张图搞懂JavaScript的原型对象与原型链
继承与原型链

《javascript高级程序设计》笔记:原型图解相关推荐

  1. JavaScript高级程序设计笔记01 | 第一章到第四章 | 关键字与保留字 | 数据类型 | 操作符 | 作用域

    观前提示:大部分都是书上的内容,个人理解的部分的较少,看我的笔记还不如去看书 第二章 async:可选.表示应该立即下载脚本,但不应妨碍页面中的其他操作,比如下载其他资源或 等待加载其他脚本.只对外部 ...

  2. javascript高级程序设计--笔记01

    概述 JavaScript的实现包含三个部分: 1  核心(ECMAScript)   提供核心语言功能 2  文档对象模型(DOM)  一套提供了访问以及操作网页内容的API 3  浏览器对象模型( ...

  3. JavaScript高级程序设计笔记 - 第四章 变量 作用域 内存问题

    4.1 基本类型和引用类型的值 基本类型: 简单的数据段 引用类型: 指那些可能有多个值构成的对象, 指保存在内存中的对象 4.1.2 复制变量值 除了保存的方式不同之外,在从一个变量向另一个变量复制 ...

  4. javascript高级程序设计笔记

    1.要讲一个值转换成其对应的Boolean类型 ,可以调用转型函数Boolean(). var message="hello world!"; var messageAsBoole ...

  5. JavaScript高级程序设计笔记 事件冒泡和事件捕获

    1.事件冒泡 要理解事件冒泡,就得先知道事件流.事件流描述的是从页面接收事件的顺序,比如如下的代码: <body><div> click me!</div> < ...

  6. 《javascript高级程序设计》笔记:内存与执行环境

    上一篇:<javascript高级程序设计>笔记:继承 近几篇博客都会围绕着图中的知识点展开 由于javascript是一门具有自动垃圾收集机制的编程语言,开发者不必担心内存的分配和回收的 ...

  7. 《javascript高级程序设计》笔记:变量对象与预解析

    上一篇:<javascript高级程序设计>笔记:内存与执行环境 上篇文章中说到: (1)当执行流进入函数时,对应的执行环境就会生成 (2)执行环境创建时会生成变量对象,确定作用域链,确定 ...

  8. 《Javascript高级程序设计》读书笔记之对象创建

    <javascript高级程序设计>读过有两遍了,有些重要内容总是会忘记,写一下读书笔记备忘 创建对象 工厂模式 工厂模式优点:有了封装的概念,解决了创建多个相似对象的问题 缺点:没有解决 ...

  9. JavaScript高级程序设计(读书笔记)(六)

    本笔记汇总了作者认为"JavaScript高级程序设计"这本书的前七章知识重点,仅供参考. 第六章 面向对象的程序设计 面向对象(Object-Oriented, OO)的语言有一 ...

最新文章

  1. feign调用如何传递token_走进Spring Cloud之五 eureka Feign(服务调用者)
  2. 奇奇怪怪的小姿势——关于UI的各种Position
  3. Android动画模式
  4. 什么是Google Play保护以及如何确保Android安全?
  5. Linux 上 docker 安装 oracle-xe-11g
  6. 小小突击队为什么服务器正在维护中,4399小小突击队3月20日5:30更新维护公告!...
  7. 鸿蒙分布式通讯子系统,【鸿蒙】分布式通信子系统--让华为手机发现Hi3861开发板...
  8. php 代码下载_PHP实现下载功能的代码
  9. #ifndef HeaderName_h #define HeaderName_h #endif 使用详解
  10. flink安装以及运行自带wordcount示例(单机版,无hadoop环境)
  11. uniapp image图片切换动效_谷歌设计团队发布了一款动效神器,让 UI 和动效无缝打通...
  12. c++/ boost 库常见错误及解决方法总结
  13. SQL,不定条件查询,case,when,then,end,isnull,join,inner,on
  14. python 控制雕刻机_GitHub - cdhigh/PrinterCnc: 废打印机改装的大行程“雕刻机”,可以制作PCB。...
  15. 《SAP快速入门》视频课程
  16. 搜狗输入法彻底杜绝广告以及弹窗的几种办法
  17. MongoDB Could not find host matching read preference { mode: “primary” } for set shard1
  18. UG NX10.0制图——修改单位小数位数
  19. VBA 根据股票代码查询价格
  20. 【Proteus仿真】【51单片机】音乐盒电子琴设计

热门文章

  1. BigDecimal使用时的参数详解
  2. CVPR 2016 有什么值得关注的亮点?
  3. 浅淡Webservice、WSDL三种服务访问的方式(附案例)
  4. 分享一些优秀有趣的博客
  5. 信息系统项目管理师历年论文题目
  6. 阿里云自营建站买一年送一年
  7. WPF#39;s Style BasedOn
  8. Project Structure详解
  9. JAVA基础实例(一)
  10. XHTML 学前概述