JavaScript面向对象

定义

面向对象是对代码的一种抽象,对外统一提供调用接口的编程思想。

基于原型的面向对象方式中,对象 (object是js的父对象 ) 则是依靠构造器 (constructor 属性是返回对创建此对象的数组函数的引用) 利用原型 (prototype属性可以向对象添加属性和方法) 构造出来的。

Object是所有对象的基类、跟,所有的JavaScript对象都是由Object延伸的。

名词概念

属性: 事物的特性

方法: 事物的功能

对象: 事物的一个实例

原型: Js函数中由prototype属性引用了一个对象,即原型对象(原型)。每个函数都有prototype属性,这个属性存储的就是原型对象。利用prototype添加属性和方法

原型链: JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做proto的内置属性,用于指向创建它的函数对象的原型对象prototype。

类型

对象分为函数对象普通对象,凡是通过new Function创建的都是函数对象,其他则都是普通对象。

构造函数对象

var obj = new Function(var1,var2,...,functionBody( ));  
//var1,var2是正常变量,functionBody( )自定义函数体

注意:构造器构造的对象,效率低,var1、var2顺序在functionBody中不能变。

闭包

闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
var n=11;
function test(){
alert(n); //undefined
var n=10;
function subTest(){
n++;
alert(n);
}
return subTest;
}
var fun=test();
fun(); //11
fun(); //12

优点:有利于封装,可以访问局部变量
缺点:内存占用浪费严重,内存泄漏。

声明对象方式

字面量声明对象

1
2
3
4
5
6
7
8
var obj = {
属性名称:属性值,
属性名称:属性值,
属性名称:属性值,
...
方法名称:function( ){ }
方法名称:function( ){ }
}

new操作符 + Object

1
2
3
4
5
6
var obj = new Object( );
obj.属性 = 属性值;
obj.属性 = 属性值;
obj.方法 = function(str){
方法代码
};

构造函数模式

构造函数可以创建特定类型的对象,类似于Array、Date等原生JS的对象。

1
2
3
4
5
6
7
8
9
10
function person(name,sex,age){
this.name = name ; //this.name属性 name参数 习惯上属性名称==参数
this.sex = sex;
this.age = age;
this.show = function(){
alert("名字" + this.name + "性别" + this.sex + "年龄" + this.age);
}
}
var obj1 = new person("zhangsan","male",18);
alert(obj1.name);

构造函数最明显的缺点就是,每个方法都要在每个实例上重新创建一遍。

工厂方式

函数内创建一个对象,给对象赋予属性及方法再将对象返回即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createObject(name,age)
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function( ){
return this.name + this.age //this指向调用者本身
};
return obj;
}

var obj1 = createObject('zhansan',100); //不用new
alert(obj1.name);
alert(obj1.run());
var obj2 = createObject('lisi',200); //obj1和obj2无关

构造和工厂模式的区别

  1. 构造模式不会显示创建对象,将属性赋值给this,不需要return对象。
  2. 工厂模式在方法内部创建object对象,返回object对象,属性和方法都是赋给obj对象。

任何模式下,同种模式中创 造出来的对象都是独立存在的,彼此之间都是无关联的。

原型模式

原型模式根本:函数本身声明为空内容,利用prototype定义一些属性及方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function test( ){
}
C
test.prototype.showInfo = function( ){
alert(this.color);
}

//或用jason数据定义属性和方法
/* test.prototype = {
color: "red",
showInfo: function( ){
alert(this.color);
}
} */
var obj = new test( );
obj.showInfo( );

任何js方法或函数,都自带一个prototype属性,且它以对象方式存在。

混合模式

混合模式即为构造模式 + 原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function blog(name,url,friend){
this.name = name;
this.url = url;
this.friend = friend;
}
blog.prototype = {
test: "awt",
showInfo:function(){
alert(this.name);
},
gets:function(){
alert(this.friend);
}
}
var peo = new blog("TeLi","TeLiChan.club","someone");
alert(peo.url);
peo.showInfo();

