前言

大家好,这里是@IT·平头哥联盟,我是团宠闪光少女——粉刷酱。

要怎么描述编程是个多幸福的工作呢?

我们很多人都想着如果能一辈子编程,那真是太好了。

而现实生活中,对未来的担忧和焦虑常常困扰着我们。我想,如果要努力维持现有的幸福的话,还是应该不停地学习。于是今天又去图书馆学习了一天,深入了解了js中的原型、原型链,现在跟大家分享一下~

正式开讲

原型和原型链大概是毕业时候面试的噩梦了,感觉怎么也理解不了,怎么也不会。后来静下心来想想,其实只是那时候在大学里实践的太少,以至于毕业时候要学习太多的实践知识,html、css、js还有各种框架,而静不下心来细细理解基础理论罢了。那现在,我们一起静下心来,好好理解一下javascript中的面向对象那些事儿吧~

先了解一下面向对象的意义。

一切事物皆对象,通过面向对象的方式,将现实世界的事物抽象成对象,现实世界中的关系抽象成类、继承,帮助人们实现对现实世界的抽象与数字建模。通过面向对象的方法,更利于用人理解的方式对复杂系统进行分析、设计与编程。同时,面向对象能有效提高编程的效率,通过封装技术,消息机制可以像搭积木的一样快速开发出一个全新的系统。面向对象是指一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的集合。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。

1.1面向对象

现在假设我是一个捏泥人的女娲,我每天的kpi是捏二百个泥人~~

var person = {name: "Nicholas",age: 29,job: "Software Engineer",sayName: function(){alert(this.name);}
};
复制代码

用对象字面量赋值方式捏了一个,挺简单的嘛,但是还得捏199个,有点累哇,巧了,我不是会编程的嘛,工厂模式走起来,先做一个小工(zuo)厂(fang) 生产小泥人,那我就只需要把小泥人信息输进去,就可以得到小泥人~~如下:

function createPerson(name, age, job){var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function(){alert(this.name);};return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
复制代码

这样我每天就节省了大量的时间,干点啥好呢,没事做呀~ 继续研究下我这个工厂,虽然解决了创建多个相似对象的问题,但是没有跟上时代面向对象的潮流,实在不高级,用构造函数的方式改写一下。

function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.sayName = function(){alert(this.name);};
}var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
复制代码

跟工厂函数代码相比,有以下几点区别:

1、没有显式地创建对象;2、直接将属性和方法赋给了 this 对象; 3、没有return语句。
复制代码

但是实际上用构造函数创建实例时,必须使用new操作符。以这种方式调用构造函数实际上会经历以下4个步骤:

(1) 创建一个新对象;(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);(3) 执行构造函数中的代码(为这个新对象添加属性);(4) 返回新对象。
复制代码

构造函数创建的实例person1和person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person。这就意味着创建的实例可以标识为一种特定的类型。这就有了面向对象的概念了。

但是这里还是存在一个问题需要优化的问题,每一个实例都创建了一个新的sayName的方法,而创建多个完成同样任务的Function实例是不必要,这里我们可以定义一个全局函数sayName,然后在构造函数中使用this.sayName指向这个全局函数。这样问题是解决了,但是破坏了面向对象的封装的特征。

所以下面我们需要了解一下js中的原型模式。

1.2 原型模式

js中,我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象。而这个对象就包含着在实例中共享的属性和方法。这就完美解决了我们上述的这些问题。给个栗子:

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();
person1.sayName();   //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName);  //true
复制代码

这里Person.prototype指向的就被称为原型对象,默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,指向这个prototype所在的函数。也就是Person.prototype.constructor==Person。

构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中 没有标准的方式访问[[Prototype]],但 Firefox、Safari和Chrome在每个对象上都支持一个属性__proto__,指向构造函数的原型对象。

当代码读取某个实例的某个属性时,首先从对象实例本身搜索,如果在实例中找到了该属性,则返回该属性的值;如果没有找到,则继续搜索该实例的原型对象,如果在原型对象中找到了这个属性,则返回该属性的值。

接下来介绍几个方法:

isPrototypeOf()方法判断实例与原型之间的关系。

alert(Person.prototype.isPrototypeOf(person1));  //true
alert(Person.prototype.isPrototypeOf(person2));  //true
复制代码

Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值,即实例对应的原型对象的值。

alert(Object.getPrototypeOf(person1) == Person.prototype); //true
复制代码

hasOwnProperty()方法属性存在于对象实例中时,才会返回 true。

in操作符会在通过对象实例能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。

