js 原型链(转)

1.1 理解原型链

JavaScript中几乎所有的东西都是对象,我们说数组是对象、DOM节点是对象、函数等也是对象,创建对象的Object也是对象(本身是构造函数),那么有一个重要的问题:对象从哪里来?

这是一句废话,对象当然是通过一定方式创建出来的,根据实际类型不同,对象的创建方式也千差万别。比如函数,我们可以声明函数、使用Function构造函数创建等,比如数组,我们可以直接通过var arr = [] 的方式创建空数组,也可以通过new Array的方式创建,比如普通的对象,我们可以字面量创建、使用内置构造函数创建等等,花样太多了,以至于我们学习的时候头昏脑涨、不得要领。

其实,归根结底所有“类型”的对象都可以认为是由相应构造函数创建出来的。 函数由Function构造函数实例化而来,普通对象由Object构造函数实例化而来,数组对象由Array构造函数实例化而来,至于Object | Array | Function等他们本身是函数,当然也有自己的构造函数。

理解了上面一点,那么接下来我们在理解原型链的时候就会容易得多。

请看刺激的推导过程

前提 所有对象都由构造函数实例化而来,构造函数默认拥有与之相关联的原型对象
? ① 构造函数的原型对象也是对象,因此也有自己的构造函数
? ② 构造函数原型对象的构造函数,也有与之相关连的原型对象
? ③ 构造函数原型对象的原型对象(__proto__)也有自己的构造函数,其也拥有关联的原型对象
? 以上就形成了一种链式的访问结构,是为原型链

其实构造函数也是对象,所以构造函数本身作为对象而言也有自己的构造函数,而这个构造函数也拥有与之相关联的原型对象,以此类推。那么,这就是另一条原型链了。综上,我们可以得出原型链并不孤单的结论。

1.2 原型链结构

现在我们基本上把原型链的由来说清楚了,那么接下来通过具体的代码来分析原型链的整体结构。

示例代码

1  //01 自定义构造函数Person和Animal
2 function Person() {}
3 function Animal() {}
4 //02 使用构造函数创建实例对象
5 var p1 = new Person();
6 var p2 = new Person();
7 var a = new Animal();
8  //03 创建数组对象
9 var arrM = ["demoA","demoB"];

上面的代码非常简单,其中p1,p2和a它们是自定义构造函数的实例化对象。其次,我们采用快捷方式创建了arrM数组,arrM其实是内置构造函数Array的实例化对象。另外,Person和Animal这两个构造函数其实是Function构造函数的实例对象。理解以上几点后,我们就可以来看一下这几行代码对应的原型链结构图了。

原型链结构图说明:


① 因为复杂度关系,arrM对象的原型链结构图单独给出。

② Object.prototype是所有原型链的顶端,终点为null。

验证原型链相关的代码

 1  //[1] 验证p1、p2的原型对象为Person.prototype
 2 //    验证a    的原型对象为Animal.prototype
 3 console.log(p1.__proto__ == Person.prototype); //true
 4 console.log(p2.__proto__ == Person.prototype); //true
 5 console.log(a.__proto__ == Animal.prototype);  //true
 6 //[2] 获取Person.prototype|Animal.prototype构造函数
 7 //    验证Person.prototype|Animal.prototype原型对象为Object.prototype
 8 //    先删除实例成员,通过原型成员访问
 9 delete  Person.prototype.constructor;