对象的遍历

对象可以当做数组进行处for(var i in object)

1
2
3
4
5
6
7
8
9
10
11
12
var ren = {};
ren.name = "zhangsan";
ren.age = "18";
ren.height = "180";
ren.demo = function(){
alert(this.name);
}
// i是属性或方法名称
for(var i in ren){
alert(i); //输出属性或方法 名称
alert(ren[i]); //输出属性的值或方法
}

使用构造函数声明的对象要实例化后才可以进行遍历。

1
2
3
4
5
6
7
8
9
10
11
12
function ren(){
this.name = "zhangsan";
this.age = "18";
this.leng = "180";
this.demo = function(){
alert(this.name);
}
}
var r =new ren( );
for(var i in r){
alert(r[i]);
}

对象的内存储存

内存五大区域

  • 栈-–局部变量,当局部变量的作用域,被执行完毕之后,这个局部变量就会被系统立即回收
  • —程序猿手动申请的字节空间,Malloc calloc realloc
  • BBS段—-未被初始化的全局变量和静态变量 一般初始化就回收,并转存到数据段中
  • 数据段(常量区)—-已经被初始化的全局静态变量常量数据,知道程序结束的时候才会被回收
  • 代码段—-存储代码,存储程序的代码

遍历构造函数例子的分析结构图

内存结构分析

封装

封装 (Encapsulation) 的概念:把对象内部数据和操作细节进行隐藏

大多面向对象的语言都支持封装的特性,提供private关键字来隐藏某些属性或方法,用来限制被封装的数据或者内容,只对外提供一个对象的专门访问的接口(一般为调用方法)。

JavaScript中没有提供专门用来封装的关键词,可以通过 闭包 实现封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function demo(){
var n = 1; //局部变量,方法外部不能直接访问
function test( ){ //外部调用的出口,特权方法
return ++n;
}
/* this.test = function(){
return ++n;
} */

return test;
}
alert(demo) // 输出test的整个函数
alert(demo()); // 2
alert(demo()); // 2
1
2
3
4
5
6
7
8
9
10
11
12
13
function A(){
function _xx( ){
alert(11);
}
}
A.prototype = {
oth:function(){
alert("普通方法");
}
}
var a = new A();
var b = a.xx(); // a.xx() --> function _xx;
b(); // 11

缺点

  1. 占用内存
  2. 不利于继承

继承

原型继承

1
2
3
4
5
6
var person = function(){};
var p = new person(); // 实例化对象的三个阶段

1. var p = {}; 创建对象
2. p.\_proto_ = person.prototype // \_proto_对象自带的一个属性,prototype代表person对象里的值
3. 创建对象(初始化对象) p -->person.call(p)

