学习javascript面向对象 理解javascript原型和原型链


Posted in Javascript onJanuary 04, 2016

先看一张图,梳理梳理。

学习javascript面向对象 理解javascript原型和原型链

一、基本概念  
【原型链】每个构造函数都有一个对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,如果原型对象等于另一个原型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。如果另一个原型又是另一个原型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。
【原型对象】这个对象包含可以由特定类型的所有实例共享的属性和方法。所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype,这也正是所有自定义类型都会继承toString()、valueOf()方法的原因
【构造函数】构造函数与其他函数的区别在于调用它们的方式不同。一般来说,函数只要通过new操作符来调用,那它就可以作为构造函数;如果不通过new操作符来调用,那它跟普通函数也不会有什么两样。
[注意]用户自定义的函数和javascript中内置的构造函数可以当成构造函数使用
【构造函数的写法】构造函数始终应该以一个大写字母开头,而非构造函数以一个小写字母开头。这个做法借鉴自其他OO语言,主要是为了区别于ECMAScript中的其他函数;因为构造函数本身也是函数,只不过可以用来创建对象而已
【构造函数的三种使用场景】
[a]当作构造函数使用

var person = new Person("Nicholas",29,"software Engineer");
person.sayName();

[b]当作普通函数调用

Person("greg",27,"doctor");//添加到window
window.sayName();//"Greg"

[c]在另一个对象的作用域中调用

var o = new Object();
Person.call(o,"Kristen",25,"Nurse");
o.sayName();//"Kristen"

【prototype属性】只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。
[注意]只有函数才有prototype属性,object没有prototype属性
【constructor属性】在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针
[注意]创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性,至于其他方法则都是从Object继承而来的
【_proto_和[[prototype]]】当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262第5版管这个指针叫[[prototype]]。虽然在脚本中标准的方式访问[[prototype]],但firefox\safari\chrome在每个对象上都支持一个属性_proto_;而在其他实现中,这个属性对脚本则是完全不可见的。这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间
二、基本操作

【原型链查询】每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始,如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性,如果找到了这个属性,则返回该属性的值。
【添加实例属性】当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性,即使将这个属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。不过,使用delete操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性。
【原型的动态性】由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能立即从实例上反映出来,即使是先创建了实例后修改原型也照样如此。
[注意]不推荐在产品化的程序中修改原生对象的原型

function Person(){};
var friend = new Person();
Person.prototype.sayHi = function(){
  alert('hi');
}
friend.sayHi();//"hi"

【重写原型】调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。实例中的指针仅指向原型,而不指向构造函数。
三、基本方法

[1]isPrototypeOf():判断实例对象和原型对象是否存在于同一原型链中,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型

function Person(){};
var person1 = new Person();
var person2 = new Object();
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Object.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//false
console.log(Object.prototype.isPrototypeOf(person2));//true

[2]ECMAScript5新增方法Object.getPrototypeOf():这个方法返回[[Prototype]]的值

function Person(){};
var person1 = new Person();
var person2 = new Object();
console.log(Object.getPrototypeOf(person1)); //Person{}
console.log(Object.getPrototypeOf(person1) === Person.prototype); //true
console.log(Object.getPrototypeOf(person1) === Object.prototype); //false
console.log(Object.getPrototypeOf(person2)); //Object{}

[3]hasOwnProperty():检测一个属性是否存在于实例中

function Person(){
  Person.prototype.name = 'Nicholas';
}
var person1 = new Person();
//不存在实例中,但存在原型中
console.log(person1.hasOwnProperty("name"));//false
//不存在实例中,也不存在原型中
console.log(person1.hasOwnProperty("no"));//false
person1.name = 'Greg';
console.log(person1.name);//'Greg'
console.log(person1.hasOwnProperty('name'));//true
delete person1.name;
console.log(person1.name);//"Nicholas"
console.log(person1.hasOwnProperty('name'));//false

[4]ECMAScript5的Object.getOwnPropertyDescriptor():只能用于取得实例属性的描述符,要取得原型属性的描述符,必须直接在原型对象上调用Object.getOwnPropertyDescription()方法

function Person(){
  Person.prototype.name = 'Nicholas';
}
var person1 = new Person();
person1.name = 'cook';
console.log(Object.getOwnPropertyDescriptor(person1,"name"));//Object {value: "cook", writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(Person.prototype,"name"));//Object {value: "Nicholas", writable: true, enumerable: true, configurable: true}

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

function Person(){}
var person1 = new Person();
person1.name = 'cook';
console.log("name" in person1);//true
console.log("name" in Person.prototype);//false
var person2 = new Person();
Person.prototype.name = 'cook';
console.log("name" in person2);//true
console.log("name" in Person.prototype);//true

[6]同时使用hasOwnProperty()方法和in操作符,来确定属性是否存在于实例中

//hasOwnProperty()返回false,且in操作符返回true,则函数返回true,判定是原型中的属性
function hasPrototypeProperty(object,name){
  return !object.hasOwnProperty(name) && (name in object);
}
function Person(){
  Person.prototype.name = 'Nicholas';
}
var person1 = new Person();
console.log(hasPrototypeProperty(person1,'name'));//true
person1.name = 'cook';
console.log(hasPrototypeProperty(person1,'name'));//false
delete person1.name;
console.log(hasPrototypeProperty(person1,'name'));//true
delete Person.prototype.name;
console.log(hasPrototypeProperty(person1,'name'));//false

