JS高级知识——面向对象

前言

学好JS,走遍天下都不怕

JS,全称JavaScript。在我印象中,它就是一个弱语言,各种语法诡异难懂,各种用法千奇百怪,全靠背,没有规律可循(可能也是我学艺不精,大神勿喷)。但是,随着前端的大热,NodeJs的火爆情形,真的是“学好JS,走遍天下都不怕”。
最近,工作中需要去接触公司封装的一个js类库,以及前端工作做的越来越多,需要阅读优秀框架的源码,js面向对象是时候拿出来梳理一下了。

推荐博客——阮一峰大神的JS教程

创建对象的几种方式

方式一:简单的创建对象

1
2
3
4
5
6
7
8
9
10
//简单场景1
var o = {};
o.id = 1;
o.name = 'kevinlsui';
//简单场景2
var a = {id:2,name:'obj'}
//简单场景3
var b = new Object();
b.id = 3;
b.name = 'b';

方式二:工厂模式,假的面向对象

有时,我们需要创建大量结构相似的对象,再使用上面的方式一,显得繁琐,体现不出我们编程的水平。这里我们可以借助一个设计模式——简单工厂模式。
但是,它仅仅是一个生产对象的工厂,并不能称为面向对象,并不能像java类那样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function createObj(id,name){
var o=new Object();//返回一个对象
o.id=id;
o.name=name;
o.sayName=function(){
console.log(this.name);
}
return o;
}
var a=createObj(1,'a');
var b=createObj(2,'b');
console.log(a.id);//1
a.sayName(); // a
b.sayName(); // b
console.log(typeof p1); // object
a instanceof createObj //false 无法判断类型

方式三:构造函数,面向对象,类似java类

要点:

  1. 函数名首字母大写,类似java类名
  2. 函数内部,使用this给属性,方法赋值
  3. 创建对象使用new关键字(不使用new则将值赋给全局window)【当然可以用一些小手段,使得不用new也可以,请看阮一峰博客

至此,已经很像java中的面向对象了,但是,每创建一个对象,都会创建一遍该对象的函数(虽然函数的代码相同),造成资源浪费。当然,也可以共用一个,类似getId()方法,放在类(函数)外面,但是显得特别不科学,所以这种用法很少见的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function CreateObj(id,name){
this.id=id;
this.name=name;
this.sayName=function(){
console.log(this.name);
};
this.getId = getId;
}
//此方法为全局,不仅仅该类对象可以调用,其他无关地方也可以掉用,比如:getId(),//window的id属性
function getId(){
console.log(this.id);
}
var a=new CreateObj(1,'aa');
var b=new CreateObj(2,'bb');
a.sayName(); // aa
b.sayName();//bb
console.log(typeof a);//object
console.log(a instanceof CreateObj); // true 可以判断类型,和java类与对象关系一样
console.log(a.sayName == b.sayName)//false 说明每个对象会产生一个函数,造成资源浪费
console.log(a.getId == b.getId); //true,同一个函数
//不使用new
var d = CreateObj(11,'cc');
console.log(d);//undefined 由于没有用new,this指向全局变量window
console.log(id);//11
console.log(window.name);//cc

方式四:原型模式(混合构造函数模式),正统的面向对象,各框架可见

各大框架中,使用面向对象,基本都是原型模式+构造函数模式的混合。如果不了解js原型,请看阮一峰大神的博客;
这里我们怎么理解了?类比java,构造函数,像类一样,里面有实例变量,每个new的对象都不同;但对于那些公共的,希望每个对象都共用一份的,咱们可以使用java中static静态变量的概念,也就是咱们这里用到的原型。每一个通过该构造函数new出的对象,都共有一个原型,都用一份,改变它大家使用时都变了,如果是java开发者,肯定很好理解的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function CreateObj(id,name){
this.id = id;//类似实例属性,每个new出对象都有自己的一份
this.name = name;
}
//类似静态变量,字符串类型,某对象中修改,只会在对象上添加一个属性,不会影响所有,但是不推荐改此变量
CreateObj.prototype.type = '我们都是类CreateObj的对象';
//类似静态变量,引用类型,某对象修改,会影响所有对象,所以,对象不要修改共有的属性
CreateObj.prototype.arr = ['1','2'];//引用类型,
CreateObj.prototype.getId = function(){
console.log(this.id);
}
CreateObj.prototype.sayName = function(){
console.log(this.name);
}
var a = new CreateObj(10,'aaa');
var b = new CreateObj(20,'bbb');
console.log(a.id);//10
console.log(b.id);//20
console.log(a.type);//'我们都是类CreateObj的对象'
console.log(b.type);//'我们都是类CreateObj的对象'
a.type = "我改一下这个type";//实际上,给该对象添加一个属性
console.log(a.type);//我改一下这个type
console.log(b.type);//我们都是类CreateObj的对象
console.log(CreateObj.prototype.type);//我们都是类CreateObj的对象
console.log(a.arr);//["1", "2"]
console.log(b.arr);//["1", "2"]
a.arr.push('hahha');//引用类型的话,会影响所有的对象
console.log(a.arr);//"1", "2", "hahha"]
console.log(b.arr);//"1", "2", "hahha"]
console.log(CreateObj.prototype.arr);//"1", "2", "hahha"]
a.getId();//10
b.getId();//20
a.sayName();//aaa
b.sayName();//bbb
console.log(a.sayName == b.sayName);//true,公用同一个函数,避免资源浪费
console.log(typeof a);//object
console.log(a instanceof CreateObj);//true,正宗面向对象

面向对象——继承

下面列出常见的继承模式,具体看代码注释即可理解。
(顺带理解下原型、原型链)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//====父类======
function A(id,name){
this.id = id;
this.name = name;
}
A.prototype.getId = function(){
console.log(this.id);
}
A.prototype.getName = function(){
console.log(this.name);
}
//=======子类=======
function B(id,name,age){
//用当前this对象去调用父类中的属性赋值代码,从而继承父类属性
A.call(this,id,name);
//A.apply(this,[id,name]);//类似上面的方法
//自己的属性
this.age = age;
}
//**核心**,原型继承,通过A的对象赋值给原型,获取A中的方法
B.prototype = new A();
B.prototype.getAge = function(){
console.log(this.age);
}
var b = new B(1,'b',10);
console.log(b.id);//1
console.log(b.name);//b
console.log(b.age);//10
b.getId();//1
b.getAge();//b
b.getName();//10
console.log(b instanceof A);//true,继承成功
console.log(b instanceof B);//true
//
//关于原型和原型链的理解
b.__proto__ === B.prototype;//true,一个a对象
b.__proto__.__proto__ === A.prototype;//true,:a对象的__proto__属性
// prototype,是函数特有的属性(一般指构造函数,普通的函数也有,但是为{})
//__proto__,是对象的属性
// 关系:b对象的__proto__属性指向他的构造函数的原型对象B.prototype;
// a对象的__proto__属性指向他的构造函数的原型对象A.prototype;

文章目录
  1. 1. 前言
  2. 2. 创建对象的几种方式
    1. 2.1. 方式一:简单的创建对象
    2. 2.2. 方式二:工厂模式,假的面向对象
    3. 2.3. 方式三:构造函数,面向对象,类似java类
    4. 2.4. 方式四:原型模式(混合构造函数模式),正统的面向对象,各框架可见
  3. 3. 面向对象——继承
|