10 delete  Animal.prototype.constructor;
11 console.log(Person.prototype.constructor == Object);    //true
12 console.log(Animal.prototype.constructor == Object);    //true
13 console.log(Person.prototype.__proto__ == Object.prototype);    //true
14 console.log(Animal.prototype.__proto__ == Object.prototype);    //true
15 //[3] 验证Person和Animal的构造函数为Function
16 //    验证Person和Animal构造函数的原型对象为空函数
17 console.log(Person.constructor == Function);                //true
18 console.log(Animal.constructor == Function);                //true
19 console.log(Person.__proto__ == Function.prototype);        //true
20 console.log(Animal.__proto__ == Function.prototype);        //true
21 //[4] 验证Function.prototype的构造函数为Function
22 console.log(Function.prototype.constructor == Function);    //true
23 //[5] 验证Function和Object的构造函数为Function
24 console.log(Function.constructor == Function);              //true
25 console.log(Object.constructor == Function);                //true
26 //[6] 验证Function.prototype的原型对象为Object.prototype而不是它自己
27 console.log(Function.prototype.__proto__ == Object.prototype);//true
28 //[7] 获取原型链的终点
29 console.log(Object.prototype.__proto__);                    //null

下面贴出数组对象的原型链结构图

验证数组对象原型链结构的代码示例

 1 //[1] 验证arrM的构造函数为Array
 2 //方法1
 3 console.log(arrM.constructor == Array);                 //true
 4 //方法2
 5 console.log(Object.prototype.toString.call(arrM));      //[object Array]
 6 //[2] 验证Array的构造函数为Function
 7 console.log(Array.constructor == Function);             //true
 8 //[3] 验证Array构造函数的原型对象为Function.prototype(空函数)
 9 console.log(Array.__proto__ == Function.prototype);     //true
10 //[4] 验证Array.prototype的构造函数为Object,原型对象为Object.prototype
11 delete Array.prototype.constructor;
12 console.log(Array.prototype.constructor == Object);         //true
13 console.log(Array.prototype.__proto__ == Object.prototype); //true

1.3 原型链的访问

原型链的访问规则

对象在访问属性或方法的时候,先检查自己的实例成员,如果存在那么就直接使用,如果不存在那么找到该对象的原型对象,查找原型对象上面是否有对应的成员,如果有那么就直接使用,如果没有那么就顺着原型链一直向上查找,如果找到则使用,找不到就重复该过程直到原型链的顶端,此时如果访问的是属性就返回undefined,方法则报错。

 1 function Person() {
 2     this.name = "wendingding";
 3 }
 4 Person.prototype = {
 5     constructor:Person,
 6     name:"自来熟",
 7     showName:function () {
 8         this.name.lastIndexOf()
 9     }
10 };
11 var p = new Person();
12 console.log(p.name);   //访问的是实例成员上面的name属性:wendingding
13 p.showName();          //打印wendingding
14 console.log(p.age);    //该属性原型链中并不存在,返回undefined
15 p.showAge();           //该属性原型链中并不存在,报错

概念和访问原则说明
? 实例成员:实例对象的属性或者是方法
? 原型成员:实例对象的原型对象的属性或者是方法
? 访问原则:就近原则

1.4 getPrototypeOf、isPrototypeOf和instanceof

Object.getPrototypeOf方法用于获取指定实例对象的原型对象,用法非常简单,只需要把实例对象作为参数传递,该方法就会把当前实例对象的原型对象返回给我们。说白了,Object的这个静态方法其作用就是返回实例对象__proto__属性指向的原型prototype。

1  //01 声明构造函数F
2 function F() {}
3 //02 使用构造函数F获取实例对象f
4 var f = new F();
5 //03 测试getPrototypeOf方法的使用
6 console.log(Object.getPrototypeOf(f));  //打印的结果为一个对象,该对象是F相关联的原型对象
7 console.log(Object.getPrototypeOf(f) === F.prototype);  //true
8 console.log(Object.getPrototypeOf(f) === f.__proto__);  //true

isPrototypeOf方法用于检查某对象是否在指定对象的原型链中,如果在,那么返回结果true,否则返回结果false。 

 1 //01 声明构造函数Person
 2 function Person() {}
 3 //02 获取实例化对象p
 4 var p = new Person();
 5 //03 测试isPrototypeOf的使用
 6 console.log(Person.prototype.isPrototypeOf(p)); //true
 7 console.log(Object.prototype.isPrototypeOf(p)); //true
 8 var arr = [1,2,3];
 9 console.log(Array.prototype.isPrototypeOf(arr));    //true