[7]ECMAScript5的Object.keys()方法:接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
[注意]一定要先new出实例对象再使用该方法,否则为空

function Person(){
  Person.prototype.name = 'Nicholas';
  Person.prototype.age = 29;
  Person.prototype.job = 'Software Engineer';
  Person.prototype.sayName = function(){
    alert(this.name);
  }  
};
var keys = Object.keys(Person.prototype);
console.log(keys);//[]
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var keys = Object.keys(Person.prototype);
console.log(keys);//["name","age","job","sayName"]
var p1Keys = Object.keys(p1);
console.log(p1Keys);//["name","age"]

[8]ECMAScript5的Object.getOwnPropertyNames()方法:接收一个对象作为参数,返回一个包含所有属性的字符串数组
[注意]一定要先new出实例对象再使用该方法,否则只有constructor

function Person(){
  Person.prototype.name = 'Nicholas';
  Person.prototype.age = 29;
  Person.prototype.job = 'Software Engineer';
  Person.prototype.sayName = function(){
    alert(this.name);
  }  
};
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);//["constructor"]
var p1 = new Person();
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);//["constructor", "name", "age", "job", "sayName"]

希望本文所述对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
扩展Jquery插件处理mouseover时内部有子元素时发生样式闪烁
Dec 08 Javascript
js截取小数点后几位的写法
Nov 14 Javascript
简体中文转换繁体中文(实现代码)
Dec 25 Javascript
js中substr,substring,indexOf,lastIndexOf的用法小结
Dec 27 Javascript
特殊情况下如何获取span里面的值
May 20 Javascript
javascript检测是否联网的实现代码
Sep 28 Javascript
node.js操作mysql(增删改查)
Jul 24 Javascript
javascript事件模型介绍
May 31 Javascript
微信小程序 MD5的方法详解及实例代码
Mar 10 Javascript
vue分类筛选filter方法简单实例
Mar 30 Javascript
微信小程序 自定义弹窗实现过程(附代码)
Dec 05 Javascript
javascript设计模式 ? 外观模式原理与用法实例分析
Apr 15 Javascript
详解Bootstrap创建表单的三种格式(一)
Jan 04 #Javascript
学习javascript面向对象 实例讲解面向对象选项卡
Jan 04 #Javascript
学习javascript面向对象 javascript实现继承的方式
Jan 04 #Javascript
学习javascript面向对象 掌握创建对象的9种方式
Jan 04 #Javascript
jQuery自动完成插件completer附源码下载
Jan 04 #Javascript
学习javascript面向对象 理解javascript对象
Jan 04 #Javascript
基于JavaScript实现移除(删除)数组中指定元素
Jan 04 #Javascript
You might like
重置版宣传动画
2020/04/09 魔兽争霸
xml在joomla表单中的应用详解分享
2012/07/19 PHP
如何在php中正确的使用json
2013/08/06 PHP
php显示时间常用方法小结
2015/06/05 PHP
php+Ajax处理xml与json格式数据的方法示例
2019/03/04 PHP
JS 页面自动加载函数(兼容多浏览器)
2009/05/18 Javascript
javascript eval(func())使用示例
2013/12/05 Javascript
Javascript中arguments对象详解
2014/10/22 Javascript
jQuery实现移动 和 渐变特效的点击事件
2015/02/26 Javascript
JavaScript操作Cookie详解
2015/02/28 Javascript
举例讲解AngularJS中的模块
2015/06/17 Javascript
Javascript实现的Map集合工具类完整实例
2015/07/31 Javascript
整理Javascript基础入门学习笔记
2015/11/29 Javascript
js实现精确到秒的日期选择器完整实例
2016/04/30 Javascript
js实现上传图片及时预览
2016/05/07 Javascript
微信小程序 template模板详解及实例
2017/02/21 Javascript
js获取隐藏元素的宽高
2017/02/24 Javascript
浅谈在vue中用webpack打包之后运行文件的问题以及相关配置方法
2018/02/21 Javascript
angularJs在多个控制器中共享服务数据的方法
2018/09/30 Javascript
JS异步执行结果获取的3种解决方式
2019/02/19 Javascript
详解datagrid使用方法(重要)
2020/11/06 Javascript
Python面向对象编程基础解析(二)
2017/10/26 Python
Python实现螺旋矩阵的填充算法示例
2017/12/28 Python
python实现从pdf文件中提取文本,并自动翻译的方法
2018/11/28 Python
python实现QQ邮箱/163邮箱的邮件发送
2019/01/22 Python
Python生成器常见问题及解决方案
2020/03/21 Python
Python+PyQt5实现灭霸响指功能
2020/05/25 Python
opencv 图像加法与图像融合的实现代码
2020/07/08 Python
俄罗斯家居用品购物网站:Евродом
2020/11/21 全球购物
.TTL是什么?有什么用处,通常那些工具会用到它?(ping? traceroute? ifconfig? netstat?)
2016/05/09 面试题
数控专业个人求职信范文
2014/02/05 职场文书
2014年保密工作总结
2014/11/22 职场文书
高中团支书竞选稿
2015/11/21 职场文书
Vue + iView实现Excel上传功能的完整代码
2021/06/22 Vue.js
win11如何查看端口是否被占用? Win11查看端口是否占用的技巧
2022/04/05 数码科技
python数字图像处理之对比度与亮度调整示例
2022/06/28 Python