hasPrototypeProperty()方法实例中具有某属性时为false,实例中不具有原型中具有时为true.

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){alert(this.name);
};
var person = new Person();
alert("name" in person);  //true
alert(person.hasOwnProperty("name"));  //false
alert(hasPrototypeProperty(person, "name"));  //true
person.name = "Greg";
alert("name" in person);  //true
alert(person.hasOwnProperty("name")); //true
alert(hasPrototypeProperty(person, "name"));  //false
复制代码

for-in 循环时,返回的是所有能够通过对象访问的、可枚举的(enumerated)属性,其中 既包括存在于实例中的属性,也包括存在于原型中的属性。

Object.keys()方法取得对象上所有可枚举的实例属性.

Object.getOwnPropertyNames()得到所有实例属性,无论它是否可枚举.

1.3 原型语法和特性

可以用对象字面量方法来重写整个原型对象。

function Person(){
}
Person.prototype = {name : "Nicholas",age : 29,job: "Software Engineer",sayName : function () {alert(this.name);}
};
复制代码

但此时constructor属性不再指向Person了,可以重设 constructor 属性。如下:

function Person(){
}
Person.prototype = {constructor : Person,name : "Nicholas", 7 age : 29,job: "Software Engineer",sayName : function () {alert(this.name);}
};
复制代码

这种方式重设 constructor 属性会导致它的[[Enumerable]]特性被设置为true。默认情况下,原生的constructor属性是不可枚举的。所以采用以下方式定义。

Object.defineProperty(Person.prototype, "constructor", {enumerable: false,value: Person
});
复制代码

原型的动态性:由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来——即使是先创建了实例后修改原型也照样如此。

var friend = new Person();
Person.prototype.sayHi = function(){alert("hi");
};
friend.sayHi(); //"hi"(没有问题!)
复制代码

我们知道,调用构造函数时会为实例添加一个指向最初原型的 [[Prototype]]指针,所以重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;它们引用的仍然是最初的原型。

function Person(){
}
var friend = new Person();
Person.prototype = {constructor: Person,name : "Nicholas",age : 29,job : "Software Engineer",sayName : function () {alert(this.name);}
};
friend.sayName();   //error
复制代码

我们也可以给原生对象的原型,定义新方法。例如:

String.prototype.startsWith = function (text) {return this.indexOf(text) == 0;
};
var msg = "Hello world!";
alert(msg.startsWith("Hello"));   //true
复制代码

原型中所有属性是被很多实例共享的,包含引用类型值的属性来说就会有些问题。

function Person(){
}
Person.prototype = {constructor: Person,name : "Nicholas",age : 29,job : "Software Engineer",friends : ["Shelby", "Court"],sayName : function () {alert(this.name);
} };
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends);    //"Shelby,Court,Van"
alert(person2.friends);    //"Shelby,Court,Van"
alert(person1.friends === person2.friends);  //true
复制代码

1.4 组合使用

组合使用构造函数模式和原型模式

function Person(name, age, job){
this.name = name; 3 this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];2}
Person.prototype = {constructor : Person,sayName : function(){alert(this.name);}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends);    //"Shelby,Count,Van"
alert(person2.friends);    //"Shelby,Count"
alert(person1.friends === person2.friends);//false
alert(person1.sayName === person2.sayName);//true
复制代码

动态原型模式

function Person(name, age, job){//属性this.name = name; this.age = age; this.job = job;//方法if (typeof this.sayName != "function"){Person.prototype.sayName = function(){alert(this.name);}; }
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();
复制代码

1.4 原型链

假如我们让原型对象等于另一个类型的实例,则此原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。

function SuperType(){this.property = true;
}
SuperType.prototype.getSuperValue = function(){return this.property;
};
function SubType(){this.subproperty = false;
}//继承了 SuperType,原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype
SubType.prototype = new SuperType();
var instance = new SubType();
alert(instance.getSuperValue());//true
复制代码

要注意instance.constructor现在指向的是SuperType,这是因为原来SubType.prototype中的 constructor被重写了的缘故。

通过实现原型链,本质上扩展了原型搜索机制。当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。就拿上面的例子来说,调用 instance.getSuperValue()会经历三个搜索步骤:1)搜索实例;2)搜索SubType.prototype; 3)搜索 SuperType.prototype,最后一步才会找到该方法。)在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。

所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。大家要记住,所有函数的默认原型都是 Object 的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。这也正是所有自定义类型都会继 toString()、 valueOf()等默认方法的根本原因。

以上~

一起学习哟~~ 比心~~

peace&love

转载于:https://juejin.im/post/5bf3e3a96fb9a049f069e008