10 console.log(Object.prototype.isPrototypeOf(arr));   //true
11 console.log(Object.prototype.isPrototypeOf(Person));//true

上述代码的原型链
① p–>Person.prototype –>Object.prototype –>null
② arr–>Array.prototype –>Object.prototype –>null
Object.prototype因处于所有原型链的顶端,故所有实例对象都继承于Object.prototype

instanceof运算符的作用跟isPrototypeOf方法类似,左操作数是待检测的实例对象,右操作数是用于检测的构造函数。如果右操作数指定构造函数的原型对象在左操作数实例对象的原型链上面,则返回结果true,否则返回结果false。

 1  //01 声明构造函数Person
 2 function Person() {}
 3 //02 获取实例化对象p
 4 var p = new Person();
 5 //03 测试isPrototypeOf的使用
 6 console.log(p instanceof Person);   //true
 7 console.log(p instanceof Object);   //true
 8 //04 Object构造函数的原型对象在Function这个实例对象的原型链中
 9 console.log(Function instanceof Object); //true
10 //05 Function构造函数的原型对象在Object这个实例对象的原型链中
11 console.log(Object instanceof Function); //true

注意:不要错误的认为instanceof检查的是该实例对象是否从当前构造函数实例化创建的,其实它检查的是实例对象是否从当前指定构造函数的原型对象继承属性。

我们可以通过下面给出的代码示例来进一步理解

 1  //01 声明构造函数Person
 2 function Person() {}
 3 //02 获取实例化对象p
 4 var p1 = new Person();
 5 //03 测试isPrototypeOf的使用
 6 console.log(p1 instanceof Person);   //true
 7 //04 替换Person默认的原型对象
 8 Person.prototype = {
 9     constructor:Person,
10     showInfo:function () {
11         console.log("xxx");
12     }
13 };
14 //05 重置了构造函数原型对象之后,因为Person
15 console.log(p1 instanceof Person); //false
16 //06 在Person构造函数重置了原型对象后重新创建实例化对象
17 var p2 = new Person();
18 console.log(p2 instanceof Person);   //true
19 //==> 建议开发中,总是先设置构造函数的原型对象,之后在创建实例化对象

贴出上面代码的原型链结构图(部分)

1.5 原型链相关的继承

继承是面向对象编程的基本特征之一,JavaScript支持面向对象编程,在实现继承的时候,有多种可行方案。接下来,我们分别来认识下原型式继承、原型链继承以及在此基础上演变出来的组合继承

原型式继承基本写法

 1  //01 提供超类型|父类型构造函数
 2 function SuperClass() {}
 3 //02 设置父类型的原型属性和原型方法
 4 SuperClass.prototype.info = ‘SuperClass的信息‘;
 5 SuperClass.prototype.showInfo = function () {
 6     console.log(this.info);
 7 };
 8 //03 提供子类型
 9 function SubClass() {}
10 //04 设置继承(原型对象继承)
11 SubClass.prototype = SuperClass.prototype;
12 SubClass.prototype.constructor = SubClass;
13 var sub = new SubClass();
14 console.log(sub.info);          //SuperClass的信息
15 sub.showInfo();                 //SuperClass的信息

贴出原型式继承结构图

提示 该方式可以继承超类型中的原型成员,但是存在和超类型原型对象共享的问题

原型链继承

实现思想

核心:把父类的实例对象设置为子类的原型对象 SubClass.prototype = new SuperClass();
问题:无法为父构造函数(SuperClass)传递参数

原型链继承基本写法

 1 //01 提供超类型|父类型
 2 function SuperClass() {
 3     this.name = ‘SuperClass的名称‘;
 4     this.showName = function () {
 5         console.log(this.name);
 6     }
 7 }
 8 //02 设置父类型的原型属性和原型方法
 9 SuperClass.prototype.info = ‘SuperClass的信息‘;
