javascript中的原型和原型链(三)
1. 图解原型链
1.1 “铁三角关系”(重点)
function Person() {};
var p = new Person();

这个图描述了构造函数,实例对象和原型三者之间的关系,是原型链的基础:
(1)实例对象由构造函数new产生;
(2)构造函数的原型属性与实例对象的原型对象均指向原型
(3)原型对象中有一个属性constructor指向对应的构造函数
原型链:p --> Person.prototype
描述:实例对象能够访问到 Person.prototype 中不同名的属性和方法
验证:
p instanceof Person; // true p.__proto__ === Person.prototype; // true Person.prototype.constructor === Person; // true
1.2 以原型为构造函数

原型链:p --> Person.prototype --> Object.prototype --> null
描述:
(1)由于构造函数的原型也是对象,因此:它也有原型对象,指向Object.__proto__
(2)由于构造函数的原型的原型也是对象,因此:它也有原型对象,指向null(特例)
验证:
p instanceof Person; // true
p instanceof Object; // true Person.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; // true
obj 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; // true Function 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 = 2
var 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__)
- 只要是函数就有 prototype 属性,即函数的原型属性(由于对象是由函数创建的,因此对象也有prototype属性)
- 函数的原型属性也是对象(因此,这个对象也有对应的prototype属性)
- 由构造函数创建出来的对象会默认链接到其构造函数的这个属性上(constructor)
- 构造函数的 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")); //false
person1.name = "Greg";
alert(person1.name); //"Greg" 来自实例
alert(person1.hasOwnProperty("name")); //true
alert(person2.name); //"Nicholas" 来自原型
alert(person2.hasOwnProperty("name")); //false
delete 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中的对象和原型(3)
在Javascript中的对象和原型(二)中我们提到,用构造函数创建的对象里面,每个对象之间都是独立的,这样就会降低系统资源的利用率,解决这样问题,我们就要用到下面提到的原型对象. 一 原型对象 原型 ...
- Javascript中的对象和原型(一)(转载)
面向对象的语言(如Java)中有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,JavaScript 没有类的概念,因此它的对象也与基于类的语言中的对象有所不同. 要了解面向对象,首 ...
- javascript中的对象,原型,原型链和面向对象
一.javascript中的属性.方法 1.首先,关于javascript中的函数/“方法”,说明两点: 1)如果访问的对象属性是一个函数,有些开发者容易认为该函数属于这个对象,因此把“属性访问”叫做 ...
- 深入理解JavaScript中的继承:原型链篇
一.何为原型链 原型是一个对象,当我调用一个对象的方法时,如果该方法没有在对象里面,就会从对象的原型去寻找.JavaScript就是通过层层的原型,形成原型链. 二.谁拥有原型 任何对象都可以有原型, ...
- JavaScript中的继承(原型链)
一.原型链 ECMAScript中将原型链作为实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 实例1: function SupType() { this.pro ...
- JavaScript中的继承与原型链
先看一个例子 function User(){} var u1 = new User(); console.log(u1.prototype);// undefined 使用对象实例无法访问到prot ...
- javascript中的构造函数和原型及原型链
纯属个人理解,有错误的地方希望大牛指出,以免误人子弟 1.构造函数: 构造函数的作用 : 初始化由new创建出来的对象 new 的作用: 创建对象(空对象) new 后面跟的是函数调用,使用ne ...
- JavaScript中的对象与原型—你不知道的JavaScript上卷读书笔记(四)
一.对象 对象可以通过两种形式定义:声明(文字)形式和构造形式.即: var myObj = { key: value // ... }; 或: var myObj = new Object(); m ...
- JavaScript——中的prototype(原型)
JS中的prototype是JS中比较难理解的一个部分 本文基于下面几个知识点: 1 原型法设计模式 在.Net中可以使用clone()来实现原型法 原型法的主要思想是,现在有1个类A,我想要创建一个 ...
随机推荐
- sql server之批量数据导入
实际应用场景中,有时会需要把一批数据导入数据库.这批数据可能来源于另一个数据源.比较常规的做法是先读取到dataset,然后跑一个循环,每一行拼一句insert into语句,执行之.用过的人会知道, ...
- L2-014. 列车调度(Dilworth定理)
火车站的列车调度铁轨的结构如下图所示. Figure 两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有N条平行的轨道.每趟列车从入口可以选择任意一条轨道进入,最后从出口 ...
- redis 学习(14)-- HyperLogLog
HyperLogLog 什么是 HyperLogLog HyperLogLog 是基于 HyperLogLog 算法的一种数据结构,该算法可以在极小空间完成独立数量统计. 在本质上还是字符串类型. 重 ...
- Ubuntu分区挂载
创建主分区: 25G 主分区 空间起始位置 Ext4日志文件系统 / (ps:安装主要放这了,原因不明) 创建swap分区: 8192MB 逻辑分区 空间起 ...
- 2018年4月份,阿里最新的java程序员面试题目,仅供参考。
目录 技术一面(23问) 技术二面(3大块) 性能优化(21点) 项目实战(34块) JAVA方向技术考察点(15点) JAVA开发技术面试中可能问到的问题(17问) 阿里技术面试1 1.Java I ...
- 小伙伴们来看啊!开源智能机 Librem 5 规格发布。
下图是 Librem 5 的高配版规格: (看到这配置,忍不住吐槽一句:放到三年前都看不上……) Librem 5 的更详细规格如下: CPU: i.MX8M @ max. 1.5GHz 四核 Cor ...
- 小黄车ofo法人被限制出境,它究竟还能撑多久?
因为季节的原因,现在正是骑车的好时候,而且北京也开通了一条自行车的专用路.但就是在这么好的时候,我们发现,路边的小黄车却越来越少了,而且它的麻烦还不断! ofo法人被限制出境 6月12日消息,据上海市 ...
- Linux工具之netstat
1.简介 Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicas ...
- PAT Basic 1004 成绩排名 (20 分)
读入 n(>)名学生的姓名.学号.成绩,分别输出成绩最高和成绩最低学生的姓名和学号. 输入格式: 每个测试输入包含 1 个测试用例,格式为 第 1 行:正整数 n 第 2 行:第 1 个学生的姓 ...
- 小程序UI设计(2)-符合视觉规范-字体规范
下图是微信小程序官方要求字体规范 根据此要求小程序设计工具定制了符合规范的组件.如下图 工具使用时,将左侧组件拖拽到设计区域即可.字体大小和颜色都是按照规范设置的.在使用时根据微信要求在不同位置摆放即 ...