在图书馆学习红宝书的一天(二)· 慢慢看原型、原型链就看懂了~相关推荐

  1. 《JavaScript高级程序设计(第四版)》红宝书学习笔记(2)(第四章:变量、作用域与内存)

    个人对第四版红宝书的学习笔记.不适合小白阅读.这是part2.持续更新,其他章节笔记看我主页. (记 * 的表示是ES6新增的知识点,记 ` 表示包含新知识点) 第四章:变量.作用域与内存 4.1 原 ...

  2. 《JavaScript高级程序设计(第四版)》红宝书学习笔记(1)

    个人对第四版红宝书的学习笔记.不适合小白阅读.这是part1,包含原书第二章(HTML中的Javascript)和第三章(语言基础).持续更新,其他章节笔记看我主页. (记 * 的表示是ES6新增的知 ...

  3. OpenGL红宝书的部分学习记录

    我看的OpenGL红宝书为: <OpenGL编程指南>-- 原书第9版 OpenGL Programming Guide – The Official Guide to Learning ...

  4. 前端学习路线-学习web前端的最 佳路线:必备javascript书籍【含红宝书和绿皮书、黄宝书等】

    需要资料的同学可以给我留言,留下你的邮箱即可. 跟着前端大佬推荐的路线学习进阶: html和css 关于最基础的html和css可以看看在线网站菜鸟教程: HTML 教程 | 菜鸟教程HTML 教程- ...

  5. 【JS红宝书学习】9客户端检测

    因为浏览器间存在大大小小的差异以及怪癖(quirk),开发人员应当优先采取一种更通用的方法,然后在使用特定于浏览器的技术增强该方案.当然这是红宝书的观点,是一种渐进增强的观点,还有与之相对的优雅降级思 ...

  6. OpenGL红宝书学习(1、概述)

    OpenGL简介 OpenGL 全称Open Graphics Library,一种用于渲染2D.3D矢量图形的跨语言.跨平台的应用程序编程接口(API).由1992年成立的OpenGL架构评审委员会 ...

  7. JS红宝书学习记录(四)

    js红宝书13-19 事件 事件处理程序 DOM0级事件处理程序:on+事件名,如onresize,onload等 DOM2级事件处理程序: addHandler: (element, type, h ...

  8. 用最简单的方法配置运行OpenGL红宝书第9版源码示例

    笔者真是苦逼啊,之前花了很多时间去学习"基于OpenGL的图形学"的开头部分,包括书本和老师的PPT.但是到自己尝试编译运行示例代码的时候真是困难重重.而且!在自己胡乱摸爬滚打终于 ...

  9. javascript 高级程序设计_JavaScript 经典「红宝书」,几代前端人的入门选择

    人的一生中总要读几本经典书,在这个"经典"泛滥的年代,什么才是权威的代表,我想大概是一本的书的口碑,能积累下上佳口碑的书,往往也是能经得住时间推敲的.比如这本: 相信许多前端开发者 ...

最新文章

  1. distinct吃亏记
  2. common pool2 mysql_连接池Commons Pool2的使用
  3. 如何在自定义数据源组件中限制用户的更改
  4. 关于网管软件中的预警功能的发展
  5. MySQL优化 之 Discuz论坛优化
  6. h5页面笔按下默认是拖动_屡屡刷屏的长页面H5原来是这样诞生的
  7. MySQL使用时遇到的问题
  8. MySql随笔part3 表操作
  9. node mysql查询回调_nodejs 数据库查询回调问题
  10. 【干货】推荐系统解构.pdf(附下载链接)
  11. Lync 小技巧-39-批量-设置-AD-分机-手机-启用-Lync-设置-Lync-分机
  12. Qt安卓开发环境搭建
  13. Java并发机制的底层实现原理(Java并发编程的艺术整理)
  14. 在python中查询excel内容
  15. HTML简易会员登录页面
  16. 恒生电子股份有限公司--软件测试--《社招、校招jd、校招行程,招聘动态》整理
  17. SpringBoot Mybatis注解调用Mysql存储过程并接收多个OUT结果集(多个mode=IN和mode=OUT参数)
  18. OA系统请假,出差等流程审批解析
  19. 使用PBO更新NV21纹理,shader处理并渲染到FBO中,再进行二次渲染的例子
  20. 联想造超级计算机,联想将造超级计算机 性能10倍于IBM蓝色基因

热门文章

  1. 鸿蒙基于JS搭建HelloWorld并修改国际化文件
  2. DevExpress的TextEdit限制输入内容的格式,比如只能输入数字
  3. madplay 操作步骤
  4. ansys参数化编程与命令手册_查看Bash手册--man命令
  5. 语言与golang语言运行速度_Golang语言情怀第3期 Go 语言数据类型
  6. 直播预告 | 后广告时代数据助力融合媒体用户收入增长
  7. 《你不知道的Javascript--中卷 学习总结》(类型、值)
  8. java集合系列之18 spring boot程序员的必修课
  9. react - antd (Table 与 Cascader 平级数据转树形实操)
  10. 第一章 关于python