10 SuperClass.prototype.showInfo = function () {
11     console.log(this.info);
12 };
13 //03 提供子类型
14 function SubClass() {}
15 //04 设置继承(原型对象继承)
16 var sup = new SuperClass();
17 SubClass.prototype = sup;
18 SubClass.prototype.constructor = SubClass;
19 var sub = new SubClass();
20 console.log(sub.name);          //SuperClass的名称
21 console.log(sub.info);          //SuperClass的信息
22 sub.showInfo();                 //SuperClass的信息
23 sub.showName();                 //SuperClass的名称

贴出原型链继承结构图

组合继承

实现思想

① 使用原型链实现对原型属性和方法的继承
② 通过伪造(冒充)构造函数来实现对实例成员的继承,并且解决了父构造函数传参问题

组合继承基本写法

 1  //01 提供超类型|父类型
 2 function SuperClass(name) {
 3     this.name = name;
 4     this.showName = function () {
 5         console.log(this.name);
 6     }
 7 }
 8 //02 设置父类型的原型属性和原型方法
 9 SuperClass.prototype.info = ‘SuperClass的信息‘;
10 SuperClass.prototype.showInfo = function () {
11     console.log(this.info);
12 };
13 //03 提供子类型
14 function SubClass(name) {
15     SuperClass.call(this,name);
16 }
17 //(1)获取父构造函数的实例成员  Person.call(this,name);
18 //(2)获取父构造函数的原型成员  SubClass.prototype = SuperClass.prototype;
19 SubClass.prototype = SuperClass.prototype;
20 SubClass.prototype.constructor = SubClass;
21 var sub_one = new SubClass("zhangsan");
22 var sub_two = new SubClass("lisi");
23 console.log(sub_one);
24 console.log(sub_two);

最后,贴出实例对象sub_one和sub_two的打印结果


原文地址:https://www.cnblogs.com/yelongsan/p/8867060.html

时间: 2024-10-13 06:19:11

js 原型链(转)的相关文章

简单粗暴地理解js原型链--js面向对象编程

简单粗暴地理解js原型链--js面向对象编程 原型链理解起来有点绕了,网上资料也是很多,每次晚上睡不着的时候总喜欢在网上找点原型链和闭包的文章看,效果极好. 不要纠结于那一堆术语了,那除了让你脑筋拧成麻花,真的不能帮你什么.简单粗暴点看原型链吧,想点与代码无关的事,比如人.妖以及人妖. 1)人是人他妈生的,妖是妖他妈生的.人和妖都是对象实例,而人他妈和妖他妈就是原型.原型也是对象,叫原型对象. 2)人他妈和人他爸啪啪啪能生出一堆人宝宝.妖他妈和妖他爸啪啪啪能生出一堆妖宝宝,啪啪啪就是构造函数,俗

JS 原型链图形详解

JS原型链 这篇文章是「深入ECMA-262-3」系列的一个概览和摘要.每个部分都包含了对应章节的链接,所以你可以阅读它们以便对其有更深的理解. 对象 ECMAScript做为一个高度抽象的面向对象语言,是通过对象来交互的.即使ECMAScript里边也有基本类型,但是,当需要的时候,它们也会被转换成对象. 一个对象就是一个属性集合,并拥有一个独立的prototype(原型)对象.这个prototype可以是一个对象或者null.* 让我们看一个关于对象的基本例子.一个对象的prototype是

深入理解JS原型链与继承

我 觉得阅读精彩的文章是提升自己最快的方法,而且我发现人在不同阶段看待同样的东西都会有不同的收获,有一天你看到一本好书或者好的文章,请记得收藏起来, 隔断时间再去看看,我想应该会有很大的收获.其实今天要讨论的主题,有许多人写过许多精彩的文章,但是今天我还是想把自己的理解的知识记录下来.我在学习 掌握JS原型链和继承的时候,就是看得@阮一峰老师的写的文章,觉得他写的技术类的文章都容易让理解,简明概要,又好理解.他是我学习JS路程里面一个比较佩服的导师,昨天重新看了他写的<Javascript 面向

