JS学习笔记2_面向对象
1.对象的定义
ECMAScript中,对象是一个无序属性集,这里的“属性”可以是基本值、对象或者函数
2.数据属性与访问器属性
数据属性即有值的属性,可以设置属性只读、不可删除、不可枚举等等
访问器属性是用来设置getter和setter的,在属性名前加上”_”(下划线)表示该属性只能通过访问器访问(私有属性),但并不是说添个下划线就把属性变成私有的了,这只是习惯约定的一种命名方式而已。访问器属性没什么用,原因如下:
var book={
_year:2004,
edition:1
}
Object.defineProperty(book,"year",{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue>2004){
this._year=newValue;
this.edition+=newValue-2004;
}
}
});
book.year=2005;
alert(book.edition);
/*
for(var attr in book){
document.write(attr + ' = ' + book[attr] + ';');
}
*/高程中使用了上面的示例代码,原理是book对象的属性中_year是数据属性,而year是访问器属性,利用gettter和setter可以插入读写控制,听起来不错。
但问题是_year和edition都是可枚举的,也就是说用for...in循环可以看到,而访问器属性year却是不可枚举的。应该公开的访问器不公开,却把应该隐藏的私有属性公开了。
除此之外,这种定义访问器的方式并不是全浏览器兼容的,[IE9+]才完整支持,当然,也有适用于旧浏览器的方式(__defineGetter__()和__defineSetter__()),还是相当麻烦。
总之,访问器属性没什么用。
3.构造函数
function Fun(){} var fun = new Fun();其中Fun是构造函数,与普通函数没有任何声明方式上的区别,只是调用方式不同(用new操作符调用)而已
构造函数可以用来定义自定义类型,例如:
function MyClass(){
this.attr = value;//成员变量
this.fun = function(){...}//成员函数
}
与Java的类声明有些相似,但也有一些差异,比如this.fun只是一个函数指针,因此完全可以让它指向可访问的其它函数(如全局函数),但这样做会破坏自定义对象的封装性
4.函数与原型prototype
声明函数的同时也创建了一个原型对象,函数名持有该原型对象的引用(fun.prototype)
可以给原型对象添加属性,也可以给实例对象添加属性,区别是原型对象的属性是该类型所有实例所共享的,而实例对象的属性是非共享的
访问实例对象的属性时,先在该实例对象的作用域中查找,找不到才在原型对象的作用域中查找,因此实例的属性可以屏蔽原型对象的同名属性
原型对象的constructor属性是一个函数指针,指向函数声明
通过原型可以给原生引用类型(Object,Array,String等)添加自定义方法,例如给String添加Chrome不支持但FF支持的startsWith方法:
var str = 'this is script';
//alert(str.startsWith('this'));//Chrome中报错
String.prototype.startsWith = function(strTarget){
return this.indexOf(strTarget) === 0;
}
alert(str.startsWith('this'));//不报错了注意:不建议给原生对象添加原型属性,因为这样可能会意外重写原生方法,影响其它原生代码(调用了该方法的原生代码)
通过原型可以实现继承,思路是让子类的prototype属性指向父类的实例,以增加子类可访问的属性,所以用原型链连接之后
子类可访问的属性
= 子类实例属性 + 子类原型属性
= 子类实例属性 + 父类实例属性 + 父类原型属性
= 子类实例属性 + 父类实例属性 + 父父类实例属性 + 父父类原型属性
= ...最终父父...父类原型属性被替换为Object.prototype指向的原型对象的属性
具体实现是SubType.prototype = new SuperType();可称之为简单原型链方式的继承
5.创建自定义类型的最佳方式(构造函数模式 + 原型模式)
实例属性用构造函数声明,共享属性用原型声明,具体实现如下:
function MyObject(){
//实例属性
this.attr = value;
this.arr = [value1, value2...];
}
MyObject.prototype = {
constructor: MyObject,//保证子类持有正确的构造器引用,否则子类实例的constructor将指向Object的构造器,因为我们把原型改成匿名对象了
//共享属性
fun: function(){...}
}
6.实现继承的最佳方式(寄生组合式继承)
function object(obj){//返回原型为obj的没有实例属性的对象
function Fun(){}
Fun.prototype = obj;
return new Fun();
}
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype);//建立原型链,继承父类原型属性,用自定义函数object处理是为了避免作为子类原型的父类实例具有实例属性,简单地说,就是为了切掉除多余的实例属性,可以对比组合继承理解
prototype.constructor = subType;//保证构造器正确,原型链会改变子类持有的构造器引用,建立原型链后应该再改回来
subType.prototype = prototype;
}
function SubType(arg1, arg2...){
SuperType.call(this, arg1, arg2...);//继承父类实例属性
this.attr = value;//子类实例属性
}
inheritPrototype(SubType, SuperType);
具体解释见代码注释,这种方式避免了SubType.prototype = new SuperType();简单原型链的缺点:
子类实例共享父类实例引用属性的问题(因为原型引用属性是所有实例共享的,建立原型链后父类的实例属性就自然地成了子类的原型属性)。
创建子类实例时无法给父类构造函数传参
7.实现继承的最常用方式(组合继承)
把上面的inheritPrototype(SubType, SuperType);语句换成:
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
这种方式的缺点是:由于调用了2次父类的构造方法,会存在一份多余的父类实例属性,具体原因如下:
第一次SuperType.call(this);语句从父类拷贝了一份父类实例属性给子类作为子类的实例属性,第二次SubType.prototype = new SuperType();创建父类实例作为子类原型,此时这个父类实例就又有了一份实例属性,但这份会被第一次拷贝来的实例属性屏蔽掉,所以多余。
JS学习笔记2_面向对象的更多相关文章
- JS 学习笔记 (七) 面向对象编程OOP
1.前言 创建对象有很多种方法,最常见的是字面量创建和new Object()创建.但是在需要创建多个相同结构的对象时,这两种方法就不太方便了. 如:创建多个学生信息的对象 let tom = { n ...
- JavaScript学习笔记2_面向对象
1.对象的定义 ECMAScript中,对象是一个无序属性集,这里的“属性”可以是基本值.对象或者函数 2.数据属性与访问器属性 数据属性即有值的属性,可以设置属性只读.不可删除.不可枚举等等 访问器 ...
- ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则
ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- Vue.js学习笔记(2)vue-router
vue中vue-router的使用:
- JS 学习笔记--9---变量-作用域-内存相关
JS 中变量和其它语言中变量最大的区别就是,JS 是松散型语言,决定了它只是在某一个特定时间保存某一特定的值的一个名字而已.由于在定义变量的时候不需要显示规定必须保存某种类型的值,故变量的值以及保存的 ...
- UML和模式应用学习笔记-1(面向对象分析和设计)
UML和模式应用学习笔记-1(面向对象分析和设计) 而只是对情节的记录:此处的用例场景为:游戏者请求掷骰子.系统展示结果:如果骰子的总点数是7,则游戏者赢得游戏,否则为输 (2)定义领域模型:在领域模 ...
- WebGL three.js学习笔记 使用粒子系统模拟时空隧道(虫洞)
WebGL three.js学习笔记 使用粒子系统模拟时空隧道 本例的运行结果如图: 时空隧道demo演示 Demo地址:https://nsytsqdtn.github.io/demo/sprite ...
- WebGL three.js学习笔记 法向量网格材质MeshNormalMaterial的介绍和创建360度全景天空盒的方法
WebGL学习----Three.js学习笔记(5) 点击查看demo演示 Demo地址:https://nsytsqdtn.github.io/demo/360/360 简单网格材质 MeshNor ...
随机推荐
- Laravel 利用 observer 类基于状态属性,对进行删除和修改的控制
1 我们知道 Observer 类可以监听模型类的相关事件 1.1 creating, created, updating, updated, saving, saved, deleting, del ...
- netsharp.weixin和sdk的配置信息管理
一.微信公众号后台配置 即在微信公众号后台配置类似如下的url:http://121.40.86.55/wx?oid=gh_befcc6d4c40d 这种情况下会执行WeixinServlet类的do ...
- 提升HTML5的性能体验系列之二 列表流畅滑动
App的顶部一般有titlebar,下面是list.常见的一个需求是要在list滚动时,titlebar不动.这个简单的需求,实现起来其实并不简单. 在普通web上的做法是使用div的滚动条,把lis ...
- HTML and CSS学习概述
一·Web浏览器是一个连接到Web服务器,向Web服务器请求信息,然后解析返回来的HTML标记,并将其显示在浏览器窗口内的程序.1.Microsoft 2.Internet Explorer(IE)3 ...
- P3834 【模板】可持久化线段树 1(主席树)
#include <bits/stdc++.h> #define read read() #define up(i,l,r) for(int i = (l);i <= (r);i++ ...
- liunx基础命令
linux的简单介绍 linux是一款免费使用和自由传播的内似于unix的操作系统软件,是一个基于POSI和unix的多用户,多任务,支持多线程和多CPU的一种操作系统.主要用于服务器,特别是网络服务 ...
- 基础知识之nginx重写规则
nginx重写规则 nginx rewrite 正则表达式匹配 大小写匹配 ~ 为区分大小写匹配 ~* 为不区分大小写匹配 !~和!~*分别为区分大小写不匹配及不区分大小写不匹配 文件及目录匹配 -f ...
- 2019.01.20 bzoj3999: [TJOI2015]旅游(树链剖分)
传送门 树链剖分菜题. 题意不清差评. 题意简述(保证清晰):给一棵带权的树,每次从aaa走到bbb,在走过的路径上任意找两个点,求后访问的点与先访问的点点权差的最大值. 思路: 考虑暴力:维护路径的 ...
- 2019.01.19 codeforces893F.Subtree Minimum Query(线段树合并)
传送门 线段树合并菜题. 题意简述:给一棵带点权的有根树,多次询问某个点ppp子树内距离ppp不超过kkk的点的点权最小值,强制在线. 思路: 当然可以用dfsdfsdfs序+主席树水过去. 然而线段 ...
- Matlab编辑器背景修改
将下段代码如到C:\Users\Peng Chen\AppData\Roaming\MathWorks\MATLAB\R2016a\matlab.prf 先备份.prf,再替代之前的. #MATLAB ...