一个函数没有实例化使用 prototype , 实例化后的原型是_proto_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var person = function(){
person.prototype.say = function(){
alert("天气挺好");
}
person.prototype.reward = 500;

var programmer = function(){};
programmer.prototype = new person();
programmer.prototype.wcd = function(){
alert("明天天气也不错");
}
programmer.prototype.reward = 1000;

var p = new programmer();
p.say(); //天气挺好
p.wcd(); //明天天气也不错
alert(p.reward); //1000

/*原型链实现过程
var p = new programmer(); p._proto_ = programmer.prototype
var p1 = new person(); programmer.prototype p1;
p.say(); p._proto_--> programmer.prototype == p1 --> p1._proto_ == person.prototype.say();
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function person(name,age){ //父
this.name = name;
this.age = age;
}
person.prototype.sayhello = function(){
alert("属性name值" + this.name);
}
/*var per = new person("zhangsan",20);
per.sayhello();*/
function student(){}; //子
student.prototype = new person("李四",18) //原型继承
student.prototype.grade = 3;
student.prototype.test = function(){
alert(this.grade);
}
var s = new student();
s.sayhello();//属性name值李四
alert(s.grade); //3

过程分析:
//s.__proto__ = student.prototype = p1 p1._protu_ = person.prototype.sayhello();

构造继承

在子类内部构造父类的对象实现继承

1
2
3
4
5
6
7
8
9
function parents(name){
this.name = name;
this.say = function(){
alert("父亲的名字:" + this.name);
}
}
function child(name,age){ //继承parents
this.pObj = parents; //子对象的参数name
}

关键词

instanceof

判断变量是否是对象的实例 (所有对象本质都是继承于Object)

1
2
3
var arr = new Array();
alert(arr instanceof Array); //trun
alert(arr instanceof Object); //true (Array本身是继承Object的)

delete

用于删除对象的属性,对方法、变量不起作用(无法删除原型链中的属性和变量)

1
2
3
4
5
6
7
8
9
10
function fun(){
this.name = "zhangsan";
this.say = function(){
alert(this.name);
}
}
var obj = new fun();
alert(obj.name); //zhangsan
delete obj.name;
alert(obj.name) //undefined

call和apply

可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象

1
2
3
4
5
6
7
8
9
10
function add(a,b){
alert(a + b);
}
function subs(a,b){
alert(a-b);
}
add.call(subs,5,3); //8 subs-->add ===>add(5,3) subs只能引用一个已存在的对象
add.apply(subs,[5,3]); // 同上,apply为两个参数
subs.call(add,4,2) //2
subs.apply(add,[4,2]) //2
1
2
3
4
5
6
7
8
9
10
11
12
13
function animal(){
this.name = "dog";
this.showName = function(){
alert(this.name);
}
}
function cat(){
this.name = "cat";
}
var a = new animal();
var c = new cat();
a.showName.call(c,","); // cat 将showName 传递 --> cat 使用
a.showName.apply(c,[]); // cat

arguments

每个函数都有一个Arguments对象的实例arguments,引用函数的参数(实参)。

可以用数组下标方式引用arguments元素。

主要作用

  • arguments.length 参数个数
  • arguments[i] arguments如果表示参数,可以遍历的
  • arguments.callee 引用函数自身
1
2
3
4
5
6
7
//用 arguments 来遍历
function count(){
for(var i=0;i<arguments.length;i++){
console.log(arguments[i])
}
}
count(5,4,3,2,1);

callee

返回正在执行的function对象(内容),callee是arguments的一个属性,指代函数本身。

1
2
3
4
function demo(){
alert(arguments.callee); //输出整个demo函数,callee当作属性、函数内容
alert(arguments.callee()) //报错,陷入死循环
}
1
2
3
4
5
6
var sum = function(n){
if(n<=1){
return n + argunments.callee(n-1);
}
}
alert(sum(5));

this

this可以在函数内部定义属性、变量

1
2
3
4
5
6
7
8
9
10
11
12
function test(){
this.x = 1; //this全局变量 等价于 x =1
alert(this.x);
}
test(); // 1

var x = 1;
function test(){
this.x = 0 // 改变了全局变量x
}
test();
alert(x); // 0

作为方法调用。构造函数内this指当前对象。

1
2
3
4
5
6
function test(){
this.name = "zhangsan";
this.age = 18;
}
var t = new test();
alert(t.name);

call、apply里,this指第一个参数

1
2
3
4
5
6
7
8
9
var x = 0;
function test(){
alert(this.x)
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply(); //0
o.m.apply(o); //1

对象冒充

概念:将父类的属性和方法一起传给子类作为特权属性和特权方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function person(name,age){
this.name = name;
this.age = age;
this.sayHi = function(){
alert("hi");
}
}
person.prototype.walk = function(){
alert("walk");
}
function student(name,age,grade){
this.newMethod = person; //冒充person对象,传递特权属性和方法给子类
this.newMethod(name,age)
this.grade = grade;
}
var s1 = new student("zhangsan",15,5); //s1是student对象,继承person,拥有person所有属性和方法。
s1.sayHi(); // hi
s1.walk(); //报错
//s1继承person中的特权方法和属性,没有继承共有属性和方法