js原型链与继承(初体验)

js原型链与继承是js中的重点,所以我们通过以下三个例子来进行详细的讲解. 首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询obj的原型,我们通过判断obj的原型是否与Object.prototype相等来证明是否存在obj的原型,答案返回true,所以存在.然后我们定义一个函数foo(),任何一个函数都有它的prototype对象,即函数的原型,我们可以在函数的原型上添加任意属性,之后通过new一个实例化的对象可以共享

浅谈js原型链与继承

js原型链与继承是js中的重点,所以我们通过以下三个例子来进行详细的讲解. 首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询obj的原型,我们通过判断obj的原型是否与Object.prototype相等来证明是否存在obj的原型,答案返回true,所以存在.然后我们定义一个函数foo(),任何一个函数都有它的prototype对象,即函数的原型,我们可以在函数的原型上添加任意属性,之后通过new一个实例化的对象可以共享

js 原型链和继承(转)

在理解继承之前,需要知道 js 的三个东西: 什么是 JS 原型链 this 的值到底是什么 JS 的 new 到底是干什么的 1. 什么是 JS 原型链? 我们知道 JS 有对象,比如 var obj = { name: "obj" }; 我们通过控制台把 obj 打印出来: 我们会发现 obj 已经有几个属性(方法)了.那么问题来了:valueOf / toString / constructor 是怎么来?我们并没有给 obj.valueOf 赋值呀. 上面这个图有点难懂,我手画

JS原型链

JS每个对象都有一个内部属性[[prototype]],我们通常称之为原型.原型的值可以是一个对象,也可以是null.如果它的值是一个对象,则这个对象也一定有自己的原型.这样就形成了一条线性的链,我们称之为原型链. 访问一个对象的原型可以使用ES5中的Object.getPrototypeOf方法,或者ES6中的__proto__属性. 原型链的作用是用来实现继承,比如我们新建一个数组,数组的方法就是从数组的原型上继承而来的. 在JavaScript中,每个函数 都有一个prototype属性,

JS原型链与几种继承方法的对比

继承的方式有两种:接口继承.实现继承接口继承:即虚函数,父类只定义接口,不具体实现子类继承接口,自己负责实现这个方法,运行时动态决定调用哪个实现.实现继承:父类实现的方法,子类可以直接调用 JS中只支持实现继承,以原型链来实现. 回顾一下构造函数.原型.实例的关系:每个构造函数都有一个原型对象,原型对象又都包含一个constructor指针指向构造函数,实例包含一个_proto_内部属性指向原型对象. 继承的实现方法(父类型叫Parent,子类型叫Child)原本Child.prototype

小谈js原型链和继承

原型(prototype)在js中可是担当着举足轻重的作用,原型的实现则是在原型链的基础上,理解原型链的原理后,对原型的使用会更加自如,也能体会到js语言的魅力. 本文章会涉及的内容 原型及原型对象 原型链(JavaScript核心部分) 类的继承 instanceof constructor 我们先用一个构造器来实现一个构造函数: function A(){ this.mark = "A"; this.changeMark = function(){ this.mark += &qu

JS 原型链学习总结

废话篇: 在js的学习过程中有一大难点就是原型链.学习的时候一直对这一内容不是十分的明白.纠结的我简直难受.,型号总算给他弄通了,哇咔咔,总算可以不用在睡梦中还想着他了. 正文篇: 要了解原型链我们首先要记住的一点是JS中所有的东西都可以用对象来理解.函数在JS中实际上也是一个对象.然后再去看原型链的东西. 上图是首要的: 实际上我们可以吧JS中的对象看成两种,一种是我们所熟知的一般的对象,还有一种就是JS中所有的方法对象. 一般对象: 对于一般的对象而言,其实际上就是一个方法与属性